From 945debbe1b25c83a94b91289d8af0136f078a9d7 Mon Sep 17 00:00:00 2001 From: Marc Dumais Date: Fri, 9 Jun 2023 16:25:17 -0400 Subject: [PATCH 1/7] WIP - set initial size and position of secondary windows e.g. sized the same as the original widget that we are extracting into it's own windows and position at the same place, with an offset. Signed-off-by: Marc Dumais --- .../default-secondary-window-service.ts | 38 ++++++++++++++++++- .../electron-main-application.ts | 27 ++++++++++++- 2 files changed, 63 insertions(+), 2 deletions(-) diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index 166d5b3de0ccf..4863ee4d76fef 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -100,7 +100,43 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), 'popup') ?? undefined; + // const options = 'popup'; + // const options = 'popup,width=500,height=500,left=500,top=500'; + console.log('**** widget: ' + widget); + console.log('**** widget.id: ' + widget.id); + + // console.log('**** widget.node.parentNode?.textContent: ' + widget.node?.parentNode?.textContent); + + // const options = 'popup,width=600,height=200,left=2800,top=150'; + // const options = 'popup,innerWidth=600,innerHeight=200,left=2800,top=150'; + let options; + if (widget.node) { + const clientBounds = widget.node.getBoundingClientRect(); + + // shift a bit right and down (enough to clear the editor's preview button) + const offsetX = 0; // 50 + widget.node.clientWidth; + const offsetY = 0; + const offsetHeigth = 0; + const offsetWidth = 0; + + // try to place secondary window left of the main window + // const offsetX = widget.node.clientWidth; + // const offsetY = 0; + + const h = widget.node.clientHeight + offsetHeigth; + const w = widget.node.clientWidth + offsetWidth; + // window.screenLeft: horizontal offset of main window (top left corner) vs desktop + // window.screenTop: vertical offset of main window vs desktop + const l = widget.node.clientLeft + window.screenLeft + clientBounds.x + offsetX; + const t = widget.node.clientTop + window.screenTop + clientBounds.y + offsetY; + + options = `popup=1,width=${w},height=${h},left=${l},top=${t}`; + // TODO: add a preference? + options += ',alwaysOnTop=true'; + console.log('*** creating secondary window with options: ' + options); + } + + const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined; if (newWindow) { newWindow.addEventListener('DOMContentLoaded', () => { newWindow.addEventListener('beforeunload', evt => { diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index ec49244cd5ad7..121ab4fe9a654 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -336,13 +336,16 @@ export class ElectronMainApplication { electronWindow.webContents.setWindowOpenHandler(() => { const { minWidth, minHeight } = this.getDefaultOptions(); const options: BrowserWindowConstructorOptions = { - ...this.getDefaultTheiaWindowBounds(), + ...this.getDefaultTheiaSecondaryWindowBounds(), // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. frame: true, minWidth, minHeight + // , + // // TODO: add a preference? + // alwaysOnTop: true }; if (!this.useNativeWindowFrame) { // If the main window does not have a native window frame, do not show an icon in the secondary window's native title bar. @@ -389,6 +392,7 @@ export class ElectronMainApplication { } protected getDefaultTheiaWindowOptions(): TheiaBrowserWindowOptions { + console.log('*** getDefaultTheiaWindowOptions() '); return { frame: this.useNativeWindowFrame, isFullScreen: false, @@ -408,6 +412,7 @@ export class ElectronMainApplication { const width = Math.round(bounds.width * (2 / 3)); const y = Math.round(bounds.y + (bounds.height - height) / 2); const x = Math.round(bounds.x + (bounds.width - width) / 2); + console.log(`*** getDefaultTheiaWindowBounds(): { width=${width}, height=${height}, x=${x}}, y=${y} }`); return { width, height, @@ -416,6 +421,26 @@ export class ElectronMainApplication { }; } + protected getDefaultTheiaSecondaryWindowBounds(): TheiaBrowserWindowOptions { + // const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); + // const height = Math.round(bounds.height * (2 / 3)); + // const width = Math.round(bounds.width * (2 / 3)); + // const y = Math.round(bounds.y + (bounds.height - height) / 2); + // const x = Math.round(bounds.x + (bounds.width - width) / 2); + // const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); + // const height = Math.round(bounds.height * (2 / 3)); + // const width = Math.round(bounds.width * (2 / 3)); + // const y = Math.round(bounds.y + (bounds.height - height) / 2); + // const x = Math.round(bounds.x + (bounds.width - width) / 2); + // console.log(`*** getDefaultTheiaSecondaryWindowBounds(): { width=${width}, height=${height}, x=${x}}, y=${y} }`); + return { + // width, + // height, + // x, + // y + }; + } + /** * Save the window geometry state on every change. */ From 583c1a2c73bc791d9e503bf3a697b0d700497ff0 Mon Sep 17 00:00:00 2001 From: Marc Dumais Date: Mon, 12 Jun 2023 10:01:31 -0400 Subject: [PATCH 2/7] clean up a bit Signed-off-by: Marc Dumais --- .../default-secondary-window-service.ts | 9 ++------ .../electron-main-application.ts | 21 ++++--------------- 2 files changed, 6 insertions(+), 24 deletions(-) diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index 4863ee4d76fef..041a2b6b0d3fc 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -104,11 +104,6 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { // const options = 'popup,width=500,height=500,left=500,top=500'; console.log('**** widget: ' + widget); console.log('**** widget.id: ' + widget.id); - - // console.log('**** widget.node.parentNode?.textContent: ' + widget.node?.parentNode?.textContent); - - // const options = 'popup,width=600,height=200,left=2800,top=150'; - // const options = 'popup,innerWidth=600,innerHeight=200,left=2800,top=150'; let options; if (widget.node) { const clientBounds = widget.node.getBoundingClientRect(); @@ -116,14 +111,14 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { // shift a bit right and down (enough to clear the editor's preview button) const offsetX = 0; // 50 + widget.node.clientWidth; const offsetY = 0; - const offsetHeigth = 0; + const offsetHeight = 0; const offsetWidth = 0; // try to place secondary window left of the main window // const offsetX = widget.node.clientWidth; // const offsetY = 0; - const h = widget.node.clientHeight + offsetHeigth; + const h = widget.node.clientHeight + offsetHeight; const w = widget.node.clientWidth + offsetWidth; // window.screenLeft: horizontal offset of main window (top left corner) vs desktop // window.screenTop: vertical offset of main window vs desktop diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 121ab4fe9a654..e13359fdf5a86 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -256,6 +256,7 @@ export class ElectronMainApplication { * @param options */ async createWindow(asyncOptions: MaybePromise = this.getDefaultTheiaWindowOptions()): Promise { + console.log('** ** ** createWindow() - '); let options = await asyncOptions; options = this.avoidOverlap(options); const electronWindow = this.windowFactory(options, this.config); @@ -271,6 +272,7 @@ export class ElectronMainApplication { } async getLastWindowOptions(): Promise { + console.log('*** *** getLastWindowOptions() '); const previousWindowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate'); const windowState = previousWindowState?.screenLayout === this.getCurrentScreenLayout() ? previousWindowState @@ -422,23 +424,7 @@ export class ElectronMainApplication { } protected getDefaultTheiaSecondaryWindowBounds(): TheiaBrowserWindowOptions { - // const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); - // const height = Math.round(bounds.height * (2 / 3)); - // const width = Math.round(bounds.width * (2 / 3)); - // const y = Math.round(bounds.y + (bounds.height - height) / 2); - // const x = Math.round(bounds.x + (bounds.width - width) / 2); - // const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint()); - // const height = Math.round(bounds.height * (2 / 3)); - // const width = Math.round(bounds.width * (2 / 3)); - // const y = Math.round(bounds.y + (bounds.height - height) / 2); - // const x = Math.round(bounds.x + (bounds.width - width) / 2); - // console.log(`*** getDefaultTheiaSecondaryWindowBounds(): { width=${width}, height=${height}, x=${x}}, y=${y} }`); - return { - // width, - // height, - // x, - // y - }; + return {}; } /** @@ -464,6 +450,7 @@ export class ElectronMainApplication { } protected saveWindowState(electronWindow: BrowserWindow): void { + console.log('*** *** saveWindowState() '); // In some circumstances the `electronWindow` can be `null` if (!electronWindow) { return; From b1a6e63aaaf6f5643804ae696fc3d345f33c8718 Mon Sep 17 00:00:00 2001 From: Vlad Arama Date: Wed, 13 Dec 2023 15:14:50 -0500 Subject: [PATCH 3/7] implement preference and initial values This prototype implements the initial size and positions of secondary windows. It also adds a preference, called `Secondary Window Placement` allowing the user to choose the placement of the secondary windows between: - originalSize: same size as the widget. - splitScreen: half the size of the Theia application, positioned to the side. - fullScreen: the secondary window will take up the full screen. Signed-off-by: Vlad Arama --- packages/core/src/browser/core-preferences.ts | 11 +++ .../default-secondary-window-service.ts | 75 +++++++++++++------ 2 files changed, 65 insertions(+), 21 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 45ae47154a852..fe4106d4d244a 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -119,6 +119,17 @@ export const corePreferenceSchema: PreferenceSchema = { scope: 'application', markdownDescription: nls.localizeByDefault('Separator used by {0}.', '`#window.title#`') }, + 'window.secondaryWindowPlacement': { + type: 'string', + enum: ['originalSize', 'splitScreen', 'fullScreen'], + enumDescriptions: [ + nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be the same as the original widget.'), + nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be half of the Theia application.'), + nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be the same as the Theia application.'), + ], + default: 'originalSize', + description: nls.localize('theia/core/secondaryWindow', 'Set the initial position and size of the extracted secondary window.'), + }, 'http.proxy': { type: 'string', pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$', diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index e5e786e19b1e2..d6e7018b4b87c 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -19,6 +19,7 @@ import { WindowService } from './window-service'; import { ExtractableWidget } from '../widgets'; import { ApplicationShell } from '../shell'; import { Saveable } from '../saveable'; +import { PreferenceService } from '../preferences'; @injectable() export class DefaultSecondaryWindowService implements SecondaryWindowService { @@ -38,6 +39,9 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { @inject(WindowService) protected readonly windowService: WindowService; + @inject(PreferenceService) + protected readonly preferenceService: PreferenceService; + @postConstruct() init(): void { // Set up messaging with secondary windows @@ -106,27 +110,8 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { console.log('**** widget.id: ' + widget.id); let options; if (widget.node) { - const clientBounds = widget.node.getBoundingClientRect(); - - // shift a bit right and down (enough to clear the editor's preview button) - const offsetX = 0; // 50 + widget.node.clientWidth; - const offsetY = 0; - const offsetHeight = 0; - const offsetWidth = 0; - - // try to place secondary window left of the main window - // const offsetX = widget.node.clientWidth; - // const offsetY = 0; - - const h = widget.node.clientHeight + offsetHeight; - const w = widget.node.clientWidth + offsetWidth; - // window.screenLeft: horizontal offset of main window (top left corner) vs desktop - // window.screenTop: vertical offset of main window vs desktop - const l = widget.node.clientLeft + window.screenLeft + clientBounds.x + offsetX; - const t = widget.node.clientTop + window.screenTop + clientBounds.y + offsetY; - - options = `popup=1,width=${w},height=${h},left=${l},top=${t}`; - // TODO: add a preference? + const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget); + options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; options += ',alwaysOnTop=true'; console.log('*** creating secondary window with options: ' + options); } @@ -155,6 +140,54 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { return newWindow; } + protected findSecondaryWindowCoordinates(widget: ExtractableWidget): (number | undefined)[] { + const clientBounds = widget.node.getBoundingClientRect(); + const preference = this.preferenceService.get('window.secondaryWindowPlacement'); + + let height; let width; let left; let top; + + switch (preference) { + case 'originalSize': { + // shift a bit right and down (enough to clear the editor's preview button) + // const offsetX = 0; // 50 + widget.node.clientWidth; + // const offsetY = 0; + const offsetHeight = 50; + const offsetWidth = 0; + + // try to place secondary window left of the main window + // const offsetX = widget.node.clientWidth; + // const offsetY = 0; + + height = widget.node.clientHeight; + width = widget.node.clientWidth + offsetWidth; + // window.screenLeft: horizontal offset of main window (top left corner) vs desktop + // window.screenTop: vertical offset of main window vs desktop + left = widget.node.clientLeft + window.screenLeft + clientBounds.x; + top = widget.node.clientTop + window.screenTop + clientBounds.y + offsetHeight; + + console.log('***height, width' + height + width); + console.log('***client left / screen left' + `${widget.node.clientLeft}` + `${window.screenLeft}`); + console.log('***client top / screen top' + `${widget.node.clientTop}` + `${window.screenTop}`); + break; + } + case 'splitScreen': { + height = window.screen.availHeight; + width = window.screen.availWidth / 2; + left = window.screen.availWidth / 2; + top = 0; + break; + } + case 'fullScreen': { + height = window.screen.availHeight; + width = window.screen.availWidth; + left = window.screen.availWidth; + top = window.screen.availHeight; + break; + } + } + return [height, width, left, top]; + } + focus(win: Window): void { win.focus(); } From 6620da7817c2e7d396917e22cd32067ddef9ab4e Mon Sep 17 00:00:00 2001 From: Vlad Arama Date: Tue, 19 Dec 2023 14:46:23 -0500 Subject: [PATCH 4/7] [wip] implement correct values This commit implements the correct values for the initial size and position of secondary windows. Signed-off-by --- .../default-secondary-window-service.ts | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index d6e7018b4b87c..b0dc9b431ed0f 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -20,6 +20,7 @@ import { ExtractableWidget } from '../widgets'; import { ApplicationShell } from '../shell'; import { Saveable } from '../saveable'; import { PreferenceService } from '../preferences'; +import { environment, isWindows } from '../../common'; @injectable() export class DefaultSecondaryWindowService implements SecondaryWindowService { @@ -137,6 +138,7 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { }); }); } + newWindow?.focus(); return newWindow; } @@ -144,44 +146,53 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { const clientBounds = widget.node.getBoundingClientRect(); const preference = this.preferenceService.get('window.secondaryWindowPlacement'); + // const offsetY = 50; + let height; let width; let left; let top; + console.log(`***height, width + ${widget.node.clientHeight} + ${widget.node.clientWidth}`); + console.log(`***client left/screen left/clienx' + ${widget.node.clientLeft} + ${window.screenLeft} + ${clientBounds.x}`); + console.log(`***client top / screen top' + ${widget.node.clientTop} + ${window.screenTop} + ${clientBounds.y}`); + console.log(`*** avail height/width ${window.screen.availHeight} ${window.screen.availWidth}`); + switch (preference) { case 'originalSize': { - // shift a bit right and down (enough to clear the editor's preview button) - // const offsetX = 0; // 50 + widget.node.clientWidth; - // const offsetY = 0; - const offsetHeight = 50; - const offsetWidth = 0; - - // try to place secondary window left of the main window - // const offsetX = widget.node.clientWidth; - // const offsetY = 0; - + if (isWindows) { + height = widget.node.clientHeight; + width = widget.node.clientWidth; + left = window.screenLeft + clientBounds.x - 8; + if (environment.electron.is()) { + top = window.screenTop + clientBounds.y; + } else { + top = window.screenTop + clientBounds.y + 48; + } + break; + } height = widget.node.clientHeight; - width = widget.node.clientWidth + offsetWidth; - // window.screenLeft: horizontal offset of main window (top left corner) vs desktop - // window.screenTop: vertical offset of main window vs desktop - left = widget.node.clientLeft + window.screenLeft + clientBounds.x; - top = widget.node.clientTop + window.screenTop + clientBounds.y + offsetHeight; - - console.log('***height, width' + height + width); - console.log('***client left / screen left' + `${widget.node.clientLeft}` + `${window.screenLeft}`); - console.log('***client top / screen top' + `${widget.node.clientTop}` + `${window.screenTop}`); + width = widget.node.clientWidth; + left = window.screenLeft + clientBounds.x; + if (environment.electron.is()) { + top = window.screenTop + clientBounds.y; + } else { + top = window.screenTop + clientBounds.y + 60; + } break; } case 'splitScreen': { height = window.screen.availHeight; width = window.screen.availWidth / 2; - left = window.screen.availWidth / 2; + left = 0; top = 0; break; } case 'fullScreen': { height = window.screen.availHeight; width = window.screen.availWidth; - left = window.screen.availWidth; - top = window.screen.availHeight; + left = 0; + top = 0; + // if (isWindows) { + // width = window.screen.availWidth - 30; + // } break; } } From ad84f78b6e1013657b73669c1fdc8bb2f7a457c3 Mon Sep 17 00:00:00 2001 From: Vlad Arama Date: Wed, 20 Dec 2023 14:29:44 -0500 Subject: [PATCH 5/7] implement size/position of secondary windows Signed-off-by: Vlad Arama --- packages/core/src/browser/core-preferences.ts | 15 +++-- .../default-secondary-window-service.ts | 66 +++++++------------ .../electron-main-application.ts | 14 +--- 3 files changed, 33 insertions(+), 62 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index fe4106d4d244a..51e7e5d0bcfa4 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -121,14 +121,19 @@ export const corePreferenceSchema: PreferenceSchema = { }, 'window.secondaryWindowPlacement': { type: 'string', - enum: ['originalSize', 'splitScreen', 'fullScreen'], + enum: ['originalSize', 'halfSize', 'fullSize'], enumDescriptions: [ - nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be the same as the original widget.'), - nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be half of the Theia application.'), - nls.localize('theia/core/secondaryWindow', 'The position and size of the widget will be the same as the Theia application.'), + nls.localize('theia/core/secondaryWindow/originalSize', 'The position and size of the extracted widget will be the same as the original widget.'), + nls.localize('theia/core/secondaryWindow/halfSize', 'The position and size of the extracted widget will be half the size of the running Theia application.'), + nls.localize('theia/core/secondaryWindow/fullSize', 'The position and size of the extracted widget will be the same as the running Theia application.'), ], default: 'originalSize', - description: nls.localize('theia/core/secondaryWindow', 'Set the initial position and size of the extracted secondary window.'), + description: nls.localize('theia/core/secondaryWindow/description', 'Sets the initial position and size of the extracted secondary window.'), + }, + 'window.secondaryWindowAlwaysOnTop': { + type: 'boolean', + default: 'true', + description: nls.localize('theia/core/secondaryWindow/alwaysOnTop', 'The secondary window will always pop-up on top of the current application.'), }, 'http.proxy': { type: 'string', diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index b0dc9b431ed0f..e573d3622d1f5 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -20,7 +20,7 @@ import { ExtractableWidget } from '../widgets'; import { ApplicationShell } from '../shell'; import { Saveable } from '../saveable'; import { PreferenceService } from '../preferences'; -import { environment, isWindows } from '../../common'; +import { environment } from '../../common'; @injectable() export class DefaultSecondaryWindowService implements SecondaryWindowService { @@ -105,18 +105,12 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } protected doCreateSecondaryWindow(widget: ExtractableWidget, shell: ApplicationShell): Window | undefined { - // const options = 'popup'; - // const options = 'popup,width=500,height=500,left=500,top=500'; - console.log('**** widget: ' + widget); - console.log('**** widget.id: ' + widget.id); let options; - if (widget.node) { - const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget); - options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; + const [height, width, left, top] = this.findSecondaryWindowCoordinates(widget); + options = `popup=1,width=${width},height=${height},left=${left},top=${top}`; + if (this.preferenceService.get('window.secondaryWindowAlwaysOnTop')) { options += ',alwaysOnTop=true'; - console.log('*** creating secondary window with options: ' + options); } - const newWindow = window.open(DefaultSecondaryWindowService.SECONDARY_WINDOW_URL, this.nextWindowId(), options) ?? undefined; if (newWindow) { newWindow.addEventListener('DOMContentLoaded', () => { @@ -138,7 +132,6 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { }); }); } - newWindow?.focus(); return newWindow; } @@ -146,53 +139,38 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { const clientBounds = widget.node.getBoundingClientRect(); const preference = this.preferenceService.get('window.secondaryWindowPlacement'); - // const offsetY = 50; - let height; let width; let left; let top; - - console.log(`***height, width + ${widget.node.clientHeight} + ${widget.node.clientWidth}`); - console.log(`***client left/screen left/clienx' + ${widget.node.clientLeft} + ${window.screenLeft} + ${clientBounds.x}`); - console.log(`***client top / screen top' + ${widget.node.clientTop} + ${window.screenTop} + ${clientBounds.y}`); - console.log(`*** avail height/width ${window.screen.availHeight} ${window.screen.availWidth}`); + const offsetY = 20; switch (preference) { case 'originalSize': { - if (isWindows) { - height = widget.node.clientHeight; - width = widget.node.clientWidth; - left = window.screenLeft + clientBounds.x - 8; - if (environment.electron.is()) { - top = window.screenTop + clientBounds.y; - } else { - top = window.screenTop + clientBounds.y + 48; - } - break; - } height = widget.node.clientHeight; width = widget.node.clientWidth; left = window.screenLeft + clientBounds.x; + top = window.screenTop + (window.outerHeight - window.innerHeight) + offsetY; if (environment.electron.is()) { top = window.screenTop + clientBounds.y; - } else { - top = window.screenTop + clientBounds.y + 60; } break; } - case 'splitScreen': { - height = window.screen.availHeight; - width = window.screen.availWidth / 2; - left = 0; - top = 0; + case 'halfSize': { + height = window.innerHeight - (window.outerHeight - window.innerHeight); + width = window.innerWidth / 2; + left = window.screenLeft; + top = window.screenTop; + if (!environment.electron.is()) { + height = window.innerHeight + clientBounds.y - offsetY; + } break; } - case 'fullScreen': { - height = window.screen.availHeight; - width = window.screen.availWidth; - left = 0; - top = 0; - // if (isWindows) { - // width = window.screen.availWidth - 30; - // } + case 'fullSize': { + height = window.innerHeight - (window.outerHeight - window.innerHeight); + width = window.innerWidth; + left = window.screenLeft; + top = window.screenTop; + if (!environment.electron.is()) { + height = window.innerHeight + clientBounds.y - offsetY; + } break; } } diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 159faf786ea21..3a67acd71ff1b 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -303,7 +303,6 @@ export class ElectronMainApplication { * @param options */ async createWindow(asyncOptions: MaybePromise = this.getDefaultTheiaWindowOptions()): Promise { - console.log('** ** ** createWindow() - '); let options = await asyncOptions; options = this.avoidOverlap(options); const electronWindow = this.windowFactory(options, this.config); @@ -319,7 +318,6 @@ export class ElectronMainApplication { } async getLastWindowOptions(): Promise { - console.log('*** *** getLastWindowOptions() '); const previousWindowState: TheiaBrowserWindowOptions | undefined = this.electronStore.get('windowstate'); const windowState = previousWindowState?.screenLayout === this.getCurrentScreenLayout() ? previousWindowState @@ -397,16 +395,13 @@ export class ElectronMainApplication { electronWindow.webContents.setWindowOpenHandler(() => { const { minWidth, minHeight } = this.getDefaultOptions(); const options: BrowserWindowConstructorOptions = { - ...this.getDefaultTheiaSecondaryWindowBounds(), + ...this.getDefaultTheiaWindowBounds(), // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. frame: true, minWidth, minHeight - // , - // // TODO: add a preference? - // alwaysOnTop: true }; if (!this.useNativeWindowFrame) { // If the main window does not have a native window frame, do not show an icon in the secondary window's native title bar. @@ -457,7 +452,6 @@ export class ElectronMainApplication { } protected getDefaultTheiaWindowOptions(): TheiaBrowserWindowOptions { - console.log('*** getDefaultTheiaWindowOptions() '); return { frame: this.useNativeWindowFrame, isFullScreen: false, @@ -477,7 +471,6 @@ export class ElectronMainApplication { const width = Math.round(bounds.width * (2 / 3)); const y = Math.round(bounds.y + (bounds.height - height) / 2); const x = Math.round(bounds.x + (bounds.width - width) / 2); - console.log(`*** getDefaultTheiaWindowBounds(): { width=${width}, height=${height}, x=${x}}, y=${y} }`); return { width, height, @@ -486,10 +479,6 @@ export class ElectronMainApplication { }; } - protected getDefaultTheiaSecondaryWindowBounds(): TheiaBrowserWindowOptions { - return {}; - } - /** * Save the window geometry state on every change. */ @@ -513,7 +502,6 @@ export class ElectronMainApplication { } protected saveWindowState(electronWindow: BrowserWindow): void { - console.log('*** *** saveWindowState() '); // In some circumstances the `electronWindow` can be `null` if (!electronWindow) { return; From 9e37cfaf74b38baa2a14ee3e49bc88c24b9c49bc Mon Sep 17 00:00:00 2001 From: Vlad Arama Date: Wed, 20 Dec 2023 14:42:24 -0500 Subject: [PATCH 6/7] add default bounds Signed-off-by: Vlad Arama --- .../core/src/electron-main/electron-main-application.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/core/src/electron-main/electron-main-application.ts b/packages/core/src/electron-main/electron-main-application.ts index 3a67acd71ff1b..bd1b3c077f625 100644 --- a/packages/core/src/electron-main/electron-main-application.ts +++ b/packages/core/src/electron-main/electron-main-application.ts @@ -395,7 +395,7 @@ export class ElectronMainApplication { electronWindow.webContents.setWindowOpenHandler(() => { const { minWidth, minHeight } = this.getDefaultOptions(); const options: BrowserWindowConstructorOptions = { - ...this.getDefaultTheiaWindowBounds(), + ...this.getDefaultTheiaSecondaryWindowBounds(), // We always need the native window frame for now because the secondary window does not have Theia's title bar by default. // In 'custom' title bar mode this would leave the window without any window controls (close, min, max) // TODO set to this.useNativeWindowFrame when secondary windows support a custom title bar. @@ -461,6 +461,10 @@ export class ElectronMainApplication { }; } + protected getDefaultTheiaSecondaryWindowBounds(): TheiaBrowserWindowOptions { + return {}; + } + protected getDefaultTheiaWindowBounds(): TheiaBrowserWindowOptions { // The `screen` API must be required when the application is ready. // See: https://electronjs.org/docs/api/screen#screen From 733f2b1a108e35db8f1877ef2713d70cf724281f Mon Sep 17 00:00:00 2001 From: Vlad Arama Date: Thu, 21 Dec 2023 08:31:48 -0500 Subject: [PATCH 7/7] address review comments This commit does the following changes: - rename `halfSize` to `halfWidth` - set `alwaysOnTop` to false by default Signed-off-by: Vlad Arama --- packages/core/src/browser/core-preferences.ts | 8 ++++---- .../browser/window/default-secondary-window-service.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/src/browser/core-preferences.ts b/packages/core/src/browser/core-preferences.ts index 51e7e5d0bcfa4..74fc83ffa8105 100644 --- a/packages/core/src/browser/core-preferences.ts +++ b/packages/core/src/browser/core-preferences.ts @@ -121,10 +121,10 @@ export const corePreferenceSchema: PreferenceSchema = { }, 'window.secondaryWindowPlacement': { type: 'string', - enum: ['originalSize', 'halfSize', 'fullSize'], + enum: ['originalSize', 'halfWidth', 'fullSize'], enumDescriptions: [ nls.localize('theia/core/secondaryWindow/originalSize', 'The position and size of the extracted widget will be the same as the original widget.'), - nls.localize('theia/core/secondaryWindow/halfSize', 'The position and size of the extracted widget will be half the size of the running Theia application.'), + nls.localize('theia/core/secondaryWindow/halfWidth', 'The position and size of the extracted widget will be half the width of the running Theia application.'), nls.localize('theia/core/secondaryWindow/fullSize', 'The position and size of the extracted widget will be the same as the running Theia application.'), ], default: 'originalSize', @@ -132,8 +132,8 @@ export const corePreferenceSchema: PreferenceSchema = { }, 'window.secondaryWindowAlwaysOnTop': { type: 'boolean', - default: 'true', - description: nls.localize('theia/core/secondaryWindow/alwaysOnTop', 'The secondary window will always pop-up on top of the current application.'), + default: false, + description: nls.localize('theia/core/secondaryWindow/alwaysOnTop', 'When enabled, the secondary window stays above all other windows, including those of different applications.'), }, 'http.proxy': { type: 'string', diff --git a/packages/core/src/browser/window/default-secondary-window-service.ts b/packages/core/src/browser/window/default-secondary-window-service.ts index e573d3622d1f5..c175acfddb78f 100644 --- a/packages/core/src/browser/window/default-secondary-window-service.ts +++ b/packages/core/src/browser/window/default-secondary-window-service.ts @@ -140,7 +140,7 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { const preference = this.preferenceService.get('window.secondaryWindowPlacement'); let height; let width; let left; let top; - const offsetY = 20; + const offsetY = 20; // Offset to avoid the window title bar switch (preference) { case 'originalSize': { @@ -153,7 +153,7 @@ export class DefaultSecondaryWindowService implements SecondaryWindowService { } break; } - case 'halfSize': { + case 'halfWidth': { height = window.innerHeight - (window.outerHeight - window.innerHeight); width = window.innerWidth / 2; left = window.screenLeft;