Nikola
Nikola

Reputation: 400

Electron: Dynamic context menu

In Electron, is there a way to enable/disable specific MenuItem in context menu, depending on the element that the user has right-clicked? Also I need information about which exact element was clicked and pass that info to context menu function.

For example, let's say I have this html in my renderer process:

<p id="p1">First paragraph</p>
<p id="p2">Second paragraph</p>
<p id="p3">Third paragraph</p>

And my context menu for that window looks like this:

var menu = new Menu();
menu.append(new MenuItem({label: "This menu item is always shown",}));
menu.append(new MenuItem({  // shown only when clicked on p1 or p3
  label: "This menu is not always shown",
  click: function(id){
    // I want variable id to be an id of paragraph that I have clicked on
  }
}));

So when I right click on first or third paragraph, a context menu with 2 items should pop up. But when I right click on second paragraph, a context menu with 1 item should pop up. Also, I want to pass the paragraph id as a parameter to a context menu function, so that I can know from there which paragraph I have clicked on.

Upvotes: 3

Views: 4718

Answers (2)

Quinton Coldwater
Quinton Coldwater

Reputation: 11

hey so super late to this, but to any one trying to find something that works for this. Ive managed to put something together that i believe works quite well.

For my application, certain windows require certain context menus and certain windows shouldnt have a context menu.

  • Application: Electron
  • Display: html
  • Communication: the html & js communicate using sockets to tell eachother what is required

    const { app, BrowserWindow, shell, ipcMain, nativeTheme, Tray, nativeImage, Menu  } = require('electron')
    const contextMenu = require('electron-context-menu');
    //*other includes and app logic*
    // ........
    // *then a function to decide which menu is required*
    var cntxMenu // variable holding the context menu
    var mainWindow, pluginWindow // variable holding windows
    
    // Function creating a window
    function createMain(){
      mainWindow = createWindow()
      mainWindow.loadURL('./index.html')
    }
    
    // Constant creating a window
    const createWindow = () => {
    const win = new BrowserWindow({
        webPreferences: {
            spellcheck: true
        },
        height: 1280,
        width: 1920
    })
    var menuOccupied = false
    function genMenu(selection){
        if (menuOccupied == true){
            // Disposing of the context menu currently in use
            cntxMenu()
            menuOccupied = false
        }
        if (selection == "a"){
            // Creating the new context menu
            cntxMenu = generateMenu()
            menuOccupied = true
        }else if (selection == "go"){
            cntxMenu = generateGoMenu()
            menuOccupied = true
        }else if (selection == "win"){
            cntxMenu = generateWinMenu()
            menuOccupied = true
        }else if (selection == "none"){
            //cntxMenu()
            //menuOccupied = false
        }
    }
    // * This function generates the menu to be used / You can have as many as you like to do whatever then just call whichever menus create function above as needed *
    function generateMenu(){
        return contextMenu({
            prepend: (defaultActions, parameters, browserWindow) => [
                {
                    label: 'label',
                    click: () => {
                        
                    }
                },
                {
                    label: 'label',
                    click: () => {
                        // Do stuff
                    }
                },
                {type: 'separator'},
                {
                    label: 'Plugins'
                },
                {
                    label: 'label: “{selection}”',
                    click: () => {
                        if (parameters.selectionText.length > 0){
                            // Do stuff
                        }else {
                            // Do stuff                     
                        }
                    }
                },
                {type: 'separator'},
                {
                    label: 'label',
                    click: () => {
                        // Do stuff
                    }
                },
            ]
        });
    }

Context menu Demo

Upvotes: 1

customcommander
customcommander

Reputation: 18901

I would dynamically (re)create the context menu in a contextmenu event handler:

In your main process:

Do not turn on nodeIntegration if you load remote content!

const { app, BrowserWindow } = require('electron');

function createWindow () {
  let win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  });

  win.loadFile('index.html');
}

app.whenReady().then(createWindow);

In your renderer process:

Note how I "remotely" load the Menu & MenuItem modules

<html>
  <head>
    <script>
      const { remote } = require('electron');
      const { Menu, MenuItem } = remote;

      window.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        const menu = new Menu();
        menu.append(new MenuItem(new MenuItem({label: "This menu item is always shown"})));
        if (e.target.id === "p1" || e.target.id === "p3") {
          menu.append(new MenuItem({
            label: "This menu is not always shown",
            click: function(){
              alert(`you clicked on ${e.target.id}`);
            }
          }));
        }
        menu.popup({ window: remote.getCurrentWindow() })
      }, false)
    </script>
  </head>
  <body>
    <p id="p1">First paragraph</p>
    <p id="p2">Second paragraph</p>
    <p id="p3">Third paragraph</p>
  </body>
</html>  

enter image description here

Upvotes: 4

Related Questions