Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Context menus

A context menu is the menu that appears on a right click. Electron has none by default: right-clicking an input field shows nothing. The menu is created with the same Menu.buildFromTemplate as the app menu, but shown with the popup method in the right place.

A menu for input fields

The main process learns about a right click from the context-menu event of the window's webContents. Add to createWindow in main.js:

// main.js
win.webContents.on('context-menu', (event, params) => {
  if (!params.isEditable) {
    return;
  }
  const menu = Menu.buildFromTemplate([
    { role: 'cut' },
    { role: 'copy' },
    { role: 'paste' },
    { type: 'separator' },
    { role: 'selectAll' },
  ]);
  menu.popup({ window: win });
});

params.isEditable is true when the click landed on an input field. Right-click the note field — a menu with the familiar commands appears. Roles perform their actions themselves, so no click is needed.

What params holds

FieldValue
isEditableThe click was on an input field
selectionTextThe selected text
linkURLThe link address under the cursor
x, yClick coordinates

A menu for a note

To delete a specific note, the main process needs to know which one was clicked. Only the page knows that, so the page itself asks for the menu:

// renderer.js
list.addEventListener('contextmenu', (event) => {
  const li = event.target.closest('li');
  if (!li) {
    return;
  }
  event.preventDefault();
  window.notes.showNoteMenu(Number(li.dataset.index));
});

When the list is rendered, each li gets li.dataset.index = i. preventDefault cancels the default handling of the right click.

// preload.js
showNoteMenu: (index) => ipcRenderer.send('notes:menu', index),
onDeleteNote: (callback) => ipcRenderer.on('notes:delete', (event, index) => callback(index)),

Showing the menu in the main process

// main.js
ipcMain.on('notes:menu', (event, index) => {
  const menu = Menu.buildFromTemplate([
    { label: 'Delete note', click: () => event.sender.send('notes:delete', index) },
  ]);
  menu.popup({ window: BrowserWindow.fromWebContents(event.sender) });
});

No answer is needed, so the send and ipcMain.on pair is used. The chosen item sends the note's index back to the page on the notes:delete channel.

// renderer.js
window.notes.onDeleteNote(async (index) => {
  notes.splice(index, 1);
  await window.notes.save(notes);
  render();
});

Right-clicking a note and choosing "Delete note" removes it from the list and the file.

Common mistake

Without the window option, popup opens the menu on the currently focused window, and with several windows it may appear in the wrong place. Always pass the window the click came from: menu.popup({ window: win }).

Thanks for reading!

Donate to the site — we work hard for you.