electron menu accelerator not working - electron

I am going through the Electron Fundamentals course on Pluralsight (Trent, 2016). I can't get the accelerator to work on my "Quit" menu item. Below is my entire main.js file. The menu is created successfully from what I can tell (picture below), and clicking directly on the Quit menu item does shut down the application, but the Alt+W key combination does not. I am on Windows 10. What am I missing?
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
app.on('ready', _ => {
new BrowserWindow();
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
accelerator: 'Alt+W',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});

The accelerator String is no longer supported. The documentation was updated in v1.4.5 to clarify how to define shortcuts using globalShortcut.
Accelerator Documentation: Shortcuts are registered with the globalShortcut module using the register method, i.e.
const {app, globalShortcut} = require('electron')
app.on('ready', () => {
// Register a 'CommandOrControl+Y' shortcut listener.
globalShortcut.register('CommandOrControl+Y', () => {
// Do stuff when Y and either Command/Control is pressed.
})
})
So change your code to this
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
const globalShortcut = electron.globalShortcut;
app.on('ready', _ => {
new BrowserWindow();
// Declare shortcuts
globalShortcut.register('Alt+W', () => app.quit());
const template = [
{
label: "File",
submenu: [{
label: 'About',
click: _ => {
console.log('clicked');
}
},
{
type: 'separator'
},
{
label: 'Quit',
click: _ => {
app.quit();
}
}]
}];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
});

looks like on Windows it needs something loaded on the BrowserWindow. Placing the loadURL make it work. Need to try something besides loadURL though..
app.on('ready', _ => {
mainWindow = new BrowserWindow()
mainWindow.loadURL('https://github.com')
const name = electron.app.getName()
const template = [
{
label: name,
submenu: [{
label: `About ${name}` ,
click: console.log('clicked!')
},{
type:'separator'
},{
label:'Quit',
click: _ =>{
app.quit()
},
accelerator:'CmdOrCtrl+Q'
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})

Related

Electron whitescreen without any elements

So I was working on an electron application for quite a while. Then suddenly when typing npm start into the console, it's just a white screen without any elements or scripts.
I think it doesn't load index.html properly, but there aren't any error messages. Before everything worked just fine.
I can't even use the JavaScript Console. When I type anything like
console.log("hello")
It just disappears and returns nothing after pressing Enter.
I don't remember changing anything.
This is my index.js file:
const { app, BrowserWindow, Menu } = require('electron');
const path = require('path');
const remoteMain = require('#electron/remote/main');
const fs = require('fs');
remoteMain.initialize();
if (require('electron-squirrel-startup')) {
app.quit();
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 1600,
height: 1000,
autoHideMenuBar: true,
webPreferences: {
nodeIntegration: true,
contextIsolation: false,
enableRemoteModule: true,
devTools: true,
preload: path.join(__dirname, 'preload.js')
},
});
mainWindow.setMenuBarVisibility(false)
mainWindow.setMenu(null)
mainWindow.on('closed', () => {
win = null
})
mainWindow.loadFile(path.join(__dirname, 'index.html'));
remoteMain.enable(mainWindow.webContents)
mainWindow.once('ready-to-show', () => {
mainWindow.show();
})
mainWindow.webContents.openDevTools()
setMainMenu(mainWindow)
};
app.on('ready', createWindow);
function setMainMenu(win) {
const template = [
{
label: 'Filter',
submenu: [
{
label: 'Hello',
accelerator: 'Shift+CmdOrCtrl+H',
click() {
console.log("dont")
}
},
{
label: 'Quit',
accelerator: 'Cmd+Q',
click() {
app.quit()
}
},
{
label: "Reload",
accelerator: "Cmd+R",
click() {
win.reload()
}
},
{
label: "Open dev tools",
accelerator: "f12",
click() {
win.openDevTools()
}
}
]
},
{
label: "Edit",
submenu: [
{ label: "Undo", accelerator: "CmdOrCtrl+Z", selector: "undo:" },
{ label: "Redo", accelerator: "Shift+CmdOrCtrl+Z", selector: "redo:" },
{ type: "separator" },
{ label: "Cut", accelerator: "CmdOrCtrl+X", selector: "cut:" },
{ label: "Copy", accelerator: "CmdOrCtrl+C", selector: "copy:" },
{ label: "Paste", accelerator: "CmdOrCtrl+V", selector: "paste:" },
{ label: "Select All", accelerator: "CmdOrCtrl+A", selector: "selectAll:" }
]}
];
Menu.setApplicationMenu(Menu.buildFromTemplate(template));
}
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

Electron: Why does showing localhost only work if never loaded another URL before?

In my Electron app I want to load an URL after the app has started:
mainWindow.loadURL(`file:///${process.resourcesPath}/Content/index.html`);
And later I want to show the file which is served on localhost:
mainWindow.loadURL(`http://127.0.0.1:2015/`);
I found out that showing localhost only works if I never load another URL before. And it does not matter if I load a local index.html or an online URL before showing localhost.
Does someone know how I can resolve this?
Here is my code. The problem is in the StartCaddy function. This function gets called when pressing on a menu item in the file menu.
// This only works if never used mainWindow.loadURL before???
mainWindow.loadURL("http://127.0.0.1:2015/");
If I comment out following line than it works without problem:
mainWindow.loadURL(windowURL);
// Modules to control application life and create native browser window
const { app, BrowserWindow } = require('electron')
const path = require('path')
let mainWindow;
var windowURL = "https://www.google.com/";
function createWindow() {
// Create the browser window.
//const mainWindow = new BrowserWindow({
mainWindow = new BrowserWindow({
width: 800,
height: 600,
title: "", // Remove title in title bar
//frame: false,
//titleBarStyle: 'hidden',
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
// MENU BEGIN /////////////////////////////////////////////
const { app, Menu } = require('electron')
const isMac = process.platform === 'darwin'
const template = [
// { role: 'appMenu' }
...(isMac ? [{
label: app.name,
submenu: [
{ role: 'about' },
{ type: 'separator' },
{ role: 'services' },
{ type: 'separator' },
{ role: 'hide' },
{ role: 'hideOthers' },
{ role: 'unhide' },
{ type: 'separator' },
{ role: 'quit' }
]
}] : []),
// { role: 'fileMenu' }
{
label: 'File',
submenu: [
{
label: 'Start Caddy Server', click() {
StartCaddy();
}
},
{
label: 'Git Action', click() {
GitAction();
}
},
{
label: 'ZIP Action', click() {
ZipAction();
}
},
isMac ? { role: 'close' } : { role: 'quit' }
]
},
// { role: 'editMenu' }
{
label: 'Edit',
submenu: [
{ role: 'undo' },
{ role: 'redo' },
{ type: 'separator' },
{ role: 'cut' },
{ role: 'copy' },
{ role: 'paste' },
...(isMac ? [
{ role: 'pasteAndMatchStyle' },
{ role: 'delete' },
{ role: 'selectAll' },
{ type: 'separator' },
{
label: 'Speech',
submenu: [
{ role: 'startSpeaking' },
{ role: 'stopSpeaking' }
]
}
] : [
{ role: 'delete' },
{ type: 'separator' },
{ role: 'selectAll' }
])
]
},
// { role: 'viewMenu' }
{
label: 'View',
submenu: [
{ role: 'reload' },
{ role: 'forceReload' },
{ role: 'toggleDevTools' },
{ type: 'separator' },
{ role: 'resetZoom' },
{ role: 'zoomIn' },
{ role: 'zoomOut' },
{ type: 'separator' },
{ role: 'togglefullscreen' }
]
},
// { role: 'windowMenu' }
{
label: 'Window',
submenu: [
{ role: 'minimize' },
{ role: 'zoom' },
...(isMac ? [
{ type: 'separator' },
{ role: 'front' },
{ type: 'separator' },
{ role: 'window' }
] : [
{ role: 'close' }
])
]
},
{
role: 'help',
submenu: [
{
label: 'Learn More',
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://electronjs.org')
}
}
]
}
]
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
// MENU END ////////////////////////////////////////////////
// and load the index.html of the app.
mainWindow.loadURL(windowURL);
// Open the DevTools.
// mainWindow.webContents.openDevTools()
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.whenReady().then(() => {
//windowURL = `file:///${process.resourcesPath}/Content/index.html`;
createWindow()
app.on('activate', function () {
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
})
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', function () {
if (process.platform !== 'darwin') app.quit()
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
// FUNCTIONS ///////////////////////////////////////////////
// Path to the resources directory (works only in builded app)
// ${process.resourcesPath}
// Path to the project's root folder
// ${__dirname}
// Start Caddy function
async function StartCaddy() {
console.log("Start of StartCaddy function");
const caddyAction = require("child_process").exec(`${process.resourcesPath}/Bin/caddy-start`, shellCallback);
caddyAction.stdout.pipe(process.stdout)
caddyAction.on("exit", () => console.log("Caddy action finished")) // This gets never executed
await sleep(3000); // Wait 3 seconds
console.log("Waited for 3 seconds now show localhost in new window");
//windowURL = `http://127.0.0.1:2015/`;
//mainWindow.close();
//createWindow();
// This only works if never used mainWindow.loadURL before???
mainWindow.loadURL("http://127.0.0.1:2015/");
//mainWindow.loadURL(`http://localhost:2015`);
//mainWindow.reload();
mainWindow.webContents.reloadIgnoringCache();
}
// Sleep function
function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
// Git action function
function GitAction() {
const gitAction = require("child_process").exec(`${process.resourcesPath}/Bin/git-action`, shellCallback);
gitAction.stdout.pipe(process.stdout)
gitAction.on("exit", () => console.log("Git action finished"))
}
// ZIP action function
function ZipAction() {
const zipAction = require("child_process").exec(`${process.resourcesPath}/Bin/zip-action`, shellCallback);
zipAction.stdout.pipe(process.stdout)
zipAction.on("exit", () => console.log("ZIP action finished"))
}
// Callback
function shellCallback(error, stdout, stderr) {
//console.log(error, stdout)
console.log("error: " + error)
//console.log("stdout: " + stdout)
//console.log("stderr: " + stderr)
}
// Execute bash script
//const exec = require("child_process").exec;
//exec("/Users/aronsommer/Documents/Xcode-Projects/CocoaWebView/Resources/caddy-start", shellCallback);
// Execute bash script and than do something if finished
// CAUTION!!! ${process.resourcesPath} does not work when yarn start, only works with app in dist folder
//const child = require("child_process").exec(`${process.resourcesPath}/Bin/test`, shellCallback);
//child.stdout.pipe(process.stdout)
//child.on("exit", () => console.log("guguuus fertig"))
// Load file from Resources folder
// CAUTION!!! ${process.resourcesPath} does not work when yarn start, only works with app in dist folder
//mainWindow.loadURL(`file:///${process.resourcesPath}/Content/index.html`);
//console.log(`file:///${process.resourcesPath}/Content/index.html`);
It looks like a scoping problem.
At the very end of your createWindow() function add the following line.
function createWindow() {
...
return mainWindow; // <-- Add this line
}
In your app.whenReady() function edit the following line.
app.whenReady().then(() => {
// windowURL = `file:///${process.resourcesPath}/Content/index.html`;
mainWindow = createWindow(); // <-- Edit this line
If functions in your above code are seperated into their own files then you can do something similar as shown below.
main.js
const { app, BrowserWindow } = require('electron')
const appMainWindow = require('mainWindow');
let mainWindow;
app.whenReady().then(() => {
mainWindow = appMainWindow.createWindow();
// Re-activate Application when in macOS dock.
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) { appMainWindow.createWindow(); }
})
})
mainWindow.js
let mainWindow;
function createWindow() {
...
return mainWindow;
}
function getWindow() {
return mainWindow;
}
module.exports = {createWindow, getWindow};
startCaddy.js
const appMainWindow = require('mainWindow');
function StartCaddy() {
...
appMainWindow.getWindow().loadURL("http://127.0.0.1:2015/");
...
}

Can not update electron application

Does anybody hit this error while updating autoupdate for electron application.
It says:
Error: Can not find Squirrel
enter image description here
below is my code:
const server = 'http://10.172.120.67:8081'
// const url = `${server}/update/${process.platform}/${app.getVersion()}`
const url = `${server}/update/`
console.log(url)
autoUpdater.setFeedURL({ url })
autoUpdater.on('update-downloaded', (event, releaseNotes, releaseName) => {
console.log("getin")
const dialogOpts = {
type: 'info',
buttons: ['Restart', 'Later'],
title: 'Application Update',
message: process.platform === 'win32' ? releaseNotes : releaseName,
detail: 'A new version has been downloaded. Restart the application to apply the updates.'
}
dialog.showMessageBox(dialogOpts).then((returnValue) => {
if (returnValue.response === 0) autoUpdater.quitAndInstall()
})
})
autoUpdater.on('error', message => {
console.error('There was a problem updating the application')
console.error(message)
})
autoUpdater.on('update-avliable', function (info){
console.log('update-avliable')
})
autoUpdater.on('checking-for-update', function () {
console.log('checking-for-update')
});
console.log("hello electron")
autoUpdater.checkForUpdates();
Try calling the check for updates when the app is ready:
app.on('ready', function() {
autoUpdater.checkForUpdates();
}

Emitting Main to Main event from electron menu

I have a listener for 'select-current-project' in my Main process. It works fine when triggered from the renderer process, but how can I emit the same event from the Menu?
Menu is created in default createWindow()
var menu = Menu.buildFromTemplate([
{
label: 'Menu',
submenu: [
{
label:'Open',
click() {
// neither of these works
// app.emit('select-current-project')
// ipcRenderer.send('select-current-project')
},
accelerator: 'CmdOrCtrl+O'
},
{
label:'Exit',
click() {
app.quit()
}
},
]
}
])
My event handler
ipcMain.on('select-current-project', async (event, arg) => {
// code
})
I'm not sure I understand the issue you are describing – but here is what I do:
The function showProjectPicker exists in main.js and is called by a menu action as shown below. If I want to call it from the renderer process I send a message over IPC.
{
label: 'File',
submenu: [
{ label: 'Open Project...', click: () => { showProjectPicker(); }, accelerator: 'CmdOrCtrl+o' }
]
}

Electron require ipcRenderer not working

I am trying to do a simple ipc.send and ipc.on but for some reason I am getting undefined on this electron require.
libs/custom-menu.js:
'use-strict';
const BrowserWindow = require('electron').BrowserWindow;
const ipcRenderer = require('electron').ipcRenderer;
exports.getTemplate = function () {
const template = [
{
label: 'Roll20',
submenu: [
{
label: 'Player Handbook',
click() {
console.log('test');
},
},
],
},
{
label: 'View',
submenu: [
{
label: 'Toggle Fullscreen',
accelerator: 'F11',
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
}
},
},
{
label: 'Toggle Developer Tools',
accelerator: (function () {
if (process.platform === 'darwin') {
return 'Alt+Command+I';
}
return 'Ctrl+Shift+I';
}()),
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.toggleDevTools();
}
},
},
{
label: 'Reload',
accelerator: 'F5',
click() {
BrowserWindow.getFocusedWindow().reloadIgnoringCache();
},
},
],
},
{
label: 'Random Generators',
submenu: [
{
label: 'World Generator',
click() {
ipcRenderer.send('show-world');
},
},
],
},
];
return template;
};
The error is
cannot read property 'send' of undefined.
The BrowserWindow module is only available in the main process, the ipcRenderer module is only available in the renderer process, so regardless of which process you run this code in it ain't gonna work. I'm guessing since ipcRenderer is not available you're attempting to run this code in the main process.
I know this answer might have been too late for you but for other
If you're trying access any of main process modules from renderer process you will need to go through remote module,
const {BrowserWindow} = require('electron').remote
see documentation remote
Just for those who can't get this to work in react app ipcRenderer or in any environment that requires preload file.
preload setup
These lines worked for me:
app.commandLine.appendSwitch('ignore-certificate-errors', 'true')
app.commandLine.appendSwitch('allow-insecure-localhost', 'true')
In the renderer process, the script tags that have the "require" statement needs to be:
<script type="javascript"></script>
Placing your call to require in a script tag without the type set doesn't work.

Resources