DEV Community

Tomasz Wegrzanowski
Tomasz Wegrzanowski

Posted on

Electron Adventures: Episode 89: Remembering Document Preferences

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()
})
Enter fullscreen mode Exit fullscreen mode

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}`)
}
Enter fullscreen mode Exit fullscreen mode

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:

Episode 89 Screenshot

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.

Discussion (0)