Emitting Main to Main event from electron menu - electron

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' }
]
}

Related

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/");
...
}

How to process events from the application menu in the renderer

The main process of an electron application receives the events when a user select a menu option
const menu = {
label: 'Foo',
submenu: [
{
label: 'Bar',
click: () => {
// execute function in render process
},
},
]
});
Menu.setApplicationMenu(Menu.buildFromTemplate(menu));
and this should execute a function in the render process.
How would I intercept the menu events in the render process or alternatively execute a function in the render process from the main process?
You can send a message to the renderer when the menu item is clicked, then do what you want when the renderer receives the message.
This communication is called IPC, Inter Process Communication
Here's what the menu item would look like:
{
label: 'Bar',
click: () => {
win.webContents.send('menuItemClicked', 'Clicked!');
},
},
Then add this code to your renderer:
var ipcRenderer = require('electron').ipcRenderer;
ipcRenderer.on('menuItemClicked', function (evt, message)
{
console.log(message); // Outputs: Clicked!
// Do your renderer stuff here.
});
And voila!

electron menu accelerator not working

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)
})

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.

Sencha Touch not initializing

MY sencha touch application is working fine in all browsers, however when I save the url to the iPad home screen. It will not load and only shows a blank screen. I get no JS errors and nothing comes through the log when debugging. Heres a sample of the app:
script type="text/javascript">
var rootPanel;
if (navigator.userAgent.match(/iPad/i)) {
viewport = document.querySelector("meta[name=viewport]");
viewport.setAttribute('content', 'width=980');
}
Ext.application({
launch: function () {
var contactForm = Ext.create('Ext.form.FormPanel', {
standardSubmit: true,
fullscreen: true,
items: [{
xtype: 'titlebar',
title: 'My App',
docked: 'top'
}, {
xtype: 'fieldset',
items: [{
xtype: 'textfield',
name: 'LoginName',
label: 'Login Name:'
}, {
xtype: 'passwordfield',
name: 'Password',
label: 'Password:'
},
{
xtype: 'hiddenfield',
name: 'ReturnUrl',
value: '/returnUser.html'
}] // items
}, {
xtype: 'toolbar',
layout: {
pack: 'center'
}, // layout
ui: 'plain',
items: [{
xtype: 'button',
text: 'Reset',
ui: 'decline',
handler: function (btn, evt) {
Ext.Msg.confirm('', 'Are you sure you want to reset this form?', function (btn) {
if (btn === 'yes') {
contactForm.setValues({
LoginName: '',
Password: ''
}); // contactForm()
} // switch
}); // confirm()
}
}, {
xtype: 'button',
text: 'Submit',
ui: 'confirm',
handler: function (btn, evt) {
var values = contactForm.getValues();
contactForm.submit({
url: 'Login',
method: 'POST',
waitTitle: 'Connecting',
waitMsg: 'Sending data...',
success: function (form, result) {
Ext.Msg.alert('Login succeeded!', result.response.reason);
},
failure: function (form, result) {
Ext.Msg.alert('Login Failed!', result.response.reason);
}
});
} // handler
}] // items (toolbar)
}] // items (formpanel)
}); // create()
} // launch
}); // application()
$(document).ready(function () {
});
I put an alert in the launch method of Ext.Application but it does not show. When I put it an alert in the document.ready function it does show. I should also note it DOES work on the ipad browser, just not when launched from the icon on the homescreen.
i faced a similar issue in android actually in my case the problem was because of Ext.Loader not enabled I see you have not included it either. Include this script before Ext.application & see if it works
Ext.Loader.setConfig({
enabled:true
});
Ext.application({...});
It appears to be the version of sencha I was using which was 2.1.0 I believe, as soon as I updated to the latest(2.1.1 Build Date 2013-02-05 12:25:50 ) I works fine.

Resources