diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 0647066e459409..316ec726dac945 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -56,6 +56,38 @@ export interface INativeHostOptions { readonly targetWindowId?: number; } +/** + * The amount to grow or shrink a window by in pixels. + */ +export interface IWindowResizeDelta { + readonly width: number; + readonly height: number; +} + +/** + * The edges to keep fixed in place while resizing a window. When `right` is + * `true`, the window grows/shrinks to the left, otherwise the left edge stays + * fixed. When `bottom` is `true`, the window grows/shrinks upwards, otherwise + * the top edge stays fixed. + */ +export interface IWindowResizeAnchor { + readonly right: boolean; + readonly bottom: boolean; +} + +/** + * Computes new window bounds by applying the `delta` to `bounds` while keeping + * the edges indicated by `anchor` fixed in place. + */ +export function getResizedWindowBounds(bounds: IRectangle, delta: IWindowResizeDelta, anchor: IWindowResizeAnchor): IRectangle { + const width = bounds.width + delta.width; + const height = bounds.height + delta.height; + const x = anchor.right ? bounds.x + bounds.width - width : bounds.x; + const y = anchor.bottom ? bounds.y + bounds.height - height : bounds.y; + + return { x, y, width, height }; +} + export interface IStartTracingOptions { /** @@ -153,6 +185,17 @@ export interface ICommonNativeHostService { moveWindowTop(options?: INativeHostOptions): Promise; positionWindow(position: IRectangle, options?: INativeHostOptions): Promise; + /** + * Resizes the window by the delta, keeping the edges as indicated by + * the anchor fixed in place. Has no effect when the window is maximized or in + * full screen. + * + * @param delta The amount to grow or shrink the window in pixels. + * @param anchor The edges to keep fixed while resizing. + * @param options Options to target a specific window. + */ + resizeWindow(delta: IWindowResizeDelta, anchor: IWindowResizeAnchor, options?: INativeHostOptions): Promise; + isWindowAlwaysOnTop(options?: INativeHostOptions): Promise; toggleWindowAlwaysOnTop(options?: INativeHostOptions): Promise; setWindowAlwaysOnTop(alwaysOnTop: boolean, options?: INativeHostOptions): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 4135c6d145a972..f660b8b9f0fd63 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -27,7 +27,7 @@ import { IEnvironmentMainService } from '../../environment/electron-main/environ import { createDecorator, IInstantiationService } from '../../instantiation/common/instantiation.js'; import { ILifecycleMainService, IRelaunchOptions } from '../../lifecycle/electron-main/lifecycleMainService.js'; import { ILogService } from '../../log/common/log.js'; -import { FocusMode, ICommonNativeHostService, INativeHostOptions, IOSProperties, IOSStatistics, IStartTracingOptions, IToastOptions, IToastResult, PowerSaveBlockerType, SystemIdleState, ThermalState } from '../common/native.js'; +import { FocusMode, ICommonNativeHostService, INativeHostOptions, IOSProperties, IOSStatistics, IStartTracingOptions, IToastOptions, IToastResult, IWindowResizeAnchor, IWindowResizeDelta, getResizedWindowBounds, PowerSaveBlockerType, SystemIdleState, ThermalState } from '../common/native.js'; import { IProductService } from '../../product/common/productService.js'; import { IPartsSplash } from '../../theme/common/themeService.js'; import { IThemeMainService } from '../../theme/electron-main/themeMainService.js'; @@ -385,6 +385,24 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } } + async resizeWindow(windowId: number | undefined, delta: IWindowResizeDelta, anchor: IWindowResizeAnchor, options?: INativeHostOptions): Promise { + const window = this.windowById(options?.targetWindowId, windowId); + if (!window?.win || window.win.isFullScreen() || window.win.isMaximized()) { + return; + } + + const currentBounds = window.win.getBounds(); + const [minWidth, minHeight] = window.win.getMinimumSize(); + + const desiredBounds = getResizedWindowBounds(currentBounds, delta, anchor); + desiredBounds.width = Math.max(desiredBounds.width, minWidth); + desiredBounds.height = Math.max(desiredBounds.height, minHeight); + desiredBounds.x = anchor.right ? currentBounds.x + currentBounds.width - desiredBounds.width : currentBounds.x; + desiredBounds.y = anchor.bottom ? currentBounds.y + currentBounds.height - desiredBounds.height : currentBounds.y; + + window.win.setBounds(desiredBounds); + } + async updateWindowControls(windowId: number | undefined, options: INativeHostOptions & { height?: number; backgroundColor?: string; foregroundColor?: string; dimmed?: boolean }): Promise { const window = this.windowById(options?.targetWindowId, windowId); window?.updateWindowControls(options); diff --git a/src/vs/platform/native/test/common/native.test.ts b/src/vs/platform/native/test/common/native.test.ts new file mode 100644 index 00000000000000..6f230161aa6d56 --- /dev/null +++ b/src/vs/platform/native/test/common/native.test.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { getResizedWindowBounds } from '../../common/native.js'; + +suite('getResizedWindowBounds', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + + const bounds = { x: 100, y: 200, width: 800, height: 600 }; + + test('left/top anchor keeps the origin fixed when growing', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: 50, height: 30 }, { right: false, bottom: false }), + { x: 100, y: 200, width: 850, height: 630 } + ); + }); + + test('right anchor keeps the right edge fixed when growing', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: 50, height: 0 }, { right: true, bottom: false }), + { x: 50, y: 200, width: 850, height: 600 } + ); + }); + + test('bottom anchor keeps the bottom edge fixed when growing', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: 0, height: 30 }, { right: false, bottom: true }), + { x: 100, y: 170, width: 800, height: 630 } + ); + }); + + test('right and bottom anchor keeps the bottom-right corner fixed when growing', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: 50, height: 30 }, { right: true, bottom: true }), + { x: 50, y: 170, width: 850, height: 630 } + ); + }); + + test('negative delta shrinks the window toward the anchored edge', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: -50, height: -30 }, { right: true, bottom: true }), + { x: 150, y: 230, width: 750, height: 570 } + ); + }); + + test('zero delta leaves the bounds unchanged', () => { + assert.deepStrictEqual( + getResizedWindowBounds(bounds, { width: 0, height: 0 }, { right: true, bottom: true }), + { x: 100, y: 200, width: 800, height: 600 } + ); + }); +}); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 89d7fd156d11b1..a13a86d513c5c9 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -7,7 +7,7 @@ import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } import { Event, Emitter } from '../../base/common/event.js'; import { EventType, addDisposableListener, getClientArea, size, IDimension, isAncestorUsingFlowTo, computeScreenAwareSize, getActiveDocument, getWindows, getActiveWindow, isActiveDocument, getWindow, getWindowId, getActiveElement, Dimension } from '../../base/browser/dom.js'; import { onDidChangeFullscreen, isFullscreen, isWCOEnabled } from '../../base/browser/browser.js'; -import { isWindows, isLinux, isMacintosh, isWeb, isIOS } from '../../base/common/platform.js'; +import { isWindows, isLinux, isMacintosh, isWeb, isIOS, isNative } from '../../base/common/platform.js'; import { EditorInputCapabilities, GroupIdentifier, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from '../common/editor.js'; import { SidebarPart } from './parts/sidebar/sidebarPart.js'; import { PanelPart } from './parts/panel/panelPart.js'; @@ -40,7 +40,7 @@ import { DiffEditorInput } from '../common/editor/diffEditorInput.js'; import { mark } from '../../base/common/performance.js'; import { IExtensionService } from '../services/extensions/common/extensions.js'; import { ILogService } from '../../platform/log/common/log.js'; -import { DeferredPromise, Promises } from '../../base/common/async.js'; +import { DeferredPromise, Promises, raceTimeout } from '../../base/common/async.js'; import { IBannerService } from '../services/banner/browser/bannerService.js'; import { IPaneCompositePartService } from '../services/panecomposite/browser/panecomposite.js'; import { AuxiliaryBarPart } from './parts/auxiliarybar/auxiliaryBarPart.js'; @@ -69,6 +69,12 @@ interface IEditorToOpen { readonly viewColumn?: number; } +interface IPartToggleWindowResize { + readonly editorSize: IViewSize; + readonly delta: { readonly width: number; readonly height: number }; + readonly anchor: { readonly right: boolean; readonly bottom: boolean }; +} + interface ILayoutInitializationState { readonly views: { readonly defaults: string[] | undefined; @@ -1475,7 +1481,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setPanelHidden(true, true); this.setAuxiliaryBarHidden(true, true); - this.setSideBarHidden(true); + this.setSideBarHidden(true, true); if (config.hideActivityBar) { this.setActivityBarHidden(true); @@ -1558,7 +1564,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } if (zenModeExitInfo.wasVisible.sideBar) { - this.setSideBarHidden(false); + this.setSideBarHidden(false, true); } if (!this.stateModel.getRuntimeValue(LayoutStateKeys.ACTIVITYBAR_HIDDEN, true)) { @@ -1673,7 +1679,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // visibility efficiently. if (part === sideBar) { - this.setSideBarHidden(!visible); + this.setSideBarHidden(!visible, true); } else if (part === panelPart && this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) === visible) { this.setPanelHidden(!visible, true); } else if (part === auxiliaryBarPart) { @@ -1906,13 +1912,19 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi ]); } - private setSideBarHidden(hidden: boolean): void { + private setSideBarHidden(hidden: boolean, skipLayout?: boolean): void { if (!hidden && this.setAuxiliaryBarMaximized(false) && this.isVisible(Parts.SIDEBAR_PART)) { return; // return: leaving maximised auxiliary bar made this part visible } + const wasHidden = !this.isVisible(Parts.SIDEBAR_PART); + this.stateModel.setRuntimeValue(LayoutStateKeys.SIDEBAR_HIDDEN, hidden); + // Optionally resize the window so that the editor keeps its size when the + // primary side bar is shown or hidden (instead of the editor making room for it) + const sideBarToggleResize = this.getPartToggleWindowResize(this.sideBarPartView, this.getSideBarPosition(), hidden, wasHidden, skipLayout, false); + // Adjust CSS if (hidden) { this.mainContainer.classList.add(LayoutClasses.SIDEBAR_HIDDEN); @@ -1939,6 +1951,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.openViewContainer(ViewContainerLocation.Sidebar, viewletToOpen); } } + + // Apply the window resize that keeps the editor size stable (no-op on web) + if (sideBarToggleResize) { + void this.resizeWindowToKeepEditorSize(sideBarToggleResize); + } } private hasViews(id: string): boolean { @@ -2057,6 +2074,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const panelOpensMaximized = this.panelOpensMaximized(); + // Optionally resize the window so that the editor keeps its size when + // the panel is shown or hidden (instead of the editor making room for it) + const panelToggleResize = this.getPartToggleWindowResize(this.panelPartView, this.getPanelPosition(), hidden, wasHidden, skipLayout, hidden ? isPanelMaximized : panelOpensMaximized); + // Adjust CSS if (hidden) { this.mainContainer.classList.add(LayoutClasses.PANEL_HIDDEN); @@ -2121,6 +2142,87 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (focusEditor) { this.editorGroupService.mainPart.activeGroup.focus(); // Pass focus to editor group if panel part is now hidden } + + // Apply the window resize that keeps the editor size stable (no-op on web) + if (panelToggleResize) { + void this.resizeWindowToKeepEditorSize(panelToggleResize); + } + } + + private getPartToggleWindowResize(partView: ISerializableView, position: Position, hidden: boolean, wasHidden: boolean, skipLayout: boolean | undefined, willBeMaximized: boolean): IPartToggleWindowResize | undefined { + const visibilityChanged = wasHidden !== hidden; + const keepEditorSizeEnabled = this.configurationService.getValue(WorkbenchLayoutSettings.KEEP_EDITOR_SIZE_ON_TOGGLE); + const editorVisible = this.isVisible(Parts.EDITOR_PART, mainWindow); + + // Window resizing is only available on desktop and only when the window is + // free to grow or shrink (not maximized or in full screen) + const canResizeWindow = isNative && !this.state.runtime.mainWindowFullscreen && !this.isWindowMaximized(mainWindow); + + if ( + skipLayout || // Skip programmatic visibility changes + willBeMaximized || // Editor is not visible when the part is maximized + this.inMaximizedAuxiliaryBarTransition || // Transition resizes all parts at once + !visibilityChanged || + !keepEditorSizeEnabled || + !canResizeWindow || + !editorVisible + ) { + return undefined; + } + + const horizontal = isHorizontal(position); + + // When hiding, the part is still visible in the grid, so use its current size + // When showing, the part is still hidden, so fall back to its last visible size + let partSizeAlongAxis: number; + if (hidden) { + const partSize = this.workbenchGrid.getViewSize(partView); + partSizeAlongAxis = horizontal ? partSize.height : partSize.width; + } else { + partSizeAlongAxis = this.workbenchGrid.getViewCachedVisibleSize(partView) ?? (horizontal ? partView.minimumHeight : partView.minimumWidth); + } + + if (partSizeAlongAxis <= 0) { + return undefined; + } + + const delta = hidden ? -partSizeAlongAxis : partSizeAlongAxis; + + return { + editorSize: this.workbenchGrid.getViewSize(this.editorPartView), + delta: { width: horizontal ? 0 : delta, height: horizontal ? delta : 0 }, + anchor: { right: position === Position.LEFT, bottom: position === Position.TOP } + }; + } + + private resizeWindowToKeepEditorSizeSeq = 0; + + private async resizeWindowToKeepEditorSize(resize: IPartToggleWindowResize): Promise { + const seq = ++this.resizeWindowToKeepEditorSizeSeq; + + try { + const beforeWidth = this._mainContainerDimension.width; + const beforeHeight = this._mainContainerDimension.height; + + const relayoutPromise = Event.toPromise(Event.once(Event.filter(this.onDidLayoutMainContainer, d => d.width !== beforeWidth || d.height !== beforeHeight))); + + await this.hostService.resizeMainWindow(resize.delta, resize.anchor); + + const didRelayout = await raceTimeout(relayoutPromise, 1000); + + if (!didRelayout) { + return; + } + } catch (error) { + this.logService.warn('[layout] resizeWindowToKeepEditorSize failed', error); + return; + } + + if (this.disposed || !this.workbenchGrid || seq !== this.resizeWindowToKeepEditorSizeSeq) { + return; + } + + this.workbenchGrid.resizeView(this.editorPartView, { width: resize.editorSize.width, height: resize.editorSize.height }); } private inMaximizedAuxiliaryBarTransition = false; @@ -2249,8 +2351,15 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi return; // return: leaving maximised auxiliary bar made this part hidden } + const wasHidden = !this.isVisible(Parts.AUXILIARYBAR_PART); + this.stateModel.setRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN, hidden); + // Optionally resize the window so that the editor keeps its size when the + // secondary side bar is shown or hidden (instead of the editor making room for it). + // The secondary side bar is always on the opposite side of the primary side bar. + const auxiliaryBarToggleResize = this.getPartToggleWindowResize(this.auxiliaryBarPartView, this.getSideBarPosition() === Position.LEFT ? Position.RIGHT : Position.LEFT, hidden, wasHidden, skipLayout, false); + // Adjust CSS if (hidden) { this.mainContainer.classList.add(LayoutClasses.AUXILIARYBAR_HIDDEN); @@ -2283,6 +2392,11 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.openViewContainer(ViewContainerLocation.AuxiliaryBar, viewletToOpen, !skipLayout); } } + + // Apply the window resize that keeps the editor size stable (no-op on web) + if (auxiliaryBarToggleResize) { + void this.resizeWindowToKeepEditorSize(auxiliaryBarToggleResize); + } } setPartHidden(hidden: boolean, part: Parts): void { @@ -2860,6 +2974,7 @@ enum WorkbenchLayoutSettings { ACTIVITY_BAR_VISIBLE = 'workbench.activityBar.visible', PANEL_POSITION = 'workbench.panel.defaultLocation', PANEL_OPENS_MAXIMIZED = 'workbench.panel.opensMaximized', + KEEP_EDITOR_SIZE_ON_TOGGLE = 'workbench.keepEditorSizeOnToggle', ZEN_MODE_CONFIG = 'zenMode', EDITOR_CENTERED_LAYOUT_AUTO_RESIZE = 'workbench.editor.centeredLayoutAutoResize', EDITOR_RESTORE_EDITORS = 'workbench.editor.restoreEditors', diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 9e631577682eda..7c251e5a7742c3 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -594,6 +594,12 @@ const registry = Registry.as(ConfigurationExtensions.Con ], agentsWindow: { default: 'never', readOnly: true }, }, + 'workbench.keepEditorSizeOnToggle': { + 'type': 'boolean', + 'default': false, + 'description': localize('keepEditorSizeOnToggle', "Controls whether showing or hiding the panel, primary side bar, or secondary side bar resizes the window such that the editor keeps its size, instead of changing the editor size to make room for it. Only applies on desktop when the window is not maximized or in full screen."), + 'included': !isWeb, + }, 'workbench.secondarySideBar.defaultVisibility': { 'type': 'string', 'enum': ['hidden', 'visibleInWorkspace', 'visible', 'maximizedInWorkspace', 'maximized'], diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 3815713423ed81..23407a6082d566 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -5,6 +5,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { IHostService, IToastOptions, IToastResult } from './host.js'; +import { IWindowResizeAnchor, IWindowResizeDelta } from '../../../../platform/native/common/native.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js'; import { IEditorService } from '../../editor/common/editorService.js'; @@ -581,6 +582,10 @@ export class BrowserHostService extends Disposable implements IHostService { // not supported in browser } + async resizeMainWindow(_delta: IWindowResizeDelta, _anchor: IWindowResizeAnchor): Promise { + // not supported in browser + } + async getCursorScreenPoint(): Promise { return undefined; } diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index a43eb767fbf440..0da34a49d95a68 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -7,7 +7,7 @@ import { VSBuffer } from '../../../../base/common/buffer.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Event } from '../../../../base/common/event.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { FocusMode } from '../../../../platform/native/common/native.js'; +import { FocusMode, IWindowResizeAnchor, IWindowResizeDelta } from '../../../../platform/native/common/native.js'; import { IWindowOpenable, IOpenWindowOptions, IOpenEmptyWindowOptions, IPoint, IRectangle, IOpenedMainWindow, IOpenedAuxiliaryWindow } from '../../../../platform/window/common/window.js'; export const IHostService = createDecorator('hostService'); @@ -111,6 +111,13 @@ export interface IHostService { */ setWindowDimmed(targetWindow: Window, dimmed: boolean): Promise; + /** + * Resizes the window by the delta, keeping the edges as indicated by + * the anchor fixed in place. Has no effect when the window is maximized or in + * full screen. + */ + resizeMainWindow(delta: IWindowResizeDelta, anchor: IWindowResizeAnchor): Promise; + /** * Get the location of the mouse cursor and its display bounds or `undefined` if unavailable. */ diff --git a/src/vs/workbench/services/host/electron-browser/nativeHostService.ts b/src/vs/workbench/services/host/electron-browser/nativeHostService.ts index 86a3d521e1bef4..ad3736a0fbb953 100644 --- a/src/vs/workbench/services/host/electron-browser/nativeHostService.ts +++ b/src/vs/workbench/services/host/electron-browser/nativeHostService.ts @@ -5,7 +5,7 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { IHostService, IToastOptions, IToastResult } from '../browser/host.js'; -import { FocusMode, INativeHostService } from '../../../../platform/native/common/native.js'; +import { FocusMode, INativeHostService, IWindowResizeAnchor, IWindowResizeDelta } from '../../../../platform/native/common/native.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { ILabelService, Verbosity } from '../../../../platform/label/common/label.js'; import { IWorkbenchEnvironmentService } from '../../environment/common/environmentService.js'; @@ -177,6 +177,10 @@ class WorkbenchHostService extends Disposable implements IHostService { return this.nativeHostService.updateWindowControls({ dimmed, targetWindowId: getWindowId(targetWindow) }); } + resizeMainWindow(delta: IWindowResizeDelta, anchor: IWindowResizeAnchor): Promise { + return this.nativeHostService.resizeWindow(delta, anchor); + } + getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }> { return this.nativeHostService.getCursorScreenPoint(); } diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index b4277f16b2aae6..04d6c7dfc902e6 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -163,6 +163,7 @@ import { IElevatedFileService } from '../../services/files/common/elevatedFileSe import { FilesConfigurationService, IFilesConfigurationService } from '../../services/filesConfiguration/common/filesConfigurationService.js'; import { IHistoryService } from '../../services/history/common/history.js'; import { IHostService, IToastOptions, IToastResult } from '../../services/host/browser/host.js'; +import { IWindowResizeAnchor, IWindowResizeDelta } from '../../../platform/native/common/native.js'; import { LabelService } from '../../services/label/common/labelService.js'; import { ILanguageDetectionService } from '../../services/languageDetection/common/languageDetectionWorkerService.js'; import { IPartVisibilityChangeEvent, IWorkbenchLayoutService, PanelAlignment, Position as PartPosition, Parts, SINGLE_WINDOW_PARTS } from '../../services/layout/browser/layoutService.js'; @@ -1384,6 +1385,8 @@ export class TestHostService implements IHostService { async setWindowDimmed(_targetWindow: Window, _dimmed: boolean): Promise { } + async resizeMainWindow(_delta: IWindowResizeDelta, _anchor: IWindowResizeAnchor): Promise { } + readonly colorScheme = ColorScheme.DARK; onDidChangeColorScheme = Event.None; } diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index 1ca6e7bf478781..d5b2dba72fd994 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -25,7 +25,7 @@ import { InMemoryFileSystemProvider } from '../../../platform/files/common/inMem import { IInstantiationService } from '../../../platform/instantiation/common/instantiation.js'; import { ISharedProcessService } from '../../../platform/ipc/electron-browser/services.js'; import { NullLogService } from '../../../platform/log/common/log.js'; -import { INativeHostOptions, INativeHostService, IOSProperties, IOSStatistics, IToastOptions, IToastResult, PowerSaveBlockerType, SystemIdleState, ThermalState } from '../../../platform/native/common/native.js'; +import { INativeHostOptions, INativeHostService, IOSProperties, IOSStatistics, IToastOptions, IToastResult, IWindowResizeAnchor, IWindowResizeDelta, PowerSaveBlockerType, SystemIdleState, ThermalState } from '../../../platform/native/common/native.js'; import { IProductService } from '../../../platform/product/common/productService.js'; import { AuthInfo, Credentials } from '../../../platform/request/common/request.js'; import { IStorageService } from '../../../platform/storage/common/storage.js'; @@ -117,6 +117,7 @@ export class TestNativeHostService implements INativeHostService { async setWindowAlwaysOnTop(alwaysOnTop: boolean, options?: INativeHostOptions): Promise { } async getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }> { throw new Error('Method not implemented.'); } async positionWindow(position: IRectangle, options?: INativeHostOptions): Promise { } + async resizeWindow(delta: IWindowResizeDelta, anchor: IWindowResizeAnchor, options?: INativeHostOptions): Promise { } async updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): Promise { } async updateWindowAccentColor(color: string): Promise { } async setMinimumSize(width: number | undefined, height: number | undefined): Promise { }