Electron, Creating Duplicate of Main Window On Button Click

Viewed 2197

I am making an electron app and the user to be able to open as many instances of the main window (the one that opens by default) as they would like.

I would like them to be able to do this by simply clicking a button within the index.html.

How would this be possible within the following default apps code ?

main.js

const { app, BrowserWindow } = require('electron')

function createWindow () {
  // Create the browser window.
  const win = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  // and load the index.html of the app.
  win.loadFile('index.html')

  // Open the DevTools.
  win.webContents.openDevTools()
}

// 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.whenReady().then(createWindow)

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

app.on('activate', () => {
  // 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 (BrowserWindow.getAllWindows().length === 0) {
    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.

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
    <!-- https://electronjs.org/docs/tutorial/security#csp-meta-tag -->
    <meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';" />
  </head>
  <body>
    <button onclick="openWindow()">click to open new window</button>
  </body>
</html>

2 Answers

All you need to do is send a message to your main process when you click your button, then in main.js create a new window whenever we receive that message.

So first send a message to main.js from your openWindow() function like this:

var ipcRenderer = require('electron').ipcRenderer;
function openWindow () {
    ipcRenderer.send('asynchronous-message', 'createNewWindow');
}

Then we listen for the message in main.js like this:

var ipcMain = require('electron').ipcMain;
ipcMain.on('asynchronous-message', function (evt, message) {
    if (message == 'createNewWindow') {
        // Message received.
        // Create new window here.
    }
});

Then all you need to do is create a new window when you receive the 'createNewWindow' message.

See docs for sending a message to the main process

See docs for receiving messages in the main process

You can achieve your goal in two ways.

You should change your index.html to like this as pre-requirements At your index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <h1>Hello World!</h1>
    <body>
    <button onclick="require('./renderer.js').openWindow()">click to open new window</button>
  </body>
    <!-- All of the Node.js APIs are available in this renderer process. -->
    We are using Node.js <script>document.write(process.versions.node)</script>,
    Chromium <script>document.write(process.versions.chrome)</script>,
    and Electron <script>document.write(process.versions.electron)</script>.

    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>
  1. Using ipc Communiation.

At your main.js

const {app, BrowserWindow, ipcMain} = require('electron')
let browserWindows = [];

function createWindow () {
  // Create the browser window.
  let newWindow = new BrowserWindow({
    width: 800,
    height: 600,
    webPreferences: {
      nodeIntegration: true
    }
  })

  newWindow.loadFile('index.html')


  newWindow.on('closed', function () {
    newWindow = null
  })

  browserWindows.push(newWindow)
}

app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', function () {
  if (process.platform !== 'darwin') {
    app.quit()
  }
})

ipcMain.on('createNewWindow', (evnet, args) => {
  createWindow();
})

At your renderer.js

const { ipcRenderer } = require('electron')
module.exports.openWindow = event => {
  ipcRenderer.send('createNewWindow', {});
}
  1. Not using ipc. you can create directly. Create browserWindow directly at your renderer without ipc Communitaion. Hence you enable the node api at your renderer. So that

At your renderer.js

const {BrowserWindow} = require('electron').remote
module.exports.openWindow = event => {
  const newWindow = new BrowserWindow({
      width: 800,
      height: 600,
      webPreferences: {
        nodeIntegration: true
      }
    })

  // and load the index.html of the app.
  newWindow.loadFile('index.html')
}

In this way, you don't need to change your main.js

Related