Author: Kurosaki
This paper mainly explains the Electron window API and some problems encountered in the development.
Although the official documentation is comprehensive, developing a commercial-grade desktop application requires a deep understanding of the entire Electron API to meet the various requirements.
1. Create a window
BrowserWindow is used to create or manage new browser Windows, and each BrowserWindow has a process to manage it.
1.1. Simply create Windows
const { BrowserWindow } = require('electron');
const win = new BrowserWindow();
win.loadURL('https://github.com');
Copy the code
The effect is as follows:
1.1.2. The optimization
Problem: The electron BrowserWindow module is created with show:false not configured, and the default background is white. The window then requests HTML and a visual flicker appears.
To solve
const { BrowserWindow } = require('electron');
const win = new BrowserWindow({ show:false });
win.loadURL('https://github.com');
win.on('ready-to-show'.() = >{
win.show();
})
Copy the code
There’s a big difference
1.2. Management window
An administrative window is equivalent to how much the main process can interfere with the window.
- Window route jump
- Window Opens a new window
- Window size, location, etc
- Window display
- Window types (borderless window, parent window)
- Inside the window
JavaScript
的node
Permissions, preloading scripts, etc - .
All of these methods exist in the BrowserWindow module.
1.2.1. Managing the application creation window
The BrowserWindow module, when creating a window, returns instances of the window. These instances have a number of function methods that we use to manage and control the window.
Map objects are used here to store these window instances.
const BrowserWindowsMap = new Map<number, BrowserWindow>()
let mainWindowId: number;
const browserWindows = new BrowserWindow({ show:false })
browserWindows.loadURL('https://github.com')
browserWindows.once('ready-to-show'.() = > {
browserWindows.show()
})
BrowserWindowsMap.set(browserWindow.id, browserWindow)
mainWindowId = browserWindow.id // Record the current window as the main window
Copy the code
The window closes and the Map instance has to be deleted.
browserWindow.on('closed'.() = >{ BrowserWindowsMap? .delete(browserWindowID) })Copy the code
1.2.2. Managing Windows created by users
The main process can control many window behaviors, which will be listed in a subsequent article. The following is an example of how the main process control window creates a new window.
usenew-window
Listen for new window creation
// Create window listener
browserWindow.webContents.on('new-window'.(event, url, frameName, disposition) = > {
/ * *@params {string} disposition* background-tab: foreground +click * foreground- TAB: foreground +click * foreground * /})Copy the code
Note: For an explanation of the Disposition field, move to the Electron document, the Electron source, the Chrome source
extensionnew-window
After experiments, not all new window creation, new-window can capture.
Windows opened in the following way can benew-window
Event capture
window.open('https://github.com')
Copy the code
<a href='https://github.com' target='__blank'>link</a>
Copy the code
** New Windows created using BrowserWindow in the rendering process will not be caught by the new-window event **
const { BrowserWindow } = require('electron').remote
const win = new BrowserWindow()
win.loadURL('https://github.com')
Copy the code
EnableRemoteModule :true this method can also open a new window, but the main process’s new-window does not catch.
New-window new-window controls the creation of new Windows, and we can do a lot of things with that; Such as link verification, browser opening links and so on. The default browser opening link code is as follows:
import { shell } from 'electron'
function openExternal(url: string) {
const HTTP_REGEXP = /^https? : \ \ / / /
// Do not enable this function for non-HTTP protocols to prevent security problems caused by user-defined protocols
if(! HTTP_REGEXP) {return false
}
try {
await shell.openExternal(url, options)
return true
} catch (error) {
console.error('open external error: ', error)
return false}}// Create window listener
browserWindow.webContents.on('new-window'.(event, url, frameName, disposition) = > {
if (disposition === 'foreground-tab') {
// Block mouse clicks on links
event.preventDefault()
openExternal(url)
}
})
Copy the code
_ about __shell_ module, can view the website www.electronjs.org/docs/api/sh… _
1.3. Close the window
** Close **** event and ****closed** event Close events are fired before the window is about to close, but before the beforeUnload and unload events of the DOM.
// The window registers the close event
win.on('close'.(event) = >{
event.preventDefault() // Prevents the window from closing
})
Copy the code
The closed event is emitted after the window is closed, but the window is already closed and there is no way to prevent it from closing by event.preventDefault().
win.on('closed', handler)
Copy the code
There are many apis for the main process to close Windows, but each has its own pros and cons.
1.3.1. win.close()
About the pros and cons of this API
- If the current window instance is registered and blocked
close
Event, will not close the page, and willPreventing computer shutdown(you must manually force the exit); - Close the page’s services, for example
websocket
The next time you open a window, the page in the window willTo render; - Through this
API
The triggerclose
In the eventunload
和beforeunload
Trigger before, that’s how you do itTrigger popover when closing;
The full code is on Github:electron-playground
- Will be
closed
Event caught.
1.3.2. win.destroy()
- Force exit, ignore
close
Event (i.e., unable to passevent.preventDefault()
To stop); - Close the page and the services in the page. The next time you open the window, the page in the window will be re-rendered.
- Will be
closed
Event caught.
1.3.3. win.hide()
This hidden window.
- Hiding the window triggers
hide
andblur
Events can also pass throughevent.preventDefault()
To prevent the - Just hide the window and go through
win.show()
, can display the window, and will keep the original window, inside the service will not hang up
2. Hide and restore the main window
2.1. The main window
2.1.1. WhyThe main window?
An application has many Windows. One window is required as the main window. If the window is closed, the entire application is closed. Scenario: When the application has only one page, the user clicks the close button to hide the application instead of closing it. For example: other apps, such as wechat, QQ and other desktop end.
Using the window closing API mentioned above, we implement a main window hiding and restoring.
Modify the close event
let mainWindowId: number // Used to mark the main window ID
const browserWindow = new BrowserWindow()
// Record the main window ID
if(! mainWindowId) { mainWindowId = browserWindow.id } browserWindow.on('close'.event= > {
// If the main window is closed, block
if (browserWindow.id === mainWindowId) {
event.preventDefault()
browserWindow.hide()
}
})
Copy the code
2.1.2. Restore the main window display
If you can hide, you can recover.
const mainWindow = BrowserWindowsMap.get(mainWindowId)
if (mainWindow) {
mainWindow.restore()
mainWindow.show()
}
Copy the code
** mainwindow.show ()** method: shows Windows as the name suggests. _ Why is __mainwindow.restore ()_? _windows_ if __hide_ _ is not followed by __show_ method but only __restore_ method, the page will be hung and unusable
2.1.3. Forcibly close the main window
In some scenarios, a forcible exit may be required, with code attached:
const mainWindow = BrowserWindowsMap.get(mainWindowId)
if (mainWindow) {
mainWindowId = -1
mainWindow.close()
}
Copy the code
Existing problems
We changed the behavior of the Electron window, and there were many scenarios where there were problems
Problem one: because it stoppedclose
Event, result inTo turn it offCannot be closed whenThe main window, you can use the following code
app.on('before-quit'.() = > {
closeMainWindow()
})
Copy the code
During macOS
Linux
Windows
Either way.
Problem two: To avoid startupMultiple applications
app.on('second-instance'.() = > {
const mainWindow = BrowserWindowsMap.get(mainWindowId)
if (mainWindow) {
mainWindow.restore()
mainWindow.show()
}
})
Copy the code
During macOS
Linux
Windows
Can be under
Problem three: Start the application for the first time, try while the application is already running, or clickThe application 的 dock 或 Taskbar iconAnd reactivate it
app.on('activate'.() = > {
if (mainWindow) {
mainWindow.restore()
mainWindow.show()
}
})
Copy the code
Applies only to macOS
下
Problem four:Double-click on the tray iconOpen theAPP
tray.on('double-click'.() = > {
if (mainWindow) {
mainWindow.restore()
mainWindow.show()
}
})
Copy the code
In this way, the code for each link can be realized. The specific code can be seen in the link
3. Window focus and out of focus
3.1. The focus
3.1.1. Configuration when creating Windows
const { BrowserWindow } = require('electron');
const win = new BrowserWindow();
win.loadURL('https://github.com')
Copy the code
Focusable :false Focusable :false in Windows focusable:false also means skipTaskbar: true. Setting focusable: false in Linux stops the window from interacting with WM, and the window will always be at the top;
The following is discussed only in the focusable:true case
const { BrowserWindow } = require('electron');
const win = new BrowserWindow() // focusable:true is the default
Copy the code
Listing the apis
3.1.2. Apis for focusing
API | function |
---|---|
BrowserWindow.getFocusedWindow() |
To get the focused window |
win.isFocused() |
Determines whether the window is focused |
win.on('focus',handler) |
To monitor whether the window is focused |
win.focus() |
Manual focus window |
3.1.3. Other API side effects related to focus:
API | function |
---|---|
win.show() |
Displays the window and focuses on the window |
win.showInactive() |
Displays the window, but does not focus on the window |
3.2 out of focus
3.2.1. About out of focusapi
API | function |
---|---|
win.blur() |
Unfocus window |
win.on('blur',cb) |
Listening out of focus |
3.2.2. Otherapi
Side effects related to loss of focus:
api | function |
---|---|
win.hide() |
The window is hidden and an out-of-focus event is triggered |
4. Window type
4.1. Bezel-less Windows
4.4.1. Describe
A frameless window is a window without a shell (including the window border, toolbar, etc.) that only contains the content of the web page
4.1.2. Achieve
Windows
macOS
Linux
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ width: 800.height: 600.frame: false })
win.show()
Copy the code
Under macOS, there are different implementations, official documentation
4.1.3. macOS
Under the unique no border
- configuration
titleBarStyle: 'hidden'
Returns a full-size content window with a hidden title bar, still with the standard window control button in the upper left corner (commonly known as “traffic light”)
// Create a window with no borders
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hidden' })
win.show()
Copy the code
The effect is as follows:
- configuration
titleBarStyle: 'hiddenInset'
Returns a different type of window with a hidden title bar, with a larger distance between the control button and the window border.
// Create a window with no borders
const { BrowserWindow } = require('electron')
let win = new BrowserWindow({ titleBarStyle: 'hiddenInset' })
win.show()
Copy the code
The effect is as follows:
configurationtitleBarStyle: 'customButtonsOnHover'
The effect is as follows:
4.1.4. The problem that the top of the window cannot be dragged
Although no border window, very beautiful, you can customize the title; But changing the default behavior at the top of the Electron window requires code that is compatible with it and does what it originally does.
This happens because, by default, bezel-less Windows are not draggable. The application needs to be inCSS
Specified in the-webkit-app-region: drag
To tell theElectron
Which areas are draggable (such as the standard title bar of the operating system) to use inside the draggable areas-webkit-app-region: no-drag
Some areas can be excluded. Note that currently only rectangular shapes are supported.Complete documentation
Drag is implemented using -webkit-app-region: drag, but invalidates the internal click event. Set the click element to -webkit-app-region: no-drag. Specific details Electron issues
In order not to affect the business code within the window, here drag-and-drop code should be triggered in Preload.
The preload code runs before the window code runs
Core code:
// Insert a removable DOM at the top
function initTopDrag() {
const topDiv = document.createElement('div') // Create a node
topDiv.style.position = 'fixed' // Always at the top
topDiv.style.top = '0'
topDiv.style.left = '0'
topDiv.style.height = '20px' // Drag only at the top 20px
topDiv.style.width = '100%' / / width is 100%
topDiv.style.zIndex = '9999' // Float in the outermost layer
topDiv.style.pointerEvents = 'none' // For click penetration
// @ts-ignore
topDiv.style['-webkit-user-select'] = 'none' // Disable text selection
// @ts-ignore
topDiv.style['-webkit-app-region'] = 'drag' / / drag
document.body.appendChild(topDiv) // Add a node
}
window.addEventListener('DOMContentLoaded'.function onDOMContentLoaded() {
initTopDrag()
})
Copy the code
Just reference Preload when creating the window
const path = require('path')
const { BrowserWindow } = require('electron')
const BaseWebPreferences = {
nodeIntegration: true.preload: path.resolve(__dirname, './windowType.js'), // The preload.js path is referenced here
}
// Main window code
const win = new BrowserWindow({ webPreferences: BaseWebPreferences, frame: false.titleBarStyle: 'hiddenInset' })
win.loadURL('https://github.com')
Copy the code
Can achieve window top drag_tips: If the window is open __devtools_
Windows can also be dragged, but the drag experience is not good
4.2. Parent-child window
The so-called father and son window, that is, the child window is always above the father window, as long as the child window exists, even if the location is not above the father window, it is unable to operate the father window
const { BrowserWindow } = require('electron')
let top = new BrowserWindow()
let child = new BrowserWindow({ parent: top })
child.show()
top.show()
Copy the code
The communication between parent and child Windows is introduced in the section of communication between Windows. Get the class BrowserWindowProxy of the parent window through getParentWindow, and communicate through Win. postMessage(Message,targetOrigin)
4.3. Modal window
Modal window is also a parent window, but the presentation will be different
const { BrowserWindow } = require('electron')
let top = new BrowserWindow()
let child = new BrowserWindow({ parent: top, modal: true.show: false })
child.loadURL('https://github.com')
child.once('ready-to-show'.() = > {
child.show()
})
Copy the code
5. Communication between Windows
Implementing window communication must not affect the injection of business code, JDK, and so on within the window
5.1. Main process intervention mode
The main process can interfere with the renderer process to create a new window, as long as webContents listens on new-window while the window is being created
import path from 'path'
import { PRELOAD_FILE } from 'app/config'
import { browserWindow } from 'electron';
const BaseWebPreferences: Electron.BrowserWindowConstructorOptions['webPreferences'] = {
nodeIntegration: true.webSecurity: false.preload: path.resolve(__dirname, PRELOAD_FILE),
}
// Create window listener
browserWindow.webContents.on('new-window'.(event, url, frameName, disposition) = > {
event.preventDefault()
// Create a window through BrowserWindow
const win = new BrowserWindow({
show:false.webPreferences: {
...BaseWebPreferences,
additionalArguments: [`--parentWindow=${browserWindow.id}`] // Pass the id of the parent window}}); win.loadURl(url); win.once('ready-to-show'.() = >{
win.show()
})
})
Copy the code
Argv is an array of strings that can be parsed using yargs
Preload. Js code
import { argv } from 'yargs'
console.log(argv);
Copy the code
Got to the parent windowid
, encapsulate the communication code and mount it towindow
上
/** * this is preload for the window communication example, * preload execution order before the window JS execution order */
import { ipcRenderer, remote } from 'electron'
const { argv } = require('yargs')
const { BrowserWindow } = remote
// The parent window listens for child window events
ipcRenderer.on('communication-to-parent'.(event, msg) = > {
alert(msg)
})
const { parentWindowId } = argv
if(parentWindowId ! = ='undefined') {
const parentWindow = BrowserWindow.fromId(parentWindowId as number)
// Mount to window
// @ts-ignore
window.send = (params: any) = > {
parentWindow.webContents.send('communication-to-parent', params)
}
}
Copy the code
Try it:This method can achieve communication, but it is too cumbersome.
5.2. Parent-child window communication
The parent window id is not passed by additionalArguments, and the parent window is called by window.parent in the child window
browserWindow.webContents.on('new-window'.(event, url, frameName, disposition) = > {
event.preventDefault()
// Create a window through BrowserWindow
const win = new BrowserWindow({
show:false.webPreferences:BaseWebPreferences,
parent:browserWindow // Add parent window
});
win.loadURl(url);
win.once('ready-to-show'.() = >{
win.show()
})
})
Copy the code
Disadvantage: Child window is always above parent window.
const path = require('path')
const { BrowserWindow } = require('electron')
const BaseWebPreferences = {
// // Integrated node
nodeIntegration: true.// // Disable the same-origin policy
// webSecurity: false,
// Preload scripts via absolute address injection
preload: path.resolve(__dirname, './communication2.js'),}// Main window code
const parent = new BrowserWindow({ webPreferences: BaseWebPreferences, left: 100.top: 0 })
parent.loadURL(
'file:///' + path.resolve(__dirname, '.. /playground/index.html#/demo/communication-part2/main'),
)
parent.webContents.on('new-window'.(event, url, frameName, disposition) = > {
// Block the default event
event.preventDefault()
// Create a window through BrowserWindow
// Child window code
const son = new BrowserWindow({
webPreferences: BaseWebPreferences,
parent,
width: 400.height: 400.alwaysOnTop: false,})// son.webContents.openDevTools();
son.loadURL(
'file:///' +
path.resolve(__dirname, '.. /playground/index.html#/demo/communication-part2/client'),)})Copy the code
preload.js
import { remote, ipcRenderer } from 'electron'
// The parent window listens for child window events
ipcRenderer.on('communication-to-parent'.(event, msg) = > {
alert(msg)
})
const parentWindow = remote.getCurrentWindow().getParentWindow()
// @ts-ignore
window.sendToParent = (params: any) = >
parentWindow.webContents.send('communication-to-parent', params)
Copy the code
But it has to be a father-child window.
5.3. Usewindow.open
The ultimate way to
On the Web side, window.open returns a windowObjectReference, which implements postMessage. But at the Electron end, the window.open method is redefined; Creating a new window using window.open returns a BrowserWindowProxy object and provides a child window with limited functionality. MDN document Electron document
const BrowserWindowProxy = window.open('https://github.com'.'_blank'.'nodeIntegration=no')
BrowserWindowProxy.postMessage(message, targetOrigin)
Copy the code
Windows. Open can be configured using options in BrowserWindow(Options).
6. Full screen, maximize, minimize, restore
6.1. The full screen
6.1.1. Go to full screen during creation
Configure new BrowserWindow({fullscreen:true})
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ fullscreen:true.fullscreenable:true })
win.loadURL('https://github.com')
Copy the code
Use 6.1.2.API
Enter the full screen
Ensure that the current window is fullscreEnable :true for the following API to be used
win.setFullScreen(flag)
, set the full-screen state;win.setSimpleFullScreen(flag)
.macOS
Set up a simple full screen.
6.1.3. Obtaining the full-screen state
win.fullScreen
, to determine whether the current window is full-screen;win.isFullScreen()
.macOS
Unique;win.isSimpleFullScreen()
.macOS
Unique.
6.1.4. Monitoring of full-screen events
rezise
Trigger after adjusting window size;enter-full-screen
Trigger when the window enters full-screen state;leave-full-screen
Triggered when the window leaves the full-screen state;enter-html-full-screen
Triggered when the window enters the full-screen state triggered by the HTML API;leave-html-full-screen
Triggered when the window leaves the full-screen state triggered by the HTML API.
6.1.5. HTML
API
The window cannot be connected
const path = require('path')
const { BrowserWindow } = require('electron')
const BaseWebPreferences = {
nodeIntegration: true.preload: path.resolve(__dirname, './fullScreen.js'),};const win = new BrowserWindow({ webPreferences: BaseWebPreferences })
win.loadURL('file:///' + path.resolve(__dirname, '.. /playground/index.html#/demo/full-screen'))
Copy the code
It is possible to use the button to full screen and exit the full screen, but it is not possible to click 🚥 in the upper left corner and exit the full screen using the button. There’s no way to know if you’re in full screen or not.
The solution is to mount the win.setFullScreen(flag) method to the window and load the preload.js code
import { remote } from 'electron'
const setFullScreen = remote.getCurrentWindow().setFullScreen
const isFullScreen = remote.getCurrentWindow().isFullScreen
window.setFullScreen = setFullScreen
window.isFullScreen = isFullScreen
Copy the code
https://www.electronjs.org/docs/api/browser-window#winsetfullscreenflag isFullScreen _ setFullScreen document Document _https: / / www.electronjs.org/docs/api/browser-window#winisfullscreen
6.2. Maximize and minimize
6.2.1. Create a window configuration
Full API documentation
const { BrowserWindow } = require('electron')
const win = new BrowserWindow({ minWidth:300.minHeight:300.maxWidth:500.maxHeight:500.width:600.height:600 })
win.loadURL('https://github.com')
Copy the code
When usingminWidth/maxWidth/minHeight/maxHeight
When the minimum or maximum window size is set, it only limits the user. It does not prevent you from passing values that do not conform to size limits tosetBounds/setSize
或 BrowserWindow
Constructor of.
6.2.2. Related events
The name of the event | The trigger condition |
---|---|
maximize |
Triggered when the window is maximized |
unmaximize |
Triggered when a window exits from the maximized state |
minimize |
Triggered when a window is minimized |
restore |
Triggered when a window resumes from its minimized state |
6.2.3. Related status API
win.minimizable
Whether the window can be minimizedwin.maximizable
Whether the window can be maximizedwin.isMaximized()
Maximizationwin.isMinimized()
Minimized or not
6.2.4. Control API
win.maximize()
Maximize the windowwin.unmaximize()
Maximization of exitwin.minimize()
Minimize the windowwin.unminimize()
Exit minimization
6.3. The window is restored
Win.restore () restores the window from its minimized state to its previous state. The main window hiding and recovery in the previous example also applies to this API
7. Triggering sequence of window events
7.1. Window loading
WebContents: webContents object in BrowserWindow instance webPreferences: The webPreferences configuration object for Options in New BrowserWindow(Options)
Work from top to bottom
The environment | The event | trigger |
---|---|---|
The preload webPreferences | – | Preload the specified script before the page runs other scripts. This script has access to all Node API scripts regardless of whether the page is integrated with Node. The script path is the absolute path of the file. |
webContents | did-start-loading |
This event is emitted when the spinner in the TAB starts to spin |
webContents | did-start-navigation |
This event is emitted when the window starts navigation |
In the windowJavaScript |
DOMContentLoaded |
The initial HTML document is fully loaded and parsed |
In the windowJavaScript |
load |
When all page resources have been loaded |
BrowserWindow The instance |
show |
Window display when triggered |
webContents |
did-frame-navigate |
frame End of navigation |
webContents |
did-navigate |
main frame End of navigation |
BrowserWindow The instance |
page-title-updated |
Triggered when the title of a document changes |
webContents |
page-title-updated |
Triggered when the title of a document changes |
webContents |
dom-ready |
This event is triggered when text in a frame has finished loading |
webContents |
did-frame-finish-load |
Triggered when the frame completes navigation |
webContents |
did-finish-load |
Triggered when navigation is complete, that is, the TAB’s spinner will stop spinning |
webContents |
did-stop-loading |
This event is emitted when the spinner in the TAB ends its rotation |
7.2. User triggers event after loading window (excluding REsize and Move)
The event | role |
---|---|
page-title-updated |
Triggered when the title of a document changes |
blur |
Triggered when a window loses focus |
focus |
Triggered when the window gets focus |
hide |
Window hidden |
show |
Window display |
maximize |
Triggered when the window is maximized (MAC is double click title) |
unmaximize |
Triggered when a window exits from the maximized state |
enter-full-screen |
Triggered when the window enters the full-screen state |
leave-full-screen |
Triggered when the window leaves the full screen state |
enter-html-full-screen |
Triggered when the window enters the full-screen state triggered by the HTML API |
leave-html-full-screen |
Triggered when the window leaves the full-screen state triggered by the HTML API |
always-on-top-changed |
The Set or unset window always fires when the other window is displayed at the top. |
app-command |
window linux unique |
7.3. User movement window
- Before moving the window
will-move
; - Moving window
move
; - After moving
moved
;
7.4. The user changes the window size
- Before the change
will-resize
; - After the change
resize
7.5. Window content exception Events (webContent
Events)
The event name | Wrong type |
---|---|
unresponsive |
Triggered when the page becomes unresponsive |
responsive |
Triggered when an unresponded page becomes responsive |
did-fail-load |
Load failed,Error code |
did-fail-provisional-load |
During page loading, executewindow.stop() |
did-frame-finish-load |
|
crashed |
Triggered when the renderer process crashes or is terminated |
render-process-gone |
Emitted when the render process fails unexpectedly |
plugin-crashed |
Triggered when a plug-in process crashes |
certificate-error |
The link validation of the certificate failed |
preload-error |
preload.js Throw an error |
7.6. Window Closure (including unexpected closure)
- Before closing: triggers registration in the main process
close
The event - Within the window
JavaScript
performwindow.onbeforeunload
- Within the window
JavaScript
performwindow.onunload
- After closing: triggers registration in the main process
closed
The event
In order to learn electron better, we have created a series at present, if you are interested, you can have a look
- [Electron playground series] Menu
- 【Electron Playground series 】Dialog with file selection
- [Electron playground series] Agreement
- 【Electron Playground series 】 The tray
- How long does it take to write a screen recording tool?
For more complete documentation, please refer to the documentation below
Electron-Playground official document
Github address portal: github.com/tal-tech/el…