NodeJS - Electron tray icon disappearing after a minute - javascript

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

Related

Electron is not killing application process, even with app.quit()

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

How to use electron-positioner

There is no explicit example of how to position your ELECTRON JS app to a specific area. The only syntax is available on GitHub, and it does not describe it well.
It's quite straight forward. Consider this code that positions the mainWindow once the ready event fires. You should be able to drop in the 'ready' event below to demonstrate the positioner.
// load the module
const Positioner = require('electron-positioner');
let mainWindow = null;
// create the main window
async function createWindow () {
mainWindow = new BrowserWindow({
height: 420,
width: 600,
x: 0, // default position left
y: 0, // default position top
show: false,
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'node_modules', 'electron', 'dist', 'electron-bridge.js')
}
});
// reposition after creating the window.
app.on('ready', async () => {
await createWindow();
let positioner = new Positioner(mainWindow);
positioner.move('bottomRight');
});
Of course this affect may be achieved with the x and y values via the BrowserWindow constructor, but it is quite handy to have canned positions provided by the module.

Electron TrayIcon not working properly on linux

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

Two browsers in the same Electron window

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

Communicating between two renderer processes in Electron

I am writing an Eletron program. In the program there is one index-window which is created by the main process (main.js). In this window there is a list of files (images). When I click on one of the files in that list I want to start a second window that displays that file.
That second window is started by the renderer process of the index-window (index.js). How can I communicate between the renderer process of the index-window and the renderer process of the second window?
Code:
Creating the index-window from the main process in main.js:
let win;
function createWindow(){
// Create the browser window.
win = new BrowserWindow({width: 1024, height: 768, minWidth: 800, minHeight: 600, show: false, icon: 'files/images/icon.png'});
win.loadURL(`file://${__dirname}/files/html/index.html`);
win.once('ready-to-show', () => {
win.show()
})
// Emitted when the window is closed.
win.on('closed', () => {
win = null;
});
}
app.on('ready', createWindow);
In the index.html index.js (renderer process) is started:
<script src="../javascript/index.js"></script>
In index.js the function create_sprite_window() is called which creates a child window:
const fs = require('fs');
const path = require('path');
const {BrowserWindow} = require('electron').remote
let child_windows = [];
function create_child_window(URL, width, height){
let rem_win = require('electron').remote.getCurrentWindow();
let new_win = new BrowserWindow({width: width, height: height, minWidth: 400, minHeight: 300, show: false, parent: rem_win, minimizable: true, maximizable: true, skipTaskbar: true});
child_windows[child_windows.length] = new_win;
console.log(child_windows);
new_win.loadURL(URL);
new_win.once('ready-to-show', () => {
new_win.show()
})
return new_win;
}
function create_sprite_window(){
new_win = create_child_window(`file://${__dirname}/../html/sprite_manager.html`, 800, 400);
}
The child windows are stored in the array child_windows.
Is it possible to then send the path of the image to the second window or, alternatively, edit the <img> tag of the second window (setting the source of the <img> tag in the second window to the image with getElementById.src = path;) from the index-window?.
I have found the answer by myself.
To show the correct image in the second renderer window, I add a GET parameter to the URL which contains the path of the image.

Categories

Resources