downthemall/lib/windowstatetracker.ts

143 lines
3.2 KiB
TypeScript

"use strict";
// License: MIT
import { Prefs } from "./prefs";
import { windows } from "./browser";
// eslint-disable-next-line no-unused-vars
import { Port } from "./bus";
const VALID_WINDOW_STATES = Object.freeze(new Set(["normal", "maximized"]));
interface Constraints {
minWidth: number;
minHeight: number;
left?: number;
top?: number;
}
export class WindowStateTracker {
private width: number;
private height: number;
private readonly minWidth: number;
private readonly minHeight: number;
private left: number;
private top: number;
private state: string;
private readonly key: string;
private windowId: number;
constructor(windowType: string, constraints: Constraints) {
// eslint-disable-next-line no-magic-numbers
const {minWidth = 500, minHeight = 400, left = -1, top = -1} = constraints;
this.width = this.minWidth = minWidth;
this.height = this.minHeight = minHeight;
this.left = left;
this.top = top;
this.state = "normal";
this.key = `window-state-${windowType}`;
this.update = this.update.bind(this);
}
async init() {
const initialState = await Prefs.get(this.key);
if (initialState) {
Object.assign(this, initialState);
}
this.validate();
}
getOptions(options: any) {
const result = Object.assign(options, {
state: this.state,
});
if (result.state !== "maximized") {
result.width = this.width;
result.height = this.height;
if (this.top >= 0) {
result.top = this.top;
result.left = this.left;
}
}
return result;
}
validate() {
this.width = Math.max(this.minWidth, this.width) || this.minWidth;
this.height = Math.max(this.minHeight, this.height) || this.minHeight;
this.top = Math.max(-1, this.top) || -1;
this.left = Math.max(-1, this.left) || -1;
this.state = VALID_WINDOW_STATES.has(this.state) ? this.state : "normal";
}
async update() {
if (!this.windowId) {
return;
}
try {
const window = await windows.get(this.windowId);
if (!VALID_WINDOW_STATES.has(window.state)) {
return;
}
const previous = JSON.stringify(this);
this.width = window.width;
this.height = window.height;
this.left = window.left;
this.top = window.top;
this.state = window.state;
this.validate();
if (previous === JSON.stringify(this)) {
// Nothing changed
return;
}
await this.save();
}
catch {
// ignored
}
}
track(windowId: number, port?: Port) {
if (port) {
port.on("resized", this.update);
port.on("unload", e => this.finalize(e));
port.on("disconnect", this.finalize.bind(this));
}
this.windowId = windowId;
}
async finalize(state?: any) {
if (state) {
this.left = state.left;
this.top = state.top;
}
await this.update();
this.windowId = 0;
if (state) {
await this.save();
}
}
async save() {
await Prefs.set(this.key, this.toJSON());
}
toJSON() {
return {
width: this.width,
height: this.height,
top: this.top,
left: this.left,
state: this.state,
};
}
}