-
Notifications
You must be signed in to change notification settings - Fork 9
rfc: Save/Restore Window-State API #16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I typed up some comments while reviewing last week, but forgot to hit submit 🤦♂️
- A window can never be restored out of reach. I am presuming no apps would want this behavior. `fallbackBehaviour` will take effect if the window is entirely off screen. We are still providing the option to relaunch in a minimized state. | ||
- If window (width, height) reopens on a different display and does not fit on screen auto adjust to fit and resave the value (even if allowOverflow=true). This would reduce the number of edge cases significantly and I highly doubt that any app would want to preserve an overflow when opened on a different display. | ||
- Not handling scaling as Windows, macOS, and Linux support multimonitor window scaling by default. | ||
- Not handling other `displayModes` such as split screen because it's innapropriate I believe. We can restore back to the same bounds in 'normal' mode. I've excluded 'normal' from the options for `displayMode` as it is the default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would this also include things like "Window Snapping"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Snapping a window does not make the window enter any special displayMode on windows and linux. We can handle it as 'normal' state only. I've updated the RFC to reflect the same.
On macOS the 'tile window to the left/right of the screen' makes it enter fullscreen mode officially. We can treat it as a fullscreen state. If you think that's inappropriate we can restore it in a normal state to where it was before the split screen. This is something that needs to be finalized.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@electron/api-wg
|
||
#### Developer-Facing Events | ||
|
||
I’m also considering emitting events such as: `will-save-window-state`, `saved-window-state`, `will-restore-window-state`, `restored-window-state`. However, I’m unsure about their practical usefulness for developers and hope to finalize them through this RFC. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@electron/api-wg any thoughts on these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@electron/api-wg any thoughts on these?
If these events allow for intercepting and overriding the state save, I can see them being used to redirect the new state to other places. The same applies to reading the window state, as it could be read or loaded from a different source.
For example, in my comment on electron/electron#47425 (comment) (4th paragraph), I am thinking of saving the users' window states in the cloud and attempting to read them from there on window initialization. This way, their preferred window position would persist even across different machines and instances of the application executable. If these events allow for that use case, they would be very welcome.
Edit: If this is not possible, maybe it could be done by providing a loader and saver handler to windowStateRestoreOptions
but if this is not planned to be supported as of now, I suppose it would complicate the API and implementation a lot. Flexibility is always good, but it may not be worth pursuing this at this stage of this RFC.
Edit 2: The option in my first edit seems very cumbersome. Maybe the will-save-window-state
and saved-window-state
can pass the data to be saved as a parameter to their handler? And also allow for intercepting and cancelling the save in the default format. Then, maybe the will-restore-window-state
could restore the window saved with custom data if the event handler returns such custom data, while also allowing for the cancellation of the default behaviour? I imagine I could integrate cloud persistence of the window state on a per-user basis if these features were available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: If this is not possible, maybe it could be done by providing a loader and saver handler to windowStateRestoreOptions but if this is not planned to be supported as of now, I suppose it would complicate the API and implementation a lot. Flexibility is always good, but it may not be worth pursuing this at this stage of this RFC.
The issue with this is that const win = new BrowserWindow()
is synchronous. Custom loader functions could introduce async operations during window construction. Also, Custom loader/saver handlers would completely defeat the purpose of having a declarative API.
Edit 2: The option in my first edit seems very cumbersome. Maybe the will-save-window-state and saved-window-state can pass the data to be saved as a parameter to their handler? And also allow for intercepting and cancelling the save in the default format. Then, maybe the will-restore-window-state could restore the window saved with custom data if the event handler returns such custom data, while also allowing for the cancellation of the default behaviour? I imagine I could integrate cloud persistence of the window state on a per-user basis if these features were available.
On second thought there isn't much utility for these events beyond just telemetry. Adding these events with the ability to intercept and modify saved values, add custom restoration logic, use a custom format would unnecessarily increase the implementation complexity and maintainability. Performance implications of running custom logic for hundreds of events per second during window manipulation needs to be considered.
We can definitely reconsider this upon further discussion. Thanks for the suggestion.
Let me know if you can think of any use case that cannot be accomplished via app.setWindowState()
app.getWindowState()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For your cloud sync use case, a simpler approach might be for electron to provide an API like app.setWindowState([stateId], [stateObj])
that allows you to synchronously modify saved states before new BrowserWindow()
initialization.
This would enable you to leverage electron's restoration logic for all the edge cases (display changes, bounds validation, etc.) rather than requiring you to reimplement that complexity yourself.
For saving to the cloud you could use app.getWindowState([stateId])
at your leisure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the API should be kept as simple as possible, while allowing versatility.
For your cloud sync use case, a simpler approach might be for electron to provide an API like
app.setWindowState([stateId], [stateObj])
that allows you to synchronously modify saved states beforenew BrowserWindow()
initialization.
Would app.setWindowState([stateId], [stateObj])
overwrite the locally saved window states for the specific stateId
s provided? This would make it possible to support my use case, because I could pull the latest state from the cloud and update it locally upon each app launch.
For saving to the cloud you could use
app.getWindowState([stateId])
at your leisure.
I would suggest having a separate BrowserWindow.getState()
or BrowserWindow.getCurrentState()
that avoids reading the window state from the disk and returns the BrowserWindow
's current state computed directly from memory. This would avoid a file read if I understand correctly, right?
Furthermore, this API would make the events you suggest very useful for my cloud use case. It would enable getting the new window state via BrowserWindow.getState()
and pushing it to the cloud only when will-save-window-state
fires (provided that BrowserWindow.getState()
would return the same state that will be saved when will-save-window-state
fires).
Having will-save-window-state
would allow for 1) intercepting the new state even if, for any reason, the file write fails and 2) avoid adding handlers to the resize
and move
events, which fire very often when the window is moving and need manual debouncing to avoid performance issues (I've faced this in my JS soultion, handling it using a setTimeout
as in electron/electron#47425 (comment)).
While the events would mainly serve for telemetry purposes, I suppose other developers who want to do quirky things like my cloud use case will find them useful in other contexts. I'd suggest keeping them unless they add complexity that is a maintenance burden.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest having a separate
BrowserWindow.getState()
orBrowserWindow.getCurrentState()
that avoids reading the window state from the disk and returns theBrowserWindow
's current state computed directly from memory. This would avoid a file read if I understand correctly, right?
Yes, it would avoid a file read. BrowserWindow.getState()
or BrowserWindow.getCurrentState()
are planned but not finalized yet.
While the events would mainly serve for telemetry purposes, I suppose other developers who want to do quirky things like my cloud use case will find them useful in other contexts. I'd suggest keeping them unless they add complexity that is a maintenance burden.
Implementation complexity and maintenance for these events should be fine. I've added them to the rfc.
- A window can never be restored out of reach. I am presuming no apps would want this behavior. `fallbackBehaviour` will take effect if the window is entirely off screen. We are still providing the option to relaunch in a minimized state. | ||
- If window (width, height) reopens on a different display and does not fit on screen auto adjust to fit and resave the value (even if allowOverflow=true). This would reduce the number of edge cases significantly and I highly doubt that any app would want to preserve an overflow when opened on a different display. | ||
- Not handling scaling as Windows, macOS, and Linux support multimonitor window scaling by default. | ||
- Not handling other `displayModes` such as split screen because it's innapropriate I believe. We can restore back to the same bounds in 'normal' mode. I've excluded 'normal' from the options for `displayMode` as it is the default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Snapping a window does not make the window enter any special displayMode on windows and linux. We can handle it as 'normal' state only. I've updated the RFC to reflect the same.
On macOS the 'tile window to the left/right of the screen' makes it enter fullscreen mode officially. We can treat it as a fullscreen state. If you think that's inappropriate we can restore it in a normal state to where it was before the split screen. This is something that needs to be finalized.
- A window can never be restored out of reach. I am presuming no apps would want this behavior. `fallbackBehaviour` will take effect if the window is entirely off screen. We are still providing the option to relaunch in a minimized state. | ||
- If window (width, height) reopens on a different display and does not fit on screen auto adjust to fit and resave the value (even if allowOverflow=true). This would reduce the number of edge cases significantly and I highly doubt that any app would want to preserve an overflow when opened on a different display. | ||
- Not handling scaling as Windows, macOS, and Linux support multimonitor window scaling by default. | ||
- Not handling other `displayModes` such as split screen because it's innapropriate I believe. We can restore back to the same bounds in 'normal' mode. I've excluded 'normal' from the options for `displayMode` as it is the default. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@electron/api-wg
I'm not trying to be disruptive here, but after giving my proposed cloud sync use case some thought, I would like to put forward a few ideas, based on my opinions of how such a use case (and other potential non-standard use cases) could be supported most cleanly. Would it be possible for Alternatively, the window state loading could be fully decoupled into a dedicated API ( To complete this flow, two instance-level events could be added. EDIT: This new What I try to achieve with these proposals is the ability to pass the window state to a new window and to get it for external saving, when it is updated, by completely bypassing the local JSON file persistence functionality. The current way the API is proposed would not allow for that and would require additional code to clean up window states periodically from disk. I'm eager to hear your feedback and opinions. |
This can be considered as an alternative to
To skip local persistence developers can skip passing the
This can be moved to future possibilities. |
But I suppose
While this is true, setting the window position manually with state from an external source would bypass the proposed repositioning algorithm that avoids overflow and excessive clipping.
Yes, but is If state persistence is disabled (by not passing the If there were a way to add a callback to the moment the window would normally persist the state (even when it does not do it), it could be intercepted by a user function and processed further (i.e., sent to the cloud). |
I just noticed that All that is left is the ability to have the state passed to/used by the window be read from any source rather than the local disk, thus my initial suggestion to allow passing the state directly to the window, avoiding I feel like I'm disrupting your work with my specific, non-standard use case, and I don't want to impede the development of this feature. As such, I will sum up my final ideas as concisely as possible here:
With just these 2 features, non-standard use cases such as mine could be supported while avoiding file reads and writes completely. For example, my cloud sync use case could fetch the window state from the cloud, adjust it using I hope you don't mind my comments, I'm just trying to help make this feature as flexible as possible, the best I can. |
// Clear the entire saved state for the window | ||
const success = BaseWindow.clearState('#1230'); | ||
console.log(success) // true if state was successfully cleared | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add clarification around when this might not be successful?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe better to be clearWindowStatePersistence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey guys, this API needs to be updated to reflect the current implementation here https://github.com/electron/electron/pull/47781/files#diff-f9f17ef8ca27777eaf90d9632c1e359effa0a710cec6bbbab55ddbee1f217a31R390 for reference
Does this sound reasonable to you guys? The API is actually called BaseWindow.clearWindowState(windowName)
and it logs some warnings if window state isn't cleared.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
state
is an ambiguous term so I think it'd be best to be specific. I agree with @reitowo's suggestion to include some variant of "persistence" in the name so it's clear what kind of state this is related to.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure! will update this to BaseWindow.clearWindowStatePersistence(name)
👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@samuelmaddock @reitowo do you guys think BaseWindow.clearSavedState(name)
sounds better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks better, I also suggest clearPersistedState
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll also have to rename the restored-window-state
event to match, so either restored-persisted-state
or restored-saved-state
.
Note: There are some other proposed events with save
in them in this RFC, such as saved-window-state
, will-save-window-state
that might need a more consistent name if we end up going with clearPersistedState
|
||
### BaseWindowConstructorOptions | ||
|
||
* `name` string (optional) - A unique identifier for the window, used internally by Electron to enable features such as state persistence. Each window must have a distinct name. It can only be reused after the corresponding window has been destroyed. An error is thrown if the name is already in use. This is not the visible title shown to users on the title bar. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing this out @reitowo! name
doesn't imply uniqueness so uniqueName
seems like the best candidate for now (as suggested by @itsananderson)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Linking previous discussions here so we have everything in one place.
https://github.com/electron/electron/pull/47764/files#r2260998113
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I still think name is not the best option, since similar names are in electron but without uniqueness
![]()
Should we leave it up to the developers to not confuse Electron.BrowserWindow.name
to be Electron.WebFrameMain.name
or something similar, despite the description for the property being well documented?
I'm also presuming the description will show up in most IDEs the same way it shows up in your example image, so that should help with the confusion.
34e79ad
to
58c6001
Compare
Co-authored-by: Erick Zhao <erick@hotmail.ca>
Co-authored-by: Erick Zhao <erick@hotmail.ca>
Note
This RFC is part of GSoC 2025
This proposal aims to implement a save/restore window state API for Electron by providing a simple but powerful configuration object
windowStatePersistence
that handles complex edge cases automatically. This approach offers a declarative way to configure window persistence behavior while maintaining flexibility for different application needs. The object would be optional in theBaseWindowConstructorOptions
.Rendered doc: https://github.com/nilayarya/rfcs/blob/main/text/0016-save-restore-window-state.md
Related: electron/electron#526