Skip to content

Commit f4d3e09

Browse files
committed
fixed save and restore last window position
1 parent a44545e commit f4d3e09

3 files changed

Lines changed: 117 additions & 127 deletions

File tree

apps/electron/main.ts

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,36 @@ import { loadConfig, writeConfig } from './config'
55
import { windowState } from './window-state'
66

77
let win: BrowserWindow = null
8-
const args = process.argv.slice(1),
8+
const args = process.argv.slice(1)
9+
const serve = args.some((val) => val === '--serve')
910

10-
serve = args.some((val) => val === '--serve')
11-
12-
// const config = loadConfig()
13-
// const winState = windowState({
14-
// defaultWidth: 800,
15-
// defaultHeight: 600,
16-
// load: () => config.window,
17-
// save: (state) => {
18-
// config.window = state
19-
// writeConfig(config)
20-
// }
21-
// })
11+
const config = loadConfig()
12+
const winState = windowState({
13+
load: () => config.window,
14+
save: (state) => {
15+
config.window = state
16+
writeConfig(config)
17+
}
18+
})
2219

2320
function createWindow(): BrowserWindow {
24-
// Create the browser window.
2521
win = new BrowserWindow({
26-
x: 0,
27-
y: 0,
28-
width: 800,
29-
height: 600,
22+
x: config?.window?.x,
23+
y: config?.window?.y,
24+
width: config?.window?.width,
25+
height: config?.window?.height,
26+
resizable: true,
3027
autoHideMenuBar: true,
3128
webPreferences: {
3229
nodeIntegration: true,
3330
allowRunningInsecureContent: serve ? true : false,
34-
contextIsolation: false, // false if you want to run e2e test with Spectron
31+
contextIsolation: false,
3532
},
3633
frame: false,
3734
})
38-
// app.whenReady().then(() => {
39-
// winState.manage(win)
40-
// })
41-
35+
app.whenReady().then(() => {
36+
winState.manage(win)
37+
})
4238

4339
win.webContents.on('new-window', function (e, url) {
4440
e.preventDefault()

apps/electron/window-state.ts

Lines changed: 97 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -10,103 +10,111 @@ export interface WindowState {
1010
displayBounds: Electron.Rectangle
1111
}
1212

13+
function isWithinBounds(bounds: Electron.Rectangle, state: Partial<Electron.Rectangle>): boolean {
14+
return (
15+
!!state &&
16+
!!bounds &&
17+
state.x >= bounds.x &&
18+
state.y >= bounds.y &&
19+
state.x + state.width <= bounds.x + bounds.width &&
20+
state.y + state.height <= bounds.y + bounds.height
21+
)
22+
}
23+
24+
function isRectangle(bounds: Partial<Electron.Rectangle>): bounds is Electron.Rectangle {
25+
return (
26+
!!bounds &&
27+
Number.isInteger(bounds.x) &&
28+
Number.isInteger(bounds.y) &&
29+
Number.isInteger(bounds.width) &&
30+
bounds.width > 0 &&
31+
Number.isInteger(bounds.height) &&
32+
bounds.height > 0
33+
)
34+
}
35+
36+
function isStateValid(state: Partial<WindowState>) {
37+
if (!state) {
38+
39+
return false
40+
}
41+
if (!isRectangle(state.displayBounds)) {
42+
43+
return false
44+
}
45+
if (!isRectangle(state)) {
46+
return false
47+
}
48+
const display = screen.getAllDisplays().find((it) => isWithinBounds(it.bounds, state))
49+
return !!display
50+
}
51+
52+
function createDefaultState(bounds: Partial<Electron.Rectangle>): WindowState {
53+
return {
54+
width: 800,
55+
height: 600,
56+
x: 0,
57+
y: 0,
58+
displayBounds: screen.getPrimaryDisplay().bounds,
59+
...(bounds || {}),
60+
}
61+
}
62+
1363
export function windowState(options: {
14-
defaultWidth: number
15-
defaultHeight: number
64+
defaultWidth?: number
65+
defaultHeight?: number
1666
save: (state: Partial<WindowState>) => void
1767
load: () => Partial<WindowState>
1868
}) {
1969
let state: Partial<WindowState> = {}
2070
let winRef: BrowserWindow
21-
let stateChangeTimer
71+
let stateChangeTimer: any
2272
const eventHandlingDelay = 100
2373
const config = {
2474
maximize: true,
2575
fullScreen: true,
26-
defaultWidth: options.defaultWidth,
27-
defaultHeight: options.defaultHeight,
76+
defaultWidth: options.defaultWidth || 800,
77+
defaultHeight: options.defaultHeight || 600,
2878
}
2979

30-
function isNormal(win) {
31-
return !win.isMaximized() && !win.isMinimized() && !win.isFullScreen()
32-
}
33-
34-
function hasBounds() {
35-
return (
36-
state &&
37-
Number.isInteger(state.x) &&
38-
Number.isInteger(state.y) &&
39-
Number.isInteger(state.width) &&
40-
state.width > 0 &&
41-
Number.isInteger(state.height) &&
42-
state.height > 0
43-
)
44-
}
45-
46-
function resetStateToDefault() {
47-
const displayBounds = screen.getPrimaryDisplay().bounds
48-
49-
// Reset state to default values on the primary display
50-
state = {
51-
width: config.defaultWidth || 800,
52-
height: config.defaultHeight || 600,
53-
x: 0,
54-
y: 0,
55-
displayBounds,
56-
}
80+
// Load previous state
81+
try {
82+
state = (options.load() || {}) as any
83+
} catch (err) {
84+
console.error(err)
85+
state = {}
5786
}
5887

59-
function windowWithinBounds(bounds): boolean {
60-
return (
61-
!!state && !!bounds &&
62-
state.x >= bounds.x &&
63-
state.y >= bounds.y &&
64-
state.x + state.width <= bounds.x + bounds.width &&
65-
state.y + state.height <= bounds.y + bounds.height
66-
)
88+
// Set state fallback values
89+
state = {
90+
width: config.defaultWidth || 800,
91+
height: config.defaultHeight || 600,
92+
...(state || {}),
6793
}
6894

69-
function ensureWindowVisibleOnSomeDisplay() {
70-
const visible = screen.getAllDisplays().some((display) => {
71-
return windowWithinBounds(display.bounds)
95+
function resetState() {
96+
state = createDefaultState({
97+
width: config.defaultWidth,
98+
height: config.defaultHeight,
7299
})
73-
74-
if (!visible) {
75-
// Window is partially or fully not visible now.
76-
// Reset it to safe defaults.
77-
return resetStateToDefault()
78-
}
79100
}
80101

81-
function validateState() {
82-
const isValid = state && (hasBounds() || state.isMaximized || state.isFullScreen)
83-
if (!isValid) {
84-
state = {}
85-
return
86-
}
87-
88-
if (hasBounds() && state.displayBounds) {
89-
ensureWindowVisibleOnSomeDisplay()
90-
}
91-
}
92-
93-
function updateState(win?: BrowserWindow) {
94-
win = win || winRef
102+
function updateState(win: BrowserWindow = winRef) {
95103
if (!win) {
96104
return
97105
}
98106
// Don't throw an error when window was closed
99107
try {
100-
const winBounds = win.getBounds()
101-
if (isNormal(win)) {
102-
state.x = winBounds.x
103-
state.y = winBounds.y
104-
state.width = winBounds.width
105-
state.height = winBounds.height
108+
const bounds = win.getBounds()
109+
if (!win.isMaximized() && !win.isMinimized() && !win.isFullScreen()) {
110+
state.x = bounds.x
111+
state.y = bounds.y
112+
state.width = bounds.width
113+
state.height = bounds.height
106114
}
107115
state.isMaximized = win.isMaximized()
108116
state.isFullScreen = win.isFullScreen()
109-
state.displayBounds = screen.getDisplayMatching(winBounds).bounds
117+
state.displayBounds = screen.getDisplayMatching(bounds).bounds
110118
} catch (err) {}
111119
}
112120

@@ -121,7 +129,6 @@ export function windowState(options: {
121129
options.save(state)
122130
} catch (err) {
123131
console.log(err)
124-
// Don't care
125132
}
126133
}
127134

@@ -142,10 +149,19 @@ export function windowState(options: {
142149
}
143150

144151
function manage(win: BrowserWindow) {
145-
if (config.maximize && state?.isMaximized) {
152+
if (!isStateValid(state)) {
153+
resetState()
154+
win.setBounds({
155+
x: state.x,
156+
y: state.y,
157+
width: state.width,
158+
height: state.height
159+
}, true)
160+
}
161+
if (config.maximize && state.isMaximized) {
146162
win.maximize()
147163
}
148-
if (config.fullScreen && state?.isFullScreen) {
164+
if (config.fullScreen && state.isFullScreen) {
149165
win.setFullScreen(true)
150166
}
151167
win.on('resize', stateChangeHandler)
@@ -175,53 +191,31 @@ export function windowState(options: {
175191
}
176192
}
177193

178-
// Load previous state
179-
try {
180-
state = (options.load() || {}) as any
181-
} catch (err) {
182-
state = {}
183-
}
184-
185-
// Check state validity
186-
app.whenReady().then(() => {
187-
validateState()
188-
})
189-
190-
191-
// Set state fallback values
192-
state = Object.assign(
193-
{
194-
width: config.defaultWidth || 800,
195-
height: config.defaultHeight || 600,
196-
},
197-
state
198-
)
199-
200194
return {
201195
get x() {
202-
return state?.x
196+
return state.x
203197
},
204198
get y() {
205-
return state?.y
199+
return state.y
206200
},
207201
get width() {
208-
return state?.width
202+
return state.width
209203
},
210204
get height() {
211-
return state?.height
205+
return state.height
212206
},
213207
get displayBounds() {
214-
return state?.displayBounds
208+
return state.displayBounds
215209
},
216210
get isMaximized() {
217-
return state?.isMaximized
211+
return state.isMaximized
218212
},
219213
get isFullScreen() {
220-
return state?.isFullScreen
214+
return state.isFullScreen
221215
},
222216
saveState,
223217
unmanage,
224218
manage,
225-
resetStateToDefault,
219+
resetStateToDefault: resetState,
226220
}
227221
}

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "nw-buddy",
3-
"version": "1.7.0-1",
3+
"version": "1.7.0-2",
44
"description": "New World Buddy",
55
"keywords": [],
66
"main": "dist/electron/main.js",

0 commit comments

Comments
 (0)