Electron Adventures: Episode 89: Remembering Document Preferences
Tomasz Wegrzanowski
Posted on November 7, 2021
Back in episode 86 we made our app remember size and position of its window. We want to do something similar now, except the app has multiple windows, so we need to remember one for each document.
We won't do anything complicated, the remembering will be based on document's full path.
So let's npm install electron-settings
and get to coding!
All the code we'll do will be just index.js
on the backend, we won't be changing anything in the frontend code.
Start the app
When we start the app, we show a file dialog to select one or more CSV files:
let { app, BrowserWindow, dialog } = require("electron")
async function startApp() {
let { canceled, filePaths } = await dialog.showOpenDialog({
properties: ["openFile", "multiSelections", "showHiddenFiles"],
filters: [
{ name: "CSV files", extensions: ["csv"] },
{ name: "All Files", extensions: ["*"] }
],
message: "Select a CSV file to open",
defaultPath: `${__dirname}/samples`,
})
if (canceled) {
app.quit()
}
for (let path of filePaths) {
createWindow(path)
}
}
app.on("ready", startApp)
app.on("window-all-closed", () => {
app.quit()
})
Once we select any number of CSV files, we call createWindow(path)
for each to create its window.
Creating windows
And then we need to create a window with given document:
let settings = require("electron-settings")
function createWindow(path) {
let key = `windowState-${path}`
let windowState = settings.getSync(key) || { width: 1024, height: 768 }
let qs = new URLSearchParams({ path }).toString();
let win = new BrowserWindow({
...windowState,
webPreferences: {
preload: `${__dirname}/preload.js`,
},
})
function saveSettings() {
windowState = win.getBounds()
console.log("SAVING", path, windowState)
settings.setSync(key, windowState)
}
win.on("resize", saveSettings)
win.on("move", saveSettings)
win.on("close", saveSettings)
win.loadURL(`http://localhost:5000/?${qs}`)
}
When we open a window, we check in saved preferences if we have anything matching its document path. If we do, we use it. Otherwise, we use default window size, and let the OS place it whenever it wants.
Whenever a window is moved or resized, we track its position and size, and save it to settings with the right key.
Limitations
Electron has backend and frontend parts, but the way responsibility is split between them is not based on any logical considerations, it's just a side effect of how regular browsers do things.
- frontend (renderer) manages everything about state of each window - and that's fine
- backend (main) process manages size and position of each window - and that's really weird and awkward
So what we did is create backend-side system, which remembers window positions and sizes for each document. But we'd need to create a whole separate system to remember anything about state of each window, such as how far each window was scrolled, or (if we implemented this), sort order for various columns and so on.
This isn't all that difficult, but Electron pretty much forces us to architect the app poorly:
- we can have two completely separate systems
- we could have backend-managed system, which would tell frontend what to do when it starts, and get messages from frontend about app state changes
- we could have frontend-managed system, which would tell backend where to reposition window when it starts (it could result in window briefly being in wrong place unless we're careful), and then get messages from the backend about window position and size changes
None of these designs are great.
Results
Here's the results, remembering resizing for every document:
In the next episode, we'll add some more OS integrations to our CSV viewer.
As usual, all the code for the episode is here.
Posted on November 7, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.