Contents

Tutorial

This tutorial will step you through the process of creating your first Molybden application.

Overview

In this tutorial you will generate a new Molybden project, install dependencies, overview the project structure, learn how to work with the application backend and frontend, how the backend and frontend communicate with each other, customize the application window and context menu, replace the app icon, and pack the application into a native executable for further distribution.

Prerequisites

  • Windows 10 or later.
  • Install Node.js version 20.11.0 or higher.
  • Install Microsoft C++ Build Tools. Make sure to select the “Desktop development with C++” workload during installation.
  • Ubuntu 22.04 64-bit or later.
  • Install Node.js version 20.11.0 or higher.
  • Install GCC or Clang that supports C++20 or later:
    sudo apt install gcc
    

Generating a project

Make sure your current working directory is the one where you intend to create a project. Run the following command in your command line:

npm create molybden-app@latest

This command will install and execute Create Molybden App, the official Molybden project scaffolding tool.

When prompted, provide the following project name, frontend framework, UI component library, and language:

Molybden CLI

Once the project is created, follow the instructions to install dependencies and run your first Molybden app in development mode:

cd MyApp
npm install
npm run molybden dev

You should now have your first Molybden app running!

Molybden app

Project overview

The project directory structure may be different depending on the frontend framework and language you chose. In this tutorial we selected the Vanilla frontend and the JavaScript language for the app frontend.

Here’s the project directory structure for the generated project:

src/
|-- assets/
|-- main.js
`-- style.css
src-cpp/
|-- assets/
|-- resources/
|-- src/
|   `-- main.cc
CMakeLists.txt
index.html
molybden.conf.json
package.json

The project directory consists of files that can be divided into three categories:

  1. Configuration files.
  2. Application frontend files.
  3. Application backend files.

The application frontend is responsible for creating the application user interface that will be displayed in the application window and communicating with the application C++ backend.

The application backend is responsible for creating the application window, displaying native system dialogs, managing the application life cycle, making calls to the operating system and third party libraries, and communicating with the application JavaScript frontend.

Calling C++ from JavaScript

You can inject C++ functions/objects/properties into JavaScript and work with them as if they were JavaScript functions/objects/properties. The function arguments and return value are automatically converted between C++ and JavaScript types.

Let’s see how the application C++ backend and JavaScript frontend communicate with each other in the generated project.

In the src-cpp/src/main.cc file we inject the greet() C++ function into JavaScript:

#include "molybden.hpp"

using namespace molybden;

std::string greet(std::string name) {
  return "Hello " + name + "! This message comes from C++";
}

void launch() {
  App::init([](std::shared_ptr<App> app) {
    auto browser = Browser::create(app);
    browser->onInjectJs = [](const InjectJsArgs& args, InjectJsAction action) {
      args.window->putProperty("greet", greet);
      action.proceed();
    };
    browser->loadUrl(app->baseUrl());
    browser->show();
  });
}

In the index.html file we define the “Greet” button and include the src/main.js file:

<!DOCTYPE html>
<html lang="en">
<head>
  <link rel="stylesheet" href="src/style.css"/>
  <title>Molybden + Vite + Vanilla JavaScript</title>
</head>
<body>
<div class="container">
  <div class="row">
    <img src="src/assets/logo.svg" class="logo" alt="Molybden logo"/>
  </div>
  <h1>Welcome to Molybden!</h1>
  <p>Please enter your name and click the button.</p>
  <div class="row">
    <div>
      <input id="greet-input" placeholder="Your name"/>
      <button id="greet-btn" type="button">Greet</button>
    </div>
  </div>
  <p id="greet-msg"></p>
  <script type="module" src="src/main.js"></script>
</div>
</body>
</html>

In the src/main.js file we define the sayHello() function that will be called when the “Greet” button is clicked:

function sayHello() {
  let name = document.querySelector("#greet-input").value;
  document.querySelector("#greet-msg").textContent = greet(name);
}

document.querySelector("#greet-btn").addEventListener("click", sayHello);

In the sayHello() function we read the name from the input field, calls the injected greet() C++ function and prints its return value:

function sayHello() {
  let name = document.querySelector("#greet-input").value;
  document.querySelector("#greet-msg").textContent = greet(name);
}

As you can see, the JavaScript String type is automatically converted to the C++ std::string type and vice versa.

If you run the app, enter your name and click the “Greet” button, you will see the following:

Molybden app

Adding features

Let’s extend the application with the features you would probably need.

In this tutorial we will be adding features to the application backend. The application backend is written in C++. So we recommend that you open the generated CMakeList.txt in your favorite C++ IDE such as MS Visual Studio, CLion, or Qt Creator, and use it to develop and debug the application backend.

You can run and debug Molybden app right from your IDE:

Molybden app

Customizing window

Let’s customize the app window by setting its title to MyApp, setting its size to 800x600, and center the window on the screen before showing it. For that, open the src-cpp/src/main.cc file and add the following code:

#include "molybden.hpp"

using namespace molybden;

std::string greet(std::string name) {
  return "Hello " + name + "! This message comes from C++";
}

void launch() {
  App::init([](std::shared_ptr<App> app) {
    auto browser = Browser::create(app);
    browser->onInjectJs = [](const InjectJsArgs& args, InjectJsAction action) {
      args.window->putProperty("greet", greet);
      action.proceed();
    };
    browser->loadUrl(app->baseUrl());
    
    // Customize the browser window title, size, and position.
    browser->setTitle("MyApp");
    browser->setSize(800, 600);
    browser->centerWindow();
    
    browser->show();
  });
}

Configuring app menu

On macOS the app must have a main menu. By default, each Molybden app has a predefined main menu. You can customize the app main menu with both the standard and custom menu items.

Let’s configure the app main menu by adding the following code to the src-cpp/src/main.cc file:

#include "molybden.hpp"

using namespace molybden;

std::string greet(std::string name) {
  return "Hello " + name + "! This message comes from C++";
}

void launch() {
  App::init([](std::shared_ptr<App> app) {
    auto browser = Browser::create(app);
    browser->onInjectJs = [](const InjectJsArgs& args, InjectJsAction action) {
      args.window->putProperty("greet", greet);
      action.proceed();
    };
    browser->loadUrl(app->baseUrl());

    // Customize the browser window title, size, and position.
    browser->setTitle("MyApp");
    browser->setSize(800, 600);
    browser->centerWindow();

    // Configure the app main menu.
    app->setMainMenu(CustomMenu::create({
        menu::MacApp({
            menu::MacHideApp(),
            menu::MacHideOthers(),
            menu::MacShowAll(),
            menu::Separator(),
            menu::Quit()
        }),
        menu::File({
            menu::CloseWindow()
        }),
        menu::Edit({
            menu::Undo(),
            menu::Redo(),
            menu::Separator(),
            menu::Cut(),
            menu::Copy(),
            menu::Paste(),
            menu::Delete(),
            menu::SelectAll(),
        }),
        menu::View({
            menu::DevTools()
        })
    }));
    
    browser->show();
  });
}

The app main menu will look like this:

Molybden app

Configuring context menu

By default, the context menu is not enabled. You can enable the context menu and customize it with both the standard and custom menu items in the Browser::onShowContextMenu delegate.

Let’s add the context menu with a few standard items to the browser window:

#include "molybden.hpp"

using namespace molybden;

std::string greet(std::string name) {
  return "Hello " + name + "! This message comes from C++";
}

void launch() {
  App::init([](std::shared_ptr<App> app) {
    auto browser = Browser::create(app);
    browser->addJavaScriptProperty("greet", greet);
    browser->loadUrl(app->baseUrl());

    // Customize the browser window title, size, and position.
    browser->setTitle("MyApp");
    browser->setSize(800, 600);
    browser->centerWindow();

    // Configure the app main menu.
    app->setMainMenu(CustomMenu::create({
        menu::MacApp({
            menu::MacHideApp(),
            menu::MacHideOthers(),
            menu::MacShowAll(),
            menu::Separator(),
            menu::Quit()
        }),
        menu::File({
            menu::CloseWindow(),
        }),
        menu::Edit({
            menu::Undo(),
            menu::Redo(),
            menu::Separator(),
            menu::Cut(),
            menu::Copy(),
            menu::Paste(),
            menu::Delete(),
            menu::SelectAll(),
        }),
        menu::View({
            menu::DevTools()
        }),
        menu::Window({})
    }));
    
    // Configure the context menu.
    browser->onShowContextMenu = [](const ShowContextMenuArgs& args,
                                    ShowContextMenuAction action) {
      action.show(context_menu::Menu({
          context_menu::GoBack(),
          context_menu::GoForward(),
          context_menu::Separator(),
          context_menu::Print(),
          context_menu::Separator(),
          context_menu::InspectElement(),
      }));
    };

    browser->show();
  });
}

When you right-click on the browser window, you should observe the following context menu:

Molybden app context menu

Customizing app details

You can customize the application name, icon, description, copyright, version, etc. by editing the corresponding properties in molybden.conf.json:

{
  "app": {
    "name": "MyApp",
    "version": {
      "major": "1",
      "minor": "0",
      "patch": "0"
    },
    "author": "MyCompany",
    "copyright": "MyCopyright",
    "description": "MyDescription",
    "bundle": {
      "macOS": {
        "icon": "src-cpp/assets/app.icns",
        "bundleID": "",
        "codesignIdentity": "",
        "codesignEntitlements": "",
        "teamID": "",
        "appleID": "",
        "password": ""
      },
      "Windows": {
        "icon": "src-cpp/assets/app.ico",
        "certFile": "",
        "certPassword": "",
        "digestAlgorithm": "",
        "timestampServerURL": ""
      },
      "Linux": {
        "icon": "src-cpp/assets/app128.png"
        }
    },
    "configurations": {
      ...
    }
  }
}

To change the app icon, just replace the existing platform-specific icon files with your own ones in the same format.

Check out the Branding Guide to learn more about customizing your app.

Packaging the app for distribution

When you are ready to ship your app to production, run the following:

npm run molybden build

This command will create a production-ready build of your application in a native executable and place it in the project’s ./build-dist/bin directory. On macOS and Windows platforms, it will also create a native installer in the ./build-dist/pack directory.

Check out the Distribution guide to learn more about shipping your app to production.

On this page
Top