desktop: migrate window state to typescript

This commit is contained in:
Abdullah Atta
2023-01-26 11:00:18 +05:00
committed by Abdullah Atta
parent 60ba6ba6cf
commit dde276f77e
3 changed files with 78 additions and 52 deletions

View File

@@ -18,36 +18,63 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { JSONStorage } from "../jsonstorage";
import { screen as _screen, remote } from "electron";
const screen = _screen || remote.screen;
import { screen as _screen, BrowserWindow } from "electron";
const screen = _screen;
class WindowState {
constructor(options) {
this.winRef = null;
type WindowStateOptions = {
storageKey: string;
maximize: boolean;
fullScreen: boolean;
defaultWidth: number;
defaultHeight: number;
};
type SerializableWindowState = {
x: number;
y: number;
width: number;
height: number;
displayBounds?: Electron.Rectangle;
isMaximized?: boolean;
isFullScreen?: boolean;
};
export class WindowState {
private readonly config: WindowStateOptions;
private state: SerializableWindowState;
private windowRef: BrowserWindow | undefined;
private readonly eventHandlingDelay = 100;
private stateChangeTimer: NodeJS.Timeout | undefined;
constructor(options?: Partial<WindowStateOptions>) {
this.windowRef = undefined;
this.stateChangeTimer = undefined;
this.eventHandlingDelay = 100;
this.config = {
storageKey: "windowState",
maximize: true,
fullScreen: true,
defaultHeight: 800,
defaultWidth: 600,
...options
};
// Load previous state
this.state = JSONStorage.get(this.config.storageKey, {});
const defaultState: SerializableWindowState = {
width: this.config.defaultWidth,
height: this.config.defaultHeight,
x: 0,
y: 0
};
this.state = JSONStorage.get<SerializableWindowState>(
this.config.storageKey,
defaultState
);
// Check state validity
this.validateState();
// Set state fallback values
this.state = {
width: this.config.defaultWidth || 800,
height: this.config.defaultHeight || 600,
...this.state
};
this.validateState(defaultState);
}
isNormal(win) {
isNormal(win: BrowserWindow) {
return !win.isMaximized() && !win.isMinimized() && !win.isFullScreen();
}
@@ -76,7 +103,9 @@ class WindowState {
};
}
windowWithinBounds(bounds) {
windowWithinBounds(bounds: Electron.Rectangle) {
if (!this.state) return false;
return (
this.state.x >= bounds.x &&
this.state.y >= bounds.y &&
@@ -97,12 +126,12 @@ class WindowState {
}
}
validateState() {
validateState(defaultState: SerializableWindowState) {
const isValid =
this.state &&
(this.hasBounds() || this.state.isMaximized || this.state.isFullScreen);
if (!isValid) {
this.state = null;
this.state = defaultState;
return;
}
@@ -111,8 +140,8 @@ class WindowState {
}
}
updateState(win) {
win = win || this.winRef;
updateState(win?: BrowserWindow) {
win = win || this.windowRef;
if (!win) {
return;
}
@@ -133,7 +162,7 @@ class WindowState {
}
}
saveState(win) {
saveState(win?: BrowserWindow) {
// Update window state only if it was provided
if (win) {
this.updateState(win);
@@ -145,7 +174,7 @@ class WindowState {
stateChangeHandler = () => {
// Handles both 'resize' and 'move'
clearTimeout(this.stateChangeTimer);
if (this.stateChangeTimer) clearTimeout(this.stateChangeTimer);
this.stateChangeTimer = setTimeout(
() => this.updateState(),
this.eventHandlingDelay
@@ -163,7 +192,7 @@ class WindowState {
this.saveState();
};
manage(win) {
manage(win: BrowserWindow) {
if (this.config.maximize && this.state.isMaximized) {
win.maximize();
}
@@ -174,17 +203,17 @@ class WindowState {
win.on("move", this.stateChangeHandler);
win.on("close", this.closeHandler);
win.on("closed", this.closedHandler);
this.winRef = win;
this.windowRef = win;
}
unmanage() {
if (this.winRef) {
this.winRef.removeListener("resize", this.stateChangeHandler);
this.winRef.removeListener("move", this.stateChangeHandler);
clearTimeout(this.stateChangeTimer);
this.winRef.removeListener("close", this.closeHandler);
this.winRef.removeListener("closed", this.closedHandler);
this.winRef = null;
if (this.windowRef) {
this.windowRef.removeListener("resize", this.stateChangeHandler);
this.windowRef.removeListener("move", this.stateChangeHandler);
if (this.stateChangeTimer) clearTimeout(this.stateChangeTimer);
this.windowRef.removeListener("close", this.closeHandler);
this.windowRef.removeListener("closed", this.closedHandler);
this.windowRef = undefined;
}
}
@@ -216,5 +245,3 @@ class WindowState {
return this.state.isFullScreen;
}
}
export { WindowState };

View File

@@ -27,7 +27,7 @@ import { getBackgroundColor, getTheme, setTheme } from "./config/theme";
import getZoomFactor from "./ipc/calls/getZoomFactor";
import { logger } from "./logger";
import { setupMenu } from "./menu";
import { WindowState } from "./config/windowstate";
import { WindowState } from "./config/window-state";
import { sendMessageToRenderer } from "./ipc/utils";
import { EVENTS } from "./events";
import "./ipc/index.js";
@@ -38,6 +38,7 @@ import yargs from "yargs";
import { hideBin } from "yargs/helpers";
import { getDesktopIntegration } from "./config/desktopIntegration";
import { AutoLaunch } from "./autolaunch";
import bringToFront from "./ipc/actions/bringToFront";
if (!RELEASE) {
require("electron-reloader")(module);
@@ -251,14 +252,14 @@ function setupTray() {
label: "Show Notesnook",
type: "normal",
icon: APP_ICON_PATH,
click: showApp
click: bringToFront
},
{ type: "separator" },
{
label: "New note",
type: "normal",
click: () => {
showApp();
bringToFront();
sendMessageToRenderer(EVENTS.createItem, { itemType: "note" });
}
},
@@ -266,7 +267,7 @@ function setupTray() {
label: "New notebook",
type: "normal",
click: () => {
showApp();
bringToFront();
sendMessageToRenderer(EVENTS.createItem, { itemType: "notebook" });
}
},
@@ -279,24 +280,12 @@ function setupTray() {
}
}
]);
tray.on("double-click", showApp);
tray.on("click", showApp);
tray.on("double-click", bringToFront);
tray.on("click", bringToFront);
tray.setToolTip("Notesnook");
tray.setContextMenu(contextMenu);
}
function showApp() {
if (globalThis.window.isMinimized()) {
if (mainWindowState.isMaximized) {
globalThis.window.maximize();
} else globalThis.window.restore();
}
globalThis.window.show();
globalThis.window.focus();
globalThis.window.moveTop();
globalThis.window.webContents.focus();
}
function setupDesktopIntegration() {
const desktopIntegration = getDesktopIntegration();

View File

@@ -17,8 +17,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { WindowState } from "../../config/window-state";
export default () => {
if (!globalThis.window) return;
if (globalThis.window.isMinimized()) {
if (new WindowState({}).isMaximized) {
globalThis.window.maximize();
} else globalThis.window.restore();
}
globalThis.window.show();
globalThis.window.focus();
globalThis.window.moveTop();
globalThis.window.webContents.focus();
};