@deepkit/desktop-ui
Version:
Library for desktop UI widgets in Angular 10+
173 lines (139 loc) • 4.68 kB
text/typescript
/*
* Deepkit Framework
* Copyright (C) 2021 Deepkit UG, Marc J. Schmidt
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the MIT License.
*
* You should have received a copy of the MIT License along with this program.
*/
import { ChangeDetectorRef, Injectable, TemplateRef, ViewContainerRef } from '@angular/core';
import { arrayRemoveItem } from '@deepkit/core';
import { WindowMenuState } from './window-menu';
import { BehaviorSubject } from 'rxjs';
import { detectChangesNextFrame } from '../app/utils';
interface WinHeader {
getBottomPosition(): number;
}
interface Win {
id: number;
electronWindow?: any;
getClosestNonDialogWindow(): Win | undefined;
header?: WinHeader;
viewContainerRef: ViewContainerRef;
}
export class WindowRegistry {
id = 0;
registry = new Map<Win, {
state: WindowState,
menu: WindowMenuState,
cd: ChangeDetectorRef,
viewContainerRef: ViewContainerRef
}>();
windowHistory: Win[] = [];
activeWindow?: Win;
/**
* When BrowserWindow of electron is focused.
*/
public focused = new BehaviorSubject(false);
constructor() {
this.focused.subscribe((v) => {
for (const win of this.registry.values()) {
win.state.focus.next(v);
}
detectChangesNextFrame();
});
}
getAllElectronWindows(): any[] {
return [...this.registry.keys()].filter(v => !!v.electronWindow).map(v => v.electronWindow);
}
register(win: Win, cd: ChangeDetectorRef, state: WindowState, menu: WindowMenuState, viewContainerRef: ViewContainerRef) {
this.id++;
win.id = this.id;
this.registry.set(win, {
state, menu, cd, viewContainerRef,
});
}
/**
* Finds the activeWindow and returns its most outer parent.
*/
getOuterActiveWindow(): Win | undefined {
if (this.activeWindow) return this.activeWindow.getClosestNonDialogWindow();
}
getCurrentViewContainerRef(): ViewContainerRef {
if (this.activeWindow) {
return this.activeWindow.viewContainerRef;
// const reg = this.registry.get(this.activeWindow);
// if (reg) {
// return reg.viewContainerRef;
// }
}
throw new Error('No active window');
}
focus(win: Win) {
if (this.activeWindow === win) return;
const reg = this.registry.get(win);
if (!reg) throw new Error('Window not registered');
this.activeWindow = win;
arrayRemoveItem(this.windowHistory, win);
this.windowHistory.push(win);
reg.state.focus.next(true);
reg.menu.focus();
detectChangesNextFrame();
}
blur(win: Win) {
const reg = this.registry.get(win);
if (reg) {
reg.state.focus.next(false);
}
if (this.activeWindow === win) {
delete this.activeWindow;
}
detectChangesNextFrame();
}
unregister(win: Win) {
const reg = this.registry.get(win);
if (reg) {
reg.state.focus.next(false);
}
this.registry.delete(win);
arrayRemoveItem(this.windowHistory, win);
if (this.windowHistory.length) {
this.focus(this.windowHistory[this.windowHistory.length - 1]);
}
detectChangesNextFrame();
}
}
interface AlignedButtonGroup {
sidebarMoved: () => void;
activateOneTimeAnimation: () => void;
}
export class WindowState {
public buttonGroupAlignedToSidebar?: AlignedButtonGroup;
public focus = new BehaviorSubject<boolean>(false);
public disableInputs = new BehaviorSubject<boolean>(false);
public toolbars: { [name: string]: TemplateRef<any>[] } = {};
public toolbarContainers: { [name: string]: { toolbarsUpdated: () => void } } = {};
closable = true;
maximizable = true;
minimizable = true;
constructor() {
}
public addToolbarContainer(forName: string, template: TemplateRef<any>) {
if (!this.toolbars[forName]) {
this.toolbars[forName] = [];
}
this.toolbars[forName].push(template);
if (this.toolbarContainers[forName]) {
this.toolbarContainers[forName].toolbarsUpdated();
}
}
public removeToolbarContainer(forName: string, template: TemplateRef<any>) {
arrayRemoveItem(this.toolbars[forName], template);
if (this.toolbarContainers[forName]) {
this.toolbarContainers[forName].toolbarsUpdated();
}
}
}