Feature: Remember the window's size, position and maximized state - #684
Open
Almighty-Shogun wants to merge 1 commit into
Open
Feature: Remember the window's size, position and maximized state#684Almighty-Shogun wants to merge 1 commit into
Almighty-Shogun wants to merge 1 commit into
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds persistence for the main window's size, position and maximized state. The window was created at a hard-coded 1400x900 on every launch and nothing ever read or wrote its geometry, so anyone who works maximized re-maximized it every single time they opened the app. Electron persists none of this on its own. It surfaced on Fedora but was never platform-specific — macOS and Windows forgot it too.
State lands in
<userData>/window-state.jsonthroughrenameAtomicSyncwith a per-call unique temp, like every other store.main/window-state.tsimports no electron, in thekeydown-intercept.tsshape: pure decisions over plain rectangles, a structural window for the tracker, andscreen.getAllDisplays()called at the seam inindex.ts. That was the point of the split — the interesting behaviour here only happens on hardware (a second monitor, a Mac in fullscreen), and this way it can be pressed by a test instead. What gets passed in is the displays' work areas, not their raw bounds: a saved position has to be judged against the space a window can actually occupy.The refusals are the feature, because each is a way the naive version is worse than the fixed size it replaces.
getNormalBounds(), nevergetBounds()— while maximized the latter returns the maximized rectangle, so saving it makes the next un-maximize hand back a screen-sized window: state that looks right and behaves wrong, and only for the users who maximize. An unreachable position is dropped, not clamped — a laptop undocked from the monitor its window was on would otherwise reopen the app off-screen, running and focusable from the dock but visible nowhere, with no gesture that rescues it; reachability is a real overlap with some work area, judged against the clamped size, and dropping keeps the user's size while letting the platform place a window it knows how to place. No capture while minimized or fullscreen —isMaximized()is false while a macOS window is fullscreen, so capturing there recordsmaximized: falseand erases exactly the preference this exists to remember; the last non-fullscreen state stands, which also means the app never reopens into fullscreen, deliberately, since that mode is usually temporary and much harder to escape on first launch. Maximizing happens before the first paint while the window is stillshow: false, saves are debounced through a drag and flushed synchronously onclose(an awaited write there races the process exit), every field is re-validated as a number on read because the file is hand-editable and its values reach theBrowserWindowconstructor before there is a window in which to report a failure, andNT_MULTIis excluded so a dev sandbox sharing the real userData cannot move the window of the app being developed.Alternatives I rejected: clamping an off-screen position into the nearest display, which invents a corner the user never chose and is indistinguishable from a bug when it happens; and restoring fullscreen alongside maximized, which is trivial to add and unpleasant to land in on a first launch.
Surfaces — Desktop: the whole feature. Server Edition: not applicable, a browser tab's geometry belongs to the browser, which is why none of this is in
src/core. Mobile: not applicable, nodeterm mobile has no window concept; nothing for @eneskirca to carry over.Verified:
npm run typecheck, and thesrc/main+src/sharedsuites plus the atomic-write guard (2525 passing). 37 unit tests cover the module. Following the mutation habit in CONTRIBUTING.md I inverted twelve of the guards above; three survived the first version of the suite — a fixture that could not tell the clamped size from the saved one, a timer fake that hid a missing debounce cancel, and no case for an overflowing exponent (1e999parses toInfinity, sotypeof === 'number'only looks sufficient). All twelve are killed now.Not verified: I tested this by hand only on Fedora 44 / GNOME, where size and maximized restore correctly across quit and relaunch. The macOS fullscreen path, the Windows path, and multi-monitor undocking are covered by unit tests against synthetic work areas and by nothing else. One known limit: a native-Wayland client cannot set its own position, so x/y is honoured under XWayland and ignored otherwise — size and maximized restore either way, which is what the drop-don't-clamp rule already degrades to.