diff --git a/gui-js/apps/minsky-electron/src/app/app.ts b/gui-js/apps/minsky-electron/src/app/app.ts index 632ed57aa..8e55f10ff 100644 --- a/gui-js/apps/minsky-electron/src/app/app.ts +++ b/gui-js/apps/minsky-electron/src/app/app.ts @@ -149,6 +149,7 @@ export default class App { context: App.mainWindow, systemWindowId: WindowManager.getSystemWindowId(this.mainWindow), menu: null, + url: "", }; WindowManager.activeWindows.set(App.mainWindow.id, mainWindowDetails); diff --git a/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts b/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts index f983398b0..6af5b0bb5 100644 --- a/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts +++ b/gui-js/apps/minsky-electron/src/app/managers/WindowManager.ts @@ -32,11 +32,11 @@ export class WindowManager { private static uidToWindowMap = new Map(); static getWindowByUid(uid: string): ActiveWindow { - return this.uidToWindowMap.get(uid); + return WindowManager.uidToWindowMap.get(uid); } static storeWindowMenu(win: BrowserWindow, menu: Menu) { - const details = this.activeWindows.get(win.id); + const details = WindowManager.activeWindows.get(win.id); if (details) { details.menu = menu; } @@ -49,7 +49,7 @@ export class WindowManager { static setApplicationMenu(win: BrowserWindow) { if (Functions.isMacOS()) { - const details = this.activeWindows.get(win.id); + const details = WindowManager.activeWindows.get(win.id); if (details) { Menu.setApplicationMenu(details.menu); } @@ -59,14 +59,14 @@ export class WindowManager { static async renderFrame() { try { - return this.currentTab?.renderFrame( + return WindowManager.currentTab?.renderFrame( { - parentWindowId: this.activeWindows.get(1).systemWindowId.toString(), - offsetLeft: this.leftOffset, - offsetTop: this.topOffset+this.electronTopOffset, - childWidth: this.canvasWidth, - childHeight: this.canvasHeight, - scalingFactor: this.scaleFactor + parentWindowId: WindowManager.activeWindows.get(1).systemWindowId.toString(), + offsetLeft: WindowManager.leftOffset, + offsetTop: WindowManager.topOffset+WindowManager.electronTopOffset, + childWidth: WindowManager.canvasWidth, + childHeight: WindowManager.canvasHeight, + scalingFactor: WindowManager.scaleFactor }); } catch (err) { @@ -76,10 +76,10 @@ export class WindowManager { } static async setCurrentTab(tab/*: RenderNativeWindow*/) { - if (this.currentTab!==tab) { - await this.currentTab?.destroyFrame(); - this.currentTab=tab; - return this.renderFrame(); + if (WindowManager.currentTab!==tab) { + await WindowManager.currentTab?.destroyFrame(); + WindowManager.currentTab=tab; + return WindowManager.renderFrame(); } } @@ -103,11 +103,11 @@ export class WindowManager { } static getMainWindow(): BrowserWindow { - return this.activeWindows.get(1)?.context; // TODO:: Is this accurate? + return WindowManager.activeWindows.get(1)?.context; // TODO:: Is WindowManager accurate? } static focusIfWindowIsPresent(uid: string) { - const windowDetails = this.uidToWindowMap.get(uid); + const windowDetails = WindowManager.uidToWindowMap.get(uid); if (windowDetails) { windowDetails.context.focus(); return true; @@ -131,6 +131,19 @@ export class WindowManager { return initialURL; } + /// if window already exists attached to \a url, then raise it + /// @return window if it exists, null otherwise + static raiseWindow(url: string): BrowserWindow { + let window=null; + for (let i of WindowManager.activeWindows) + if (i[1].url==url) { + window=i[1].context; + break; + } + if (window) window.show(); + return window; + } + static createPopupWindowWithRouting( payload: CreateWindowPayload, // eslint-disable-next-line @typescript-eslint/ban-types @@ -142,14 +155,14 @@ export class WindowManager { url.searchParams.set('systemWindowId',WindowManager.getSystemWindowId(window).toString()); const relativeUrlString=(payload.url[0]=='#'?'#':'') + url.pathname+'?'+url.searchParams.toString(); - window.loadURL(this.getWindowUrl(relativeUrlString)); + window.loadURL(WindowManager.getWindowUrl(relativeUrlString)); return window; } static closeWindowByUid(uid: string) { - const windowDetails = this.uidToWindowMap.get(uid); + const windowDetails = WindowManager.uidToWindowMap.get(uid); if (windowDetails) { - this.uidToWindowMap.delete(uid); + WindowManager.uidToWindowMap.delete(uid); windowDetails.context.close(); } } @@ -167,8 +180,6 @@ export class WindowManager { slashes: true, }); - console.log(payload.url); - console.log(filePath); window.loadURL(filePath); return window; } @@ -177,8 +188,14 @@ export class WindowManager { payload: CreateWindowPayload, onCloseCallback?: (ev : Electron.Event) => void ) { - const { width, height, minWidth, minHeight, title, modal = true, backgroundColor=StoreManager.store.get('backgroundColor'), alwaysOnTop } = payload; + const { width, height, minWidth, minHeight, title, modal = true, backgroundColor=StoreManager.store.get('backgroundColor'), alwaysOnTop, url } = payload; + // do not duplicate window if requested and window already exists + if (payload.raiseIfPresent) { + const childWindow=WindowManager.raiseWindow(url); + if (childWindow) return childWindow; + } + const childWindow = new BrowserWindow({ width, height, @@ -189,7 +206,7 @@ export class WindowManager { useContentSize: true, minimizable: false, show: false, - parent: modal ? this.getMainWindow() : null, + parent: modal ? WindowManager.getMainWindow() : null, modal, backgroundColor, alwaysOnTop, @@ -225,21 +242,22 @@ export class WindowManager { context: childWindow, systemWindowId: windowId, menu: null, + url, }; if (payload.uid) { - this.uidToWindowMap.set(payload.uid, childWindowDetails); + WindowManager.uidToWindowMap.set(payload.uid, childWindowDetails); } - this.activeWindows.set(childWindow.id, childWindowDetails); + WindowManager.activeWindows.set(childWindow.id, childWindowDetails); childWindow.on('close', (ev : Electron.Event) => { try { if (payload?.uid) { - this.uidToWindowMap.delete(payload.uid); + WindowManager.uidToWindowMap.delete(payload.uid); } if (childWindow?.id) { - this.activeWindows.delete(childWindow.id); + WindowManager.activeWindows.delete(childWindow.id); } if (onCloseCallback) { onCloseCallback(ev); @@ -249,13 +267,13 @@ export class WindowManager { } }); // in the event the webcontents is closed without the containing window being so. - childWindow.webContents.on('destroyed', ()=> this.activeWindows.delete(childWindow.id)); + childWindow.webContents.on('destroyed', ()=> WindowManager.activeWindows.delete(childWindow.id)); return childWindow; } public static scrollToCenter() { - // TODO:: Replace this with something cleaner - this.getMainWindow().webContents.executeJavaScript( + // TODO:: Replace WindowManager with something cleaner + WindowManager.getMainWindow().webContents.executeJavaScript( `var container=document.getElementsByClassName('minsky-canvas-container')[0]; var canvas = container.getElementsByTagName('canvas')[0]; container.scrollTop=canvas.clientHeight/2; container.scrollLeft=canvas.clientWidth/2;`, false ); @@ -263,22 +281,21 @@ export class WindowManager { static onAppLayoutChanged(payload: AppLayoutPayload) { - this.topOffset = Math.round(payload.offset.top); - console.log("topOffset=",this.topOffset); - this.leftOffset = Math.round(payload.offset.left); - this.scaleFactor = screen.getPrimaryDisplay().scaleFactor; + WindowManager.topOffset = Math.round(payload.offset.top); + WindowManager.leftOffset = Math.round(payload.offset.left); + WindowManager.scaleFactor = screen.getPrimaryDisplay().scaleFactor; if (Functions.isWindows()) - // calculate this offset internally in C++ - this.electronTopOffset = 0; + // calculate WindowManager offset internally in C++ + WindowManager.electronTopOffset = 0; else { - let size=this.getMainWindow().getSize(); - let contentSize=this.getMainWindow().getContentSize(); - this.electronTopOffset = size[1]-contentSize[1]; + let size=WindowManager.getMainWindow().getSize(); + let contentSize=WindowManager.getMainWindow().getContentSize(); + WindowManager.electronTopOffset = size[1]-contentSize[1]; } - this.canvasHeight = payload.drawableArea.height; - this.canvasWidth = payload.drawableArea.width; + WindowManager.canvasHeight = payload.drawableArea.height; + WindowManager.canvasWidth = payload.drawableArea.width; } diff --git a/gui-js/libs/shared/src/lib/interfaces/ActiveWindow.ts b/gui-js/libs/shared/src/lib/interfaces/ActiveWindow.ts index 731aeeea6..8db225804 100644 --- a/gui-js/libs/shared/src/lib/interfaces/ActiveWindow.ts +++ b/gui-js/libs/shared/src/lib/interfaces/ActiveWindow.ts @@ -6,5 +6,6 @@ export interface ActiveWindow { isMainWindow: boolean; context: BrowserWindow; systemWindowId: bigint; - menu : Menu + menu: Menu; + url: string; /// url opened in this window, if any. Can be none or empty. } diff --git a/gui-js/libs/shared/src/lib/interfaces/Interfaces.ts b/gui-js/libs/shared/src/lib/interfaces/Interfaces.ts index da9cd53ee..b7bc26d65 100644 --- a/gui-js/libs/shared/src/lib/interfaces/Interfaces.ts +++ b/gui-js/libs/shared/src/lib/interfaces/Interfaces.ts @@ -132,6 +132,7 @@ export interface CreateWindowPayload { minWidth?: number; minHeight?: number; alwaysOnTop?: boolean; + raiseIfPresent?: boolean; ///< if true, then raise an existing window instead of creating a duplicate } diff --git a/gui-js/libs/ui-components/src/lib/wiring/variable/variable.component.ts b/gui-js/libs/ui-components/src/lib/wiring/variable/variable.component.ts index 2a995abf6..0c8acd8b9 100644 --- a/gui-js/libs/ui-components/src/lib/wiring/variable/variable.component.ts +++ b/gui-js/libs/ui-components/src/lib/wiring/variable/variable.component.ts @@ -42,14 +42,15 @@ export class VariableComponent { }); } } - openVariablePane() { - this.electronService.send(events.CREATE_MENU_POPUP, { - title: 'Variables', - url: "#/headless/variable-pane", - width: 400, - height: 450, - alwaysOnTop: true, - modal: false, - }); + openVariablePane() { + this.electronService.send(events.CREATE_MENU_POPUP, { + title: 'Variables', + url: "#/headless/variable-pane", + width: 400, + height: 450, + alwaysOnTop: true, + modal: false, + raiseIfPresent: true, + }); } }