Reputation: 341
window close
button will prompt the user to (a) save their work (if necessary) or (b) just quit.window.beforeunload
to achieve this, but find I am getting stuck in a loop, where trying to "quit" makes the same prompt appear ad infinitum.Here's some code:
windowCloseCheck() {
window.onbeforeunload = function(e) {
e.returnValue = false;
// window.alert('try to close me');
if(file.isUnsaved() || file.hasChanged()) {
// prompt - save or just quit?
file.fileWarning('You have unsaveeed work.', 'Save', 'Quit', function(){
// OPTION A - save
file.save();
}, function() {
// OPTION B: Quit.
ipcRenderer.send('quitter')
})
}
else {
// file is saved and no new work has been done:
ipcRenderer.send('quitter')
}
windowCloseCheck
is invoked when the application is setup, initiating an event listener for closing the window. My conditional is checking if the file is unsaved or has changed from the original.
fileWarning
is a function that just wraps the electron dialog box, making a pop up appear with two choices and respective functions to call for each choice.
The rest of the code is available if I'm (probably) leaving out necessary information. Would be happy to clarify if I'm not being very clear.
Upvotes: 29
Views: 19830
Reputation: 61724
The following is a working solution for latest Electron, based on awijeet's answer and Sergio Mazzoleni's comment.
At the bottom of createWindow()
, below your win = new BrowserWindow(...)
, use:
win.on('close', function(e) {
const choice = require('electron').dialog.showMessageBoxSync(this,
{
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
message: 'Are you sure you want to quit?'
});
if (choice === 1) {
e.preventDefault();
}
});
Upvotes: 17
Reputation: 124
// Create the browser window.
mainWindow = new BrowserWindow({width: 400, height: 400})
mainWindow.on('close', function(e){
e.preventDefault();
var choice = require('electron').dialog.showMessageBox(mainWindow,
{
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
message: 'Are you sure you want to quit?'
});
choice.then(function(res){
// 0 for Yes
if(res.response== 0){
// Your Code
}
// 1 for No
if(res.response== 1){
// Your Code
}
}
});
Notice that dialog.showMessageBox
Returns Promise<Object>
According to Electron Doc
And change this
to mainWindow
This is modified answer for original answer to Awijeet
Upvotes: 3
Reputation: 1922
If the user would choose the save button, then we should save the data and close the window using window.close()
.
But, With this approach we will get an infinite loop that asks for saving the unsaved work because window.close()
will emit window's close
event again.
To solve this issue, we must declare a new boolean var forceQuit
that initially set to false
then we set it to true
after saving the data or if the user decided to just close without saving the data.
import { app, BrowserWindow, dialog } from 'electron';
let win = null;
let forceQuit = false;
app.on('ready', () => {
win = new BrowserWindow({
// Your window options..
});
mainWindow.on('close', e => {
if (!forceQuit) {
const clickedButtonId = dialog.showMessageBox(mainWindow, {
type: 'warning',
buttons: ['Save', 'Cancel', `Don't save`],
cancelId: 1,
title: 'Confirm',
message: 'You have unsaved work!'
});
if (clickedButtonId === 0) {
e.preventDefault();
console.log('Saving...');
/** Here do your data saving logic */
forceQuit = true;
win.close();
} else if (clickedButtonId === 1) {
e.preventDefault();
} else if (clickedButtonId === 2) {
forceQuit = true;
win.close();
}
}
});
});
Upvotes: 1
Reputation: 21
you can add a local variable to avoid mainWindow.destroy(), like:
let showExitPrompt = true;
// ask renderer process whether should close the app (mainWindow)
mainWindow.on('close', e => {
if (showExitPrompt) {
e.preventDefault(); // Prevents the window from closing
mainWindow.webContents.send('on-app-closing');
}
});
// renderer allows the app to close
ipcMain.on('quitter', (e) => {
showExitPrompt = false;
mainWindow.close();
})
Upvotes: 2
Reputation: 396
Awijeet's solution is just perfect! Bth, thx Awijeet: it saved me hours! ;-)
In addition, if you need to go further, be aware e.preventDefault()
is spreading everywhere in the code. Once you managed properly the preventDefault()
you need to turn the variable e.defaultPrevented = false
to get back to the natural behavior of your app.
Actually, it seems e.preventDefault()
function is turnind the variable e.defaultPrevented
to true
until you change its value.
Upvotes: 1
Reputation: 1586
Please add following block inside the function where you have defined browser window(In my case it's createWindow() function declared in main.js)
// Create the browser window.
mainWindow = new BrowserWindow({width: 400, height: 400})
mainWindow.on('close', function(e){
var choice = require('electron').dialog.showMessageBox(this,
{
type: 'question',
buttons: ['Yes', 'No'],
title: 'Confirm',
message: 'Are you sure you want to quit?'
});
if(choice == 1){
e.preventDefault();
}
});
Upvotes: 28
Reputation: 341
I ended up going with using window.destroy()
to smash the render process to bits (from the main process):
ipcMain.on('quitter', (e) => {
mainWindow.destroy(); // necessary to bypass the repeat-quit-check in the render process.
app.quit()
})
Not really sure if this is recommended as there seem to be better ways to properly quit an electron app. Still happy to get any feedback if you know a better answer!
/shrug!
Upvotes: 4