Tees
Tees

Reputation: 341

Prompt to save / quit before closing window

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

Answers (7)

Francesco Borzi
Francesco Borzi

Reputation: 61724

Answer for Electron 7+

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

Close Electron app confirm dialog

Upvotes: 17

Abdullah Ockba
Abdullah Ockba

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

parse
parse

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

balloy
balloy

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

Jerome
Jerome

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

Awijeet
Awijeet

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

Tees
Tees

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

Related Questions