Is it possible to minimize window when it has tray? - electron

Is it possible to use .minimize() on window (Windows OS) when it has tray? Now I'd like to hide it into windows dock (Or how is it called?), but when I have tray in an app and trying to call .minimize() on browser window, nothing happens. Sure, I can use .hide() instead, but I'd like to keep it in dock instead of just fully hiding it.
I'm using Electron 10.1.1.
For example:
// First we have to create the window
const { BrowserWindow, Tray } = require('electron')
const win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL('https://github.com');
// Then we have to create tray
const tray = new Tray('/path/to/my/icon')
const contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', type: 'radio' },
{ label: 'Item2', type: 'radio' },
{ label: 'Item3', type: 'radio', checked: true },
{ label: 'Item4', type: 'radio' }
])
tray.setToolTip('This is my application.')
tray.setContextMenu(contextMenu)
// Here we expect application will be hidden into windows dock, but nothing happens.
win.minimize()
Will appreciate any help, thanks!

Your code runs fine if the BrowserWindow is created when the app is ready.
const {app,BrowserWindow,Tray,Menu} = require("electron");
const path = require("path");
let win, tray;
app.on("ready", function() {
win = new BrowserWindow({ width: 800, height: 600 });
win.loadURL("https://github.com");
const trayIcon = path.join(__dirname, "icon.png");
tray = new Tray(trayIcon);
const contextMenu = Menu.buildFromTemplate([
{ label: "Item1", type: "radio" },
{ label: "Item2", type: "radio" },
{ label: "Item3", type: "radio", checked: true },
{ label: "Item4", type: "radio" }
]);
tray.setToolTip("This is my application.");
tray.setContextMenu(contextMenu);
win.minimize();
});
When trying to create the BrowserWindow without waiting for the ready event, you should get the following error:
Error: Cannot create BrowserWindow before app is ready

Related

Electron: parent - child browserwindow macos behavior

I have the following scenario: when I minimize the main window, the main window hides and a child window opens, and when I click on it, the main window opens again and the child window closes.
main.js
app.on("ready", () => {
// Main window
const mainWindow = windows.createMainWindow(app);
provider.generateProviderData(mainWindow)
const ses = mainWindow.webContents.session
// Display Dev Tools
//mainWindow.openDevTools();
// Quit when all windows are closed.
app.on("window-all-closed", () => {
app.quit()
});
ipcMain.on("windowControls:maximize", (ipcEvent) => {
const minimizedWindow = findBrowserWindow(ipcEvent)
const mainWindow = minimizedWindow.getParentWindow()
const minimizedWindowPosition = minimizedWindow.getPosition()
mainWindow.setPosition(minimizedWindowPosition[0], minimizedWindowPosition[1])
mainWindow.show()
minimizedWindow.close()
})
ipcMain.on("windowControls:minimize", (_ipcEvent) => {
mainWindow.hide()
windows.createMinimizedWindow(mainWindow)
})
function findBrowserWindow(ipcEvent) {
return BrowserWindow.fromWebContents(ipcEvent.sender)
}
});
window.js
exports.createMinimizedWindow = (parent) => {
const minimizedWindow = new BrowserWindow({
width: 220,
height: 50,
frame: false,
resizable: false,
transparent: true,
alwaysOnTop: true,
parent: parent,
webPreferences: {
preload: path.join(__dirname, "../preload.js"),
},
})
minimizedWindow.loadFile("minimized.html")
const parentPosition = parent.getPosition()
minimizedWindow.setPosition(parentPosition[0], parentPosition[1])
return minimizedWindow
}
The problem is that on Windows platform it works ok, but on MacOs it does not work and always keeps the parent window behind the child and does not keep hidden.

how to apply electron-context-menu in window?

I installed electron-context-menu using the command
npm i electron-context-menu
And then I used the code that is on this site
const {app, BrowserWindow} = require('electron');
const contextMenu = require('electron-context-menu');
contextMenu({
showSaveImageAs: true
});
let mainWindow;
(async () => {
await app.whenReady();
mainWindow = new BrowserWindow({
webPreferences: {
spellcheck: true
}
});
})();
But when I right click on the window, the ContextMenu with the items doesn't appear.
A white window only appears:
What should I do to make ContextMeu appear?
You won't need a lib for do this (And IMO you Shouldn't). The electron API already give to you a context-menu ready to use.
If you already use contextBridge just follow the electron site steps.
main.ts
// ...
ipcMain.on('show-context-menu', (event) => {
const template = [
{
label: 'Menu Item 1',
click: () => { event.sender.send('context-menu-command', 'menu-item-1') }
},
{ type: 'separator' },
{ label: 'Menu Item 2', type: 'checkbox', checked: true }
]
const menu = Menu.buildFromTemplate(template)
menu.popup(BrowserWindow.fromWebContents(event.sender))
})
renderer.ts
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
ipcRenderer.send('show-context-menu')
})
ipcRenderer.on('context-menu-command', (e, command) => {
// What it will do when this options is click it
})
You can create a preload for each action too if you want
Also, if you want the browser default contextMenu, here comes a example.

How to make about popup in electron

I want to know how can I add about button in my electron app that opens popup with software details? something like this:
Code
This is how currently my index.js looks like:
const { app, BrowserWindow, Menu } = require('electron');
const isDevMode = require('electron-is-dev');
const { CapacitorSplashScreen, configCapacitor } = require('#capacitor/electron');
const path = require('path');
// Place holders for our windows so they don't get garbage collected.
let mainWindow = null;
// Placeholder for SplashScreen ref
let splashScreen = null;
//Change this if you do not wish to have a splash screen
let useSplashScreen = true;
// Create simple menu for easy devtools access, and for demo
const menuTemplateDev = [
{
label: 'Options',
submenu: [
{
label: 'Open Dev Tools',
accelerator: "CmdOrCtrl+D",
click() {
mainWindow.openDevTools();
},
},
],
},
{
label: 'Help',
submenu: [
{
label: 'Support',
accelerator: "CmdOrCtrl+H",
click: async () => {
const { shell } = require('electron');
await shell.openExternal('https://example.com/contact-us');
}
},
{ type: 'separator' },
{
label: 'Quit!',
accelerator: "CmdOrCtrl+Q",
click() {
app.quit();
}
}
]
}
];
const menuTemplateProd = [
{
label: 'Help',
submenu: [
{
label: 'Support',
accelerator: "CmdOrCtrl+H",
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://example.com/contact-us')
}
},
{ type: 'separator' },
{
label: 'Quit!',
accelerator: "CmdOrCtrl+Q",
click() {
app.quit();
}
}
]
}
];
async function createWindow () {
// Define our main window size
mainWindow = new BrowserWindow({
height: 920,
width: 1600,
show: false,
// icon: __dirname + '/Icon/Icon.icns',
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'node_modules', '#capacitor', 'electron', 'dist', 'electron-bridge.js')
}
});
mainWindow.setIcon(path.join(__dirname, '/splash_assets/logo.png'));
configCapacitor(mainWindow);
if (isDevMode) {
// Set our above template to the Menu Object if we are in development mode, dont want users having the devtools.
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplateDev));
// If we are developers we might as well open the devtools by default.
mainWindow.webContents.openDevTools();
} else {
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplateProd));
}
if(useSplashScreen) {
splashScreen = new CapacitorSplashScreen(mainWindow);
splashScreen.init();
} else {
mainWindow.loadURL(`file://${__dirname}/app/index.html`);
mainWindow.webContents.on('dom-ready', () => {
mainWindow.show();
});
}
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some Electron APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X 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 OS X 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();
}
});
// Define any IPC or other custom functionality below here
Any suggestions?
SOLVED
I've added new window function and new index file. And changed my index.js to the following code:
const { app, BrowserWindow, Menu } = require('electron');
const isDevMode = require('electron-is-dev');
const { CapacitorSplashScreen, configCapacitor } = require('#capacitor/electron');
const path = require('path');
// Place holders for our windows so they don't get garbage collected.
let mainWindow = null;
let childWindow = null; // <---- ADDED
// Placeholder for SplashScreen ref
let splashScreen = null;
//Change this if you do not wish to have a splash screen
let useSplashScreen = true;
// Create simple menu for easy devtools access, and for demo
const menuTemplateDev = [
{
label: 'Options',
submenu: [
{
label: 'Open Dev Tools',
accelerator: "CmdOrCtrl+D",
click() {
mainWindow.openDevTools();
},
},
],
},
{
label: 'Help',
submenu: [
{ // <---- ADDED
label: 'About', // <---- ADDED
accelerator: "F1", // <---- ADDED
click: async () => { // <---- ADDED
createAboutWindow(); // <---- ADDED
} // <---- ADDED
}, // <---- ADDED
{
label: 'Support',
accelerator: "CmdOrCtrl+H",
click: async () => {
const { shell } = require('electron');
await shell.openExternal('https://example.com/contact-us');
}
},
{ type: 'separator' },
{
label: 'Quit!',
accelerator: "CmdOrCtrl+Q",
click() {
app.quit();
}
}
]
}
];
const menuTemplateProd = [
{
label: 'Help',
submenu: [
{ // <---- ADDED
label: 'About', // <---- ADDED
accelerator: "F1", // <---- ADDED
click: async () => { // <---- ADDED
createAboutWindow(); // <---- ADDED
} // <---- ADDED
}, // <---- ADDED
{
label: 'Support',
accelerator: "CmdOrCtrl+H",
click: async () => {
const { shell } = require('electron')
await shell.openExternal('https://example.com/contact-us')
}
},
{ type: 'separator' },
{
label: 'Quit!',
accelerator: "CmdOrCtrl+Q",
click() {
app.quit();
}
}
]
}
];
async function createWindow () {
// Define our main window size
mainWindow = new BrowserWindow({
height: 920,
width: 1600,
show: false,
// icon: __dirname + '/Icon/Icon.icns',
webPreferences: {
nodeIntegration: true,
preload: path.join(__dirname, 'node_modules', '#capacitor', 'electron', 'dist', 'electron-bridge.js')
}
});
mainWindow.setIcon(path.join(__dirname, '/splash_assets/logo.png'));
configCapacitor(mainWindow);
if (isDevMode) {
// Set our above template to the Menu Object if we are in development mode, dont want users having the devtools.
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplateDev));
// If we are developers we might as well open the devtools by default.
mainWindow.webContents.openDevTools();
} else {
Menu.setApplicationMenu(Menu.buildFromTemplate(menuTemplateProd));
}
if(useSplashScreen) {
splashScreen = new CapacitorSplashScreen(mainWindow);
splashScreen.init();
} else {
mainWindow.loadURL(`file://${__dirname}/app/index.html`);
mainWindow.webContents.on('dom-ready', () => {
mainWindow.show();
});
}
}
async function createAboutWindow () { // <---- ADDED
// Define our main window size
childWindow = new BrowserWindow({ // <---- ADDED
height: 400, // <---- ADDED
width: 600, // <---- ADDED
show: false, // <---- ADDED
minimizable: false, // <---- ADDED
maximizable: false, // <---- ADDED
parent: mainWindow, // <---- ADDED
// icon: __dirname + '/Icon/Icon.icns', // <---- ADDED
webPreferences: { // <---- ADDED
nodeIntegration: true, // <---- ADDED
preload: path.join(__dirname, 'node_modules', '#capacitor', 'electron', 'dist', 'electron-bridge.js') // <---- ADDED
} // <---- ADDED
}); // <---- ADDED
childWindow.setIcon(path.join(__dirname, '/splash_assets/logo.png')); // <---- ADDED
configCapacitor(childWindow); // <---- ADDED
childWindow.removeMenu(); // <---- ADDED
childWindow.loadURL(`file://${__dirname}/index_child.html`); // <---- ADDED
childWindow.webContents.on('dom-ready', () => { // <---- ADDED
childWindow.show(); // <---- ADDED
}); // <---- ADDED
} // <---- ADDED
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some Electron APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed.
app.on('window-all-closed', function () {
// On OS X 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 OS X 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();
}
if (childWindow === null) { // <---- ADDED
createAboutWindow(); // <---- ADDED
} // <---- ADDED
});
// Define any IPC or other custom functionality below here
Final result
Hope it can help those in need :) .

Show Electron Windows Tray Icon on main taskbar, not under Show Hidden Icons

I'm building an electron app and I realized that the tray icon on Windows does now appear on the main taskbar, but in the area that pops out when you click the upward arrow.
enter image description here
I see that Dropbox has theirs on the main taskbar and I know I can move mine on the main taskbar, but I would love to have it there by default.
Thank you in advance!
I was looking for the same things, this is what worked for me:
const { Menu, Tray } = require('electron')
let tray = null
app.whenReady().then(() => {
//add your path
tray = new Tray(path.join(app.getAppPath(), 'src', 'assets', 'company.jpg'))
const contextMenu = Menu.buildFromTemplate([
{ label: 'Item1', type: 'radio' },
{ label: 'Item2', type: 'radio' },
{ label: 'Item3', type: 'radio', checked: true },
{ label: 'Item4', type: 'radio' }
])
tray.setToolTip('This is my application.')
tray.setContextMenu(contextMenu)
})
hope It helps, and make sure you use the app.getAppPath() otherwise it will run on your electron build but won't work on win. package

How to make a dialog to block the BrowserWindow when open?

I call dialog.showOpenDialog() to find the path to the folder. But the problem is that this does not block the mainWindow. It is necessary that when the standard path selection GUI appears, the program continues to work only after the path selection is completed. Googled and realized that you need to use remote. But nothing happens.
I get:
Cannot destructure property dialog of 'undefined' or 'null'.
if from electron.remote take dialog.
I tried a lot of different things (and these are not all attempts, just what I remembered):
const { dialog } = require ('electron').remote;
var remote = electron.remote;
var dialog = remote.dialog;
const dialog = require ('electron').remote.dialog;
I tried to connect a lot and yet.
My main.js:
const url = require('url');
const path = require('path');
const {dialog} = electron.remote;
const {app, BrowserWindow, Menu} = electron;
app.on('ready', function () {
const {start_width, start_height} = electron.screen.getPrimaryDisplay().workAreaSize;
mainWindow = new BrowserWindow({
minWidth: 1250,
minHeight: 800,
height: start_height,
width: start_width,
center: true,
show: false,
});
mainWindow.loadURL(url.format({
pathname: path.join(__dirname, 'templates/mainWindow.html'),
protocol: 'file:',
slashes: true
}));
mainWindow.on('closed', function () {
app.quit();
});
// mainWindow.webContents.openDevTools();
const mainMenu = Menu.buildFromTemplate(mainMenuTemplate);
Menu.setApplicationMenu(mainMenu);
mainWindow.maximize();
mainWindow.show();
});
function createAddWindow() {
addWindow = new BrowserWindow({
width: 300,
height: 200,
title: 'Add item'
});
addWindow.loadURL(url.format({
pathname: path.join(__dirname, 'templates/addWindow.html'),
protocol: 'file:',
slashes: true
}));
addWindow.on('close', function () {
addWindow = null;
})
}
const mainMenuTemplate = [
{
label: 'Analysis',
submenu: [{
label: 'Global search',
accelerator: 'Ctrl+O',
click() {
var path = createAddWindow();
console.log(path);
}
},
{
label: 'Search for a year',
accelerator: 'Ctrl+Alt+O',
click() {
console.log(folderSelect());//i need console.log (like the whole program) to continue to work after folderSelect returns some value.
}
},
{
label: 'Quit',
accelerator: process.platform == 'darwin' ? 'Command+Q' : 'Ctrl+Q',
click() {
app.quit();
}
},
]
}
];
function folderSelect() {
dialog.showOpenDialog({properties: ['openDirectory']}, function (path) {
console.log(path[0]);
if (path === undefined) {
return 'error';
}
else{
return path[0];
}
});
}
I need console.log (like the whole program) to continue to work after folderSelect returns some value.
For example, if I called the folderSelect function, you cannot interact with the program until the folder after selection window closes.
I saw a lot of similar questions on SO, but whatever I did nothing works.
In order to block the main window, you need to pass a BrowserWindow object to the dialog.showOpenDialog method as the first optional argument, the one you would like to attach the dialog to (mainWindow in your case I guess).
Quote from the docs:
dialog.showOpenDialog([browserWindow, ]options)
The browserWindow argument allows the dialog to attach itself to a
parent window, making it modal.
Now, how you make it happen is the whole different matter. It can be done in multiple ways, but if you are looking to invoke the dialog from renderer process the simplest one would be something like this:
import { remote } from 'electron'
remote.dialog.showOpenDialog(
remote.getCurrentWindow(), // <- notice this one
{ properties: ['openDirectory'] }
).then(result => {
// prefer promised API
})
The crucial part for whole thing to work properly is to have nodeIntegration enabled in your BrowserWindow options, which depending on a version of Electron you are using, you might or might not have (they've switched defaults from true to false in version 4). In any case it's better to have it set explicitly from now on. So that's how you would typically initiate your mainwindow now:
mainWindow = new BrowserWindow({
// ...
show: false,
webPreferences: {
nodeIntegration: true // <- this one
}
});

Resources