I'm trying to make an Electron app that runs on system tray, once the tray icon is clicked, the app window will appears.
I tested it on linux, windows and mac, on windows and mac it works perfectly, when I click the tray icon, the app window appears but on linux, when I click the tray icon a context menu appears (even tough i haven't set it) with the app name and the app window only appears if I click on the app name.
That's how I made the tray
let mainWindow
let tray = null;
function createWindow () {
mainWindow = new BrowserWindow({
width: 400,
height: 500,
skipTaskbar: true,
frame: false,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.setMenu(null)
mainWindow.hide();
tray = new Tray("./assets/icon#2x.png");
tray.on('click', () => {
mainWindow.isVisible() ? mainWindow.hide() : mainWindow.show()
})
mainWindow.on('show', () => {
tray.setHighlightMode('always')
const pos = tray.getBounds()
mainWindow.setPosition(pos.x - 195, pos.y + 30);
})
mainWindow.on('hide', () => {
tray.setHighlightMode('never')
})
mainWindow.loadFile('index.html')
mainWindow.on('closed', function () {
mainWindow = null
})
}
I want it to works how it works on windows and mac, when I click the tray icon, the app window appears, someone knows how to do it? Thanks!
just change your tray method from
tray.setHighlightMode()
to
tray.setToolTip()
this my code to prevent close win
win.on('show', () => {
//tray.setHighlightMode('always');
tray.setToolTip("Server Started");
});
win.on('hide', () => {
//tray.popUpContextMenu();
tray.setToolTip("Server Started");
});
This is an open issue with the electron project, there is nothing wrong with your code.
https://github.com/electron/electron/issues/14941
Related
Is there a way to let the program remember which URL was open when the user closed the program? For example if the user closes the application, the last URL gets added to the loadURL. The program is being used for users that only can interact with touchscreen and cant leave the specific site. I am using windows 10 and the newest version of electron.
// Modules to control application life and create native browser window.
const { app, BrowserWindow, Menu } = require("electron");
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let mainWindow;
function createWindow() {
// Create the browser window.
mainWindow = new BrowserWindow({
width: 800,
height: 600,
frame: false,
webPreferences: {
preload: `${__dirname}/preload.js`
}
});
// The loadURL that loads if you start up the application.
mainWindow.loadURL("https://google.com");
// Emitted when the window is closed.
mainWindow.on("closed", function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
}
// The toggleFullScreen function stated in the createMainMenu function.
function toggleFullscreen() {
if (mainWindow.isFullScreen()) {
mainWindow.setFullScreen(false);
} else {
mainWindow.setFullScreen(true);
}
}
function createMainMenu() {
const template = [
{
label: "Options",
submenu:
[
{
label: "Quit",
accelerator: "CmdOrCtrl+Q",
click() {
app.quit();
}
},
{
label: 'Toggle full screen',
accelerator: 'CmdOrCtrl+F',
click: () => {
toggleFullscreen();
}
},
{
label: 'Toggle developer tools',
accelerator: 'CmdOrCtrl+I',
click(item, focusedWindow){
focusedWindow.toggleDevTools();
}
}
]
}
];
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
}
// 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.on("ready", () => {
createWindow();
createMainMenu();
});
// Quit when all windows are closed.
app.on("window-all-closed", function() {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q.
if (process.platform !== "darwin") {
app.quit();
}
});
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 (mainWindow === null) {
createWindow();
}
});
// 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.
You have two options.
First, each time the URL changes, use LocalStorage in the renderer process. It will work just like in a web app.
Second, each the URL changes, have it send a message back to the main process, and have it write that to a local file. getPath('appData') can be used to get the OS-specific directory of where settings files can be written.
My electron app has a system tray menu and everything is working as is except when I right click the icon in the system tray to fully exit the application to kill the process. The system doesn't do anything.
I am trying to fully kill the process if a user does not want the application to run.
I've tried putting "app.quit()" as mentioned in the title however, the application doesn't do anything
function createWindow () {
// Create the browser window.
win = new BrowserWindow({ width: 350, height: 550, resizable: true})
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
if (!process.env.IS_TEST) win.webContents.openDevTools()
} else {
createProtocol('app')
// Load the index.html when not in development
win.loadURL('app://./index.html')
}
//position the electron window
var positioner = new Positioner(win)
positioner.move('bottomRight')
//logo for the task bar
win.setIcon('logo/logo.png');
win.on('minimize',function(event) {
event.preventDefault();
win.hide();
});
//clicking 'x' will close the app, but the app will still run in system tray (intended)
win.on('close', function(event) {
event.preventDefault();
win.hide();
})
}
//function to show/hide app when right clicking on the system tray and selecting "show/hide"
function toggleWindow() {
if (win.isVisible()) {
win.webContents.send('setVisible', false);
setTimeout(function () {
win.hide();
}, 300);
}
else {
win.show();
win.webContents.send('setVisible', true);
}
}
/**
* setting bottom right tray menu icon
* added left/right click events for system tray
*/
let tray = null
app.on('ready', () => {
tray = new Tray(path.join(__dirname, '../logo/tray_logo.png'))
tray.on('click', function handleClicked () {
toggleWindow();
})
const contextMenu = Menu.buildFromTemplate([
{ label: 'Show/Hide', type: 'normal', click: function () { toggleWindow(); } },
{ label: 'Exit', type: 'normal', click: function() { } } //exit is not working. I've tried app.quit();
])
tray.setToolTip('Activity')
tray.setContextMenu(contextMenu)
})
// Quit when all windows are closed.
app.on('window-all-closed', () => {
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if (process.platform !== 'darwin') {
app.quit()
}
})
I have no idea what's going on, to be honest.
I've been keeping an eye to the icon and it just vanishes after a few minutes. No, it does not go to the arrow near the clock:
This is my icon showing up (the explosion in red):
I don't know how to debug if the icon is there but empty or if there's an event triggering it to hide, or if the tray process closes itself because of a bug. Nothing happens in the console or my app.
Could someone please help? Below is my whole index.js:
const {app, BrowserWindow, Tray, Menu} = require('electron');
const path = require('path');
var win = '',
iconpath = path.join(__dirname, '/libs/img/icon.ico');
// Create the browser window
function createWindow () {
// BrowserWindow size
win = new BrowserWindow({
width: 800,
height: 720,
webPreferences: {
nodeIntegration: true
}
});
// tray menu
var contextMenu = Menu.buildFromTemplate([
{
label: 'Show app', click: function () {
win.show()
}
},
{
label: 'Quit', click: function () {
app.isQuiting = true;
app.quit();
}
}
]);
// Creates tray menu with tray icon
var appIcon = new Tray(iconpath);
// Define menu
appIcon.setContextMenu(contextMenu);
win.on('close', function () {
app.isQuiting = true;
app.quit();
});
// Load the index.html of the app
win.loadFile('./view/index.html');
}
app.on('ready', createWindow);
This is a well-known problem related to garbage collection, mentioned in the Electron FAQ page:
My app's window/tray disappeared after a few minutes.
So, a quick fix is to move up the declaration of the appIcon variable out of the createWindow function, next to the win variable for instance:
const {app, BrowserWindow, Tray, Menu} = require('electron');
const path = require('path');
var win = '',
appIcon = null,
iconpath = path.join(__dirname, '/libs/img/icon.ico');
// Create the browser window
function createWindow () {
// BrowserWindow size
win = new BrowserWindow({
width: 800,
height: 720,
webPreferences: {
nodeIntegration: true
}
});
// tray menu
var contextMenu = Menu.buildFromTemplate([
{
label: 'Show app', click: function () {
win.show()
}
},
{
label: 'Quit', click: function () {
app.isQuiting = true;
app.quit();
}
}
]);
// Creates tray menu with tray icon
appIcon = new Tray(iconpath);
// Define menu
appIcon.setContextMenu(contextMenu);
win.on('close', function () {
app.isQuiting = true;
app.quit();
});
// Load the index.html of the app
win.loadFile('./view/index.html');
}
app.on('ready', createWindow);
I was having the same problem, but I got the solution for this.
This happens when your tray variable which is used to store the tray gets garbage collected.
You can get rid of this just by making the variable global.
In your case create appIcon variable out of the createWindow function like this:
let appIcon = null;
and then assign tray object like this:
appIcon = new Tray(iconpath);
ref: https://www.electronjs.org/docs/faq#my-apps-tray-disappeared-after-a-few-minutes
I'd like to have one single Electron window, split in two parts:
the left part is a BrowserWindow loading https://gmail.com
the right part is another BrowserWindow loading Gmail too, but I'd like these two browsers to be "independent", i.e. the cookies/LocalStorage/etc. should be independent (like if we have a normal Chrome window vs. a incognito window) ; thus allowing to have one Gmail account on left / another account connected on the right part
some other UI buttons on top of the single Electron window.
This code works, but it creates 2 windows instead of one:
const { app, BrowserWindow } = require('electron')
const path = require('path')
app.once('ready', () => {
let win = new BrowserWindow({show: false})
win.once('show', () => { win.webContents.executeJavaScript('validateFlights()') })
win.loadURL('https://www.gmail.com')
win.show()
let win2 = new BrowserWindow({show: false})
win2.once('show', () => { win.webContents.executeJavaScript('validateFlights()') })
win2.loadURL('https://www.gmail.com')
win2.show()
})
How to have them in one window?
A little late, but to add two browsers within one window you have to use BrowserWindow.addBrowserView instead of BrowserWindow.setBrowserView. You'll get the following:
const { BrowserView, BrowserWindow, app } = require('electron')
function twoViews () {
const win = new BrowserWindow({ width: 800, height: 600 })
const view = new BrowserView()
win.addBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 400, height: 300 })
view.webContents.loadURL('https://electronjs.org')
const secondView = new BrowserView()
win.addBrowserView(secondView)
secondView.setBounds({ x: 400, y: 0, width: 400, height: 300 })
secondView.webContents.loadURL('https://electronjs.org')
app.on('window-all-closed', () => {
win.removeBrowserView(secondView)
win.removeBrowserView(view)
app.quit()
})
}
app.whenReady().then(twoViews)
Once you create two BrowserView objects, you just add them to the window. You'll also want to remove the views when tearing down the application. If you don't you might get a segmentation fault.
What you are looking for is BrowserView
From the docs:
A BrowserView can be used to embed additional web content into a BrowserWindow. It is like a child window, except that it is positioned relative to its owning window. It is meant to be an alternative to the webview tag.
It looks like this is what you want, the views can render separate HTML pages and position them relatively inside the same browser window.
// In the main process.
const { BrowserView, BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800, height: 600 })
win.on('closed', () => {
win = null
})
let view = new BrowserView({
webPreferences: {
nodeIntegration: false
}
})
win.setBrowserView(view)
view.setBounds({ x: 0, y: 0, width: 300, height: 300 })
view.webContents.loadURL('https://electronjs.org')
I have been looking for Electron app events for when the application is shown or hidden. I see in the docs that there is 'browser-window-blur' and 'browser-window-focus' but those do not do what I want.
I would like to know when the user has switched to another application or switched back to my app. The above events get triggered if the user switches between browser windows – including the "developer's tools" window.
The code in main.js
app.on('browser-window-focus', () => {
if (mainWindow) {
console.log('browser-window-focus');
mainWindow.webContents.send('projectMsg', { "event": "focus" });
}
});
app.on('browser-window-blur', () => {
console.log('browser-window-blur');
if (mainWindow) {
mainWindow.webContents.send('projectMsg', { "event": "blur" });
}
});
It seems to me that it works exactly as you described, so maybe the requirements are different.
This code
const {app, BrowserWindow} = require('electron')
app.on('browser-window-focus', (event, win) => {
console.log('browser-window-focus', win.webContents.id)
})
app.on('browser-window-blur', (event, win) => {
if (win.webContents.isDevToolsFocused()) {
console.log('Ignore this case')
} else {
console.log('browser-window-blur', win.webContents.id)
}
})
app.once('ready', () => {
new BrowserWindow()
new BrowserWindow().webContents.openDevTools({detach: true})
})
works the following way (in 3.0.3) given that nothing is focused initially:
Clicking on window 1 prints browser-window-focus 1
Clicking on window 2 prints browser-window-blur 1 browser-window-focus 2
Clicking on devtools window prints browser-window-blur 2 Ignore this case
So as far as I see devtool is not included in these events, windows are getting blurred for any other window focused (including devtool)
There is also show and hide, though you have to explicitly show/hide the app with win.show() and win.hide() to trigger these events.
Check out of these BrowserWindow's events:
Event: 'blur': Emitted when the window loses focus.
Event: 'show': Emitted when the window is shown.
For example:
app.once('ready', () => {
let mainWindow = new BrowserWindow({show: false}) //Create main window
mainWindow.on('show', () => {
//Do something
})
})
Hope this help.