From 464e6b16160944d6dd49ad4b0090ea30d167219e Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 16:31:54 +0200 Subject: [PATCH 1/6] show workspace title inside "input like button", move other quick pick options into normal dropdown menu --- .../browser/actions/quickAccessActions.ts | 7 +++-- .../parts/titlebar/media/titlebarpart.css | 24 +++++++--------- .../browser/parts/titlebar/titlebarPart.ts | 28 +++++++++++-------- .../browser/parts/titlebar/windowTitle.ts | 4 +++ 4 files changed, 36 insertions(+), 27 deletions(-) diff --git a/src/vs/workbench/browser/actions/quickAccessActions.ts b/src/vs/workbench/browser/actions/quickAccessActions.ts index f3f41c78e1d..1eff30cf896 100644 --- a/src/vs/workbench/browser/actions/quickAccessActions.ts +++ b/src/vs/workbench/browser/actions/quickAccessActions.ts @@ -140,11 +140,14 @@ registerAction2(class QuickAccessAction extends Action2 { mac: globalQuickAccessKeybinding.mac }, f1: true, - menu: { + menu: [{ id: MenuId.TitleMenuQuickPick, group: '1/workspaceNav', order: 1 - } + }, { + id: MenuId.TitleMenu, + order: 100 + }] }); } diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 5949b5b88c3..5855d9214e1 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -86,10 +86,12 @@ } .monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen { + display: flex; color: var(--vscode-input-foreground); border: 1px solid var(--vscode-dropdown-border); + border-radius: 5px; height: 20px; - line-height: 20px; + line-height: 18px; width: 38vw; max-width: 600px; margin: 4px 4px; @@ -98,25 +100,19 @@ overflow: hidden; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen:HOVER, -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-container .action-label:HOVER { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen:HOVER { background-color: var(--vscode-dropdown-border); line-height: 18px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-container { - flex: 1 0 auto; - display: flex; - justify-content: center; +.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-label { + display: inline-block; + width: 100%; + text-align: center; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-container .keybinding { - font-size: 11px; - padding: 3px; -} - -.monaco-workbench .part.titlebar>.titlebar-container>.title-menu .action-item.quickopen .dropdown-action-container { - margin-left: auto; +.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-label:HOVER { + background-color: var(--vscode-dropdown-border); } /* Menubar */ diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index e779741e0ac..f9ca787eb2c 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -25,8 +25,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { Emitter } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { createActionViewItem, createAndFillInContextMenuActions, DropdownWithDefaultActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, IMenu, MenuId, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { createActionViewItem, createAndFillInContextMenuActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, IMenu, MenuId, MenuRegistry, MenuItemAction } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Codicon } from 'vs/base/common/codicons'; @@ -191,21 +191,26 @@ export class TitlebarPart extends Part implements ITitleService { const titleToolbar = new ToolBar(this.title, this.contextMenuService, { actionViewItemProvider: (action) => { - if (action instanceof SubmenuItemAction && action.item.submenu === MenuId.TitleMenuQuickPick) { - class QuickInputDropDown extends DropdownWithDefaultActionViewItem { + if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpen') { + + class InputLikeViewItem extends MenuEntryActionViewItem { override render(container: HTMLElement): void { super.render(container); container.classList.add('quickopen'); - container.title = that.windowTitle.value; - this._store.add(that.windowTitle.onDidChange(() => container.title = that.windowTitle.value)); + this._store.add(that.windowTitle.onDidChange(this._updateFromWindowTitle, this)); + this._updateFromWindowTitle(); + } + private _updateFromWindowTitle() { + if (this.label) { + this.label.innerText = localize('search', "Search {0}", that.windowTitle.workspaceName); + this.label.title = that.windowTitle.value; + } } } - return that.instantiationService.createInstance(QuickInputDropDown, action, { - keybindingProvider: action => that.keybindingService.lookupKeybinding(action.id), - renderKeybindingWithDefaultActionLabel: true - }); + return that.instantiationService.createInstance(InputLikeViewItem, action, undefined); } - return undefined; + + return createActionViewItem(this.instantiationService, action); } }); const titleMenu = this.titleDisposables.add(this.menuService.createMenu(MenuId.TitleMenu, this.contextKeyService)); @@ -497,5 +502,6 @@ registerThemingParticipant((theme, collector) => { MenuRegistry.appendMenuItem(MenuId.TitleMenu, { submenu: MenuId.TitleMenuQuickPick, title: localize('title', "Select Mode"), + icon: Codicon.search, order: Number.MAX_SAFE_INTEGER }); diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index 3a4d781cf55..cdae94ad277 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -58,6 +58,10 @@ export class WindowTitle extends Disposable { return this.title ?? ''; } + get workspaceName() { + return this.labelService.getWorkspaceLabel(this.contextService.getWorkspace()); + } + private registerListeners(): void { this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e))); this._register(this.editorService.onDidActiveEditorChange(() => this.onActiveEditorChange())); From bd60885c5b9c8d712f2bab827150b6694f460a34 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 16:35:35 +0200 Subject: [PATCH 2/6] cleanup: no more need for keybindings service --- src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index f9ca787eb2c..94bc84ff67e 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -32,7 +32,6 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Codicon } from 'vs/base/common/codicons'; import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; export class TitlebarPart extends Part implements ITitleService { @@ -83,7 +82,6 @@ export class TitlebarPart extends Part implements ITitleService { @IMenuService private readonly menuService: IMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IHostService private readonly hostService: IHostService, - @IKeybindingService private readonly keybindingService: IKeybindingService, ) { super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); this.windowTitle = this._register(instantiationService.createInstance(WindowTitle)); @@ -222,7 +220,6 @@ export class TitlebarPart extends Part implements ITitleService { titleToolbar.setActions(actions); }; this.titleDisposables.add(titleMenu.onDidChange(updateTitleMenu)); - this.titleDisposables.add(this.keybindingService.onDidUpdateKeybindings(updateTitleMenu)); this.titleDisposables.add(toDisposable(() => clearNode(this.title))); updateTitleMenu(); } From 7f3b00083363118d27d79b68364feae6949e8333 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 17:50:15 +0200 Subject: [PATCH 3/6] * make `ILayoutService#offset` return general `top` and specific `quickPickTop` * improve ergonomics of `ILayoutService#offset` * Use new `quickPickTop` to overlay title bar part when title menu is enabled * hide title menu whenever quick pick shows to avoid unwanted overlap * extract title menu into its own file --- .../quickinput/browser/media/quickInput.css | 2 +- .../quickInput/standaloneQuickInputService.ts | 3 +- .../browser/standaloneLayoutService.ts | 5 +- .../platform/layout/browser/layoutService.ts | 13 ++- .../platform/quickinput/browser/quickInput.ts | 4 +- src/vs/workbench/browser/layout.ts | 20 ++--- .../parts/titlebar/media/titlebarpart.css | 14 ++-- .../parts/titlebar/titleMenuControl.ts | 83 +++++++++++++++++++ .../browser/parts/titlebar/titlebarPart.ts | 78 +++++------------ .../contrib/debug/browser/debugToolBar.ts | 6 +- .../welcomeOverlay/browser/welcomeOverlay.ts | 2 +- .../parts/titlebar/titlebarPart.ts | 4 +- .../services/title/common/titleService.ts | 10 +++ 13 files changed, 160 insertions(+), 84 deletions(-) create mode 100644 src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index c448eee320b..802cc6adfe7 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -6,7 +6,7 @@ .quick-input-widget { position: absolute; width: 600px; - z-index: 2000; + z-index: 4000; padding: 0 1px 1px 1px; left: 50%; margin-left: -300px; diff --git a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts index f19d0f662f5..ee8c43af99f 100644 --- a/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts +++ b/src/vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts @@ -44,7 +44,8 @@ export class EditorScopedQuickInputService extends QuickInputService { get container() { return widget.getDomNode(); }, get dimension() { return editor.getLayoutInfo(); }, get onDidLayout() { return editor.onDidLayoutChange; }, - focus: () => editor.focus() + focus: () => editor.focus(), + offset: { top: 0, quickPickTop: 0 } }; } else { this.host = undefined; diff --git a/src/vs/editor/standalone/browser/standaloneLayoutService.ts b/src/vs/editor/standalone/browser/standaloneLayoutService.ts index 6ded16a8be7..f49cf6d3787 100644 --- a/src/vs/editor/standalone/browser/standaloneLayoutService.ts +++ b/src/vs/editor/standalone/browser/standaloneLayoutService.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; +import { ILayoutService, ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -40,9 +40,12 @@ class StandaloneLayoutService implements ILayoutService { this._codeEditorService.getFocusedCodeEditor()?.focus(); } + readonly offset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; + constructor( @ICodeEditorService private _codeEditorService: ICodeEditorService ) { } + } export class EditorScopedLayoutService extends StandaloneLayoutService { diff --git a/src/vs/platform/layout/browser/layoutService.ts b/src/vs/platform/layout/browser/layoutService.ts index 6b6575e27b0..73c42d38282 100644 --- a/src/vs/platform/layout/browser/layoutService.ts +++ b/src/vs/platform/layout/browser/layoutService.ts @@ -9,6 +9,17 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' export const ILayoutService = createDecorator('layoutService'); +export interface ILayoutOffsetInfo { + /** + * Generic top offset + */ + readonly top: number; + /** + * Quick pick specific top offset. + */ + readonly quickPickTop: number; +} + export interface ILayoutService { readonly _serviceBrand: undefined; @@ -40,7 +51,7 @@ export interface ILayoutService { /** * An offset to use for positioning elements inside the container. */ - readonly offset?: { top: number }; + readonly offset: ILayoutOffsetInfo; /** * An event that is emitted when the container is layed out. The diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 94f688b7c3e..a8f94e4f107 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -84,10 +84,10 @@ export class QuickInputService extends Themable implements IQuickInputService { ...options })); - controller.layout(host.dimension, host.offset?.top ?? 0); + controller.layout(host.dimension, host.offset.quickPickTop); // Layout changes - this._register(host.onDidLayout(dimension => controller.layout(dimension, host.offset?.top ?? 0))); + this._register(host.onDidLayout(dimension => controller.layout(dimension, host.offset.quickPickTop))); // Context keys this._register(controller.onShow(() => this.resetContextKeys())); diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index 88e4d4f892d..acb2fc49198 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -144,16 +144,13 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi get dimension(): IDimension { return this._dimension; } get offset() { - return { - top: (() => { - let offset = 0; - if (this.isVisible(Parts.TITLEBAR_PART)) { - offset = this.getPart(Parts.TITLEBAR_PART).maximumHeight; - } - - return offset; - })() - }; + let top = 0; + let quickPickTop = 0; + if (this.isVisible(Parts.TITLEBAR_PART)) { + top = this.getPart(Parts.TITLEBAR_PART).maximumHeight; + quickPickTop = this.titleService.titleMenuVisible ? 0 : top; + } + return { top, quickPickTop }; } //#endregion @@ -271,6 +268,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this._register(this.titleService.onMenubarVisibilityChange(visible => this.onMenubarToggled(visible))); } + // Title Menu changes + this._register(this.titleService.onDidChangeTitleMenuVisibility(() => this._onDidLayout.fire(this._dimension))); + // Theme changes this._register(this.themeService.onDidColorThemeChange(() => this.updateStyles())); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 5855d9214e1..d338279d230 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -80,12 +80,16 @@ } /* Window Title Menu */ -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu { z-index: 3000; -webkit-app-region: no-drag; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu.hide { + display: none; +} + +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen { display: flex; color: var(--vscode-input-foreground); border: 1px solid var(--vscode-dropdown-border); @@ -100,18 +104,18 @@ overflow: hidden; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen:HOVER { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen:HOVER { background-color: var(--vscode-dropdown-border); line-height: 18px; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-label { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen .action-label { display: inline-block; width: 100%; text-align: center; } -.monaco-workbench .part.titlebar>.titlebar-container>.window-title.title-menu .action-item.quickopen .action-label:HOVER { +.monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen .action-label:HOVER { background-color: var(--vscode-dropdown-border); } diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts new file mode 100644 index 00000000000..7ebdae6aa1b --- /dev/null +++ b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts @@ -0,0 +1,83 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; +import { IAction } from 'vs/base/common/actions'; +import { Codicon } from 'vs/base/common/codicons'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { createActionViewItem, createAndFillInContextMenuActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId, MenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; + +export class TitleMenuControl { + + private readonly _disposables = new DisposableStore(); + + readonly element: HTMLElement = document.createElement('div'); + + constructor( + windowTitle: WindowTitle, + @IContextMenuService contextMenuService: IContextMenuService, + @IContextKeyService contextKeyService: IContextKeyService, + @IInstantiationService instantiationService: IInstantiationService, + @IMenuService menuService: IMenuService, + @IQuickInputService quickInputService: IQuickInputService, + ) { + this.element.classList.add('title-menu'); + const titleToolbar = new ToolBar(this.element, contextMenuService, { + actionViewItemProvider: (action) => { + + if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpen') { + + class InputLikeViewItem extends MenuEntryActionViewItem { + override render(container: HTMLElement): void { + super.render(container); + container.classList.add('quickopen'); + this._store.add(windowTitle.onDidChange(this._updateFromWindowTitle, this)); + this._updateFromWindowTitle(); + } + private _updateFromWindowTitle() { + if (this.label) { + this.label.innerText = localize('search', "Search {0}", windowTitle.workspaceName); + this.label.title = windowTitle.value; + } + } + } + return instantiationService.createInstance(InputLikeViewItem, action, undefined); + } + + return createActionViewItem(instantiationService, action); + } + }); + const titleMenu = this._disposables.add(menuService.createMenu(MenuId.TitleMenu, contextKeyService)); + const titleMenuDisposables = this._disposables.add(new DisposableStore()); + const updateTitleMenu = () => { + titleMenuDisposables.clear(); + const actions: IAction[] = []; + titleMenuDisposables.add(createAndFillInContextMenuActions(titleMenu, undefined, actions)); + titleToolbar.setActions(actions); + }; + updateTitleMenu(); + this._disposables.add(titleMenu.onDidChange(updateTitleMenu)); + this._disposables.add(quickInputService.onShow(() => this.element.classList.toggle('hide', true))); + this._disposables.add(quickInputService.onHide(() => this.element.classList.toggle('hide', false))); + } + + dispose(): void { + this._disposables.dispose(); + } +} + +MenuRegistry.appendMenuItem(MenuId.TitleMenu, { + submenu: MenuId.TitleMenuQuickPick, + title: localize('title', "Quick Pick"), + icon: Codicon.search, + order: Number.MAX_SAFE_INTEGER +}); diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 94bc84ff67e..0a94668c80f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -13,29 +13,34 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { IAction, toAction } from 'vs/base/common/actions'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; -import { DisposableStore, dispose, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IThemeService, registerThemingParticipant, ThemeIcon } from 'vs/platform/theme/common/themeService'; import { TITLE_BAR_ACTIVE_BACKGROUND, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_BACKGROUND, TITLE_BAR_BORDER, WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'; import { Color } from 'vs/base/common/color'; -import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend, clearNode } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend, reset } from 'vs/base/browser/dom'; import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { Emitter } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { createActionViewItem, createAndFillInContextMenuActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, IMenu, MenuId, MenuRegistry, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, IMenu, MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { Codicon } from 'vs/base/common/codicons'; import { getIconRegistry } from 'vs/platform/theme/common/iconRegistry'; import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; +import { TitleMenuControl } from 'vs/workbench/browser/parts/titlebar/titleMenuControl'; export class TitlebarPart extends Part implements ITitleService { + private static readonly configTitleMenu = 'window.experimental.titleMenu'; + + declare readonly _serviceBrand: undefined; + //#region IView readonly minimumWidth: number = 0; @@ -48,7 +53,8 @@ export class TitlebarPart extends Part implements ITitleService { private _onMenubarVisibilityChange = this._register(new Emitter()); readonly onMenubarVisibilityChange = this._onMenubarVisibilityChange.event; - declare readonly _serviceBrand: undefined; + private readonly _onDidChangeTitleMenuVisibility = new Emitter(); + readonly onDidChangeTitleMenuVisibility: Event = this._onDidChangeTitleMenuVisibility.event; protected rootContainer!: HTMLElement; protected windowControls: HTMLElement | undefined; @@ -96,6 +102,10 @@ export class TitlebarPart extends Part implements ITitleService { this.windowTitle.updateProperties(properties); } + get titleMenuVisible() { + return this.configurationService.getValue(TitlebarPart.configTitleMenu); + } + private registerListeners(): void { this._register(this.hostService.onDidChangeFocus(focused => focused ? this.onFocus() : this.onBlur())); this._register(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationChanged(e))); @@ -127,9 +137,10 @@ export class TitlebarPart extends Part implements ITitleService { this.layoutControls.classList.toggle('show-layout-control', this.layoutControlEnabled); } - if (event.affectsConfiguration('window.experimental.titleMenu')) { + if (event.affectsConfiguration(TitlebarPart.configTitleMenu)) { this.updateTitle(); this.adjustTitleMarginToCenter(); + this._onDidChangeTitleMenuVisibility.fire(); } } @@ -172,10 +183,7 @@ export class TitlebarPart extends Part implements ITitleService { private updateTitle(): void { this.titleDisposables.clear(); - const enableTitleMenu = this.configurationService.getValue('window.experimental.titleMenu'); - this.title.classList.toggle('title-menu', enableTitleMenu); - - if (!enableTitleMenu) { + if (!this.titleMenuVisible) { // Text Title this.title.innerText = this.windowTitle.value; this.titleDisposables.add(this.windowTitle.onDidChange(() => { @@ -184,44 +192,9 @@ export class TitlebarPart extends Part implements ITitleService { })); } else { // Menu Title - clearNode(this.title); - const that = this; - const titleToolbar = new ToolBar(this.title, this.contextMenuService, { - actionViewItemProvider: (action) => { - - if (action instanceof MenuItemAction && action.id === 'workbench.action.quickOpen') { - - class InputLikeViewItem extends MenuEntryActionViewItem { - override render(container: HTMLElement): void { - super.render(container); - container.classList.add('quickopen'); - this._store.add(that.windowTitle.onDidChange(this._updateFromWindowTitle, this)); - this._updateFromWindowTitle(); - } - private _updateFromWindowTitle() { - if (this.label) { - this.label.innerText = localize('search', "Search {0}", that.windowTitle.workspaceName); - this.label.title = that.windowTitle.value; - } - } - } - return that.instantiationService.createInstance(InputLikeViewItem, action, undefined); - } - - return createActionViewItem(this.instantiationService, action); - } - }); - const titleMenu = this.titleDisposables.add(this.menuService.createMenu(MenuId.TitleMenu, this.contextKeyService)); - const titleMenuDisposables = this.titleDisposables.add(new DisposableStore()); - const updateTitleMenu = () => { - titleMenuDisposables.clear(); - const actions: IAction[] = []; - titleMenuDisposables.add(createAndFillInContextMenuActions(titleMenu, undefined, actions)); - titleToolbar.setActions(actions); - }; - this.titleDisposables.add(titleMenu.onDidChange(updateTitleMenu)); - this.titleDisposables.add(toDisposable(() => clearNode(this.title))); - updateTitleMenu(); + const titleMenu = this.instantiationService.createInstance(TitleMenuControl, this.windowTitle); + reset(this.title, titleMenu.element); + this.titleDisposables.add(titleMenu); } } @@ -495,10 +468,3 @@ registerThemingParticipant((theme, collector) => { `); } }); - -MenuRegistry.appendMenuItem(MenuId.TitleMenu, { - submenu: MenuId.TitleMenuQuickPick, - title: localize('title', "Select Mode"), - icon: Codicon.search, - order: Number.MAX_SAFE_INTEGER -}); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 13d089466e4..202926d5c34 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -71,7 +71,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { super(themeService); this.$el = dom.$('div.debug-toolbar'); - this.$el.style.top = `${layoutService.offset?.top ?? 0}px`; + this.$el.style.top = `${layoutService.offset.top}px`; this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); @@ -161,7 +161,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { // Prevent default to stop editor selecting text #8524 mouseMoveEvent.preventDefault(); // Reduce x by width of drag handle to reduce jarring #16604 - this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - (this.layoutService.offset?.top ?? 0)); + this.setCoordinates(mouseMoveEvent.posx - 14, mouseMoveEvent.posy - (this.layoutService.offset.top)); }); const mouseUpListener = dom.addDisposableGenericMouseUpListener(window, (e: MouseEvent) => { @@ -207,7 +207,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { } private setYCoordinate(y = this.yCoordinate): void { - const titlebarOffset = this.layoutService.offset?.top ?? 0; + const titlebarOffset = this.layoutService.offset.top; this.$el.style.top = `${titlebarOffset + y}px`; this.yCoordinate = y; } diff --git a/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts b/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts index 4b443bbf63e..f3a0b3d96ac 100644 --- a/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts +++ b/src/vs/workbench/contrib/welcomeOverlay/browser/welcomeOverlay.ts @@ -169,7 +169,7 @@ class WelcomeOverlay extends Disposable { } private create(): void { - const offset = this.layoutService.offset?.top ?? 0; + const offset = this.layoutService.offset.top; this._overlay = dom.append(this.layoutService.container, $('.welcomeOverlay')); this._overlay.style.top = `${offset}px`; this._overlay.style.height = `calc(100% - ${offset}px)`; diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index 351187dc0c0..75a1a5fa6bd 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -21,7 +21,6 @@ import { getTitleBarStyle } from 'vs/platform/window/common/window'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Codicon } from 'vs/base/common/codicons'; import { NativeMenubarControl } from 'vs/workbench/electron-sandbox/parts/titlebar/menubarControl'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; export class TitlebarPart extends BrowserTitleBarPart { private maxRestoreControl: HTMLElement | undefined; @@ -55,9 +54,8 @@ export class TitlebarPart extends BrowserTitleBarPart { @IContextKeyService contextKeyService: IContextKeyService, @IHostService hostService: IHostService, @INativeHostService private readonly nativeHostService: INativeHostService, - @IKeybindingService keybindingService: IKeybindingService, ) { - super(contextMenuService, configurationService, environmentService, instantiationService, themeService, storageService, layoutService, menuService, contextKeyService, hostService, keybindingService); + super(contextMenuService, configurationService, environmentService, instantiationService, themeService, storageService, layoutService, menuService, contextKeyService, hostService); this.environmentService = environmentService; } diff --git a/src/vs/workbench/services/title/common/titleService.ts b/src/vs/workbench/services/title/common/titleService.ts index 74ce02b4566..a89bb2f096a 100644 --- a/src/vs/workbench/services/title/common/titleService.ts +++ b/src/vs/workbench/services/title/common/titleService.ts @@ -23,6 +23,16 @@ export interface ITitleService { */ readonly onMenubarVisibilityChange: Event; + /** + * Title menu is visible + */ + readonly titleMenuVisible: boolean; + + /** + * An event when the title menu is enabled/disabled + */ + readonly onDidChangeTitleMenuVisibility: Event; + /** * Update some environmental title properties. */ From 18269f744d94d272c30bd49cb711557e10c7d8bc Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 19:06:24 +0200 Subject: [PATCH 4/6] define and use theme colors for title menu --- .../parts/titlebar/media/titlebarpart.css | 8 ++-- .../parts/titlebar/titleMenuControl.ts | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index d338279d230..0ba8303e4d4 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -91,8 +91,8 @@ .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen { display: flex; - color: var(--vscode-input-foreground); - border: 1px solid var(--vscode-dropdown-border); + color: var(--vscode-titleMenu-foreground); + border: 1px solid var(--vscode-titleMenu-border); border-radius: 5px; height: 20px; line-height: 18px; @@ -105,7 +105,7 @@ } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen:HOVER { - background-color: var(--vscode-dropdown-border); + background-color: var(--vscode-titleMenu-activeBackground); line-height: 18px; } @@ -116,7 +116,7 @@ } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen .action-label:HOVER { - background-color: var(--vscode-dropdown-border); + background-color: var(--vscode-titleMenu-activeBackground); } /* Menubar */ diff --git a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts index 7ebdae6aa1b..f662aa9b832 100644 --- a/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/titleMenuControl.ts @@ -14,7 +14,9 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import * as colors from 'vs/platform/theme/common/colorRegistry'; import { WindowTitle } from 'vs/workbench/browser/parts/titlebar/windowTitle'; +import { MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_FOREGROUND } from 'vs/workbench/common/theme'; export class TitleMenuControl { @@ -75,9 +77,45 @@ export class TitleMenuControl { } } +// --- quick pick submenu + MenuRegistry.appendMenuItem(MenuId.TitleMenu, { submenu: MenuId.TitleMenuQuickPick, title: localize('title', "Quick Pick"), icon: Codicon.search, order: Number.MAX_SAFE_INTEGER }); + + +// --- theme colors + +colors.registerColor( + 'titleMenu.foreground', + { dark: colors.inputForeground, hcDark: colors.inputForeground, light: colors.inputForeground, hcLight: colors.inputForeground }, + localize('titleMenu-foreground', "Foreground color of the title menu"), + false +); +colors.registerColor( + 'titleMenu.background', + { dark: colors.inputForeground, hcDark: colors.inputForeground, light: colors.inputForeground, hcLight: colors.inputForeground }, + localize('titleMenu-background', "Background color of the title menu"), + false +); +const activeBackground = colors.registerColor( + 'titleMenu.activeBackground', + { dark: MENUBAR_SELECTION_BACKGROUND, hcDark: MENUBAR_SELECTION_BACKGROUND, light: MENUBAR_SELECTION_BACKGROUND, hcLight: MENUBAR_SELECTION_BACKGROUND }, + localize('titleMenu-activeBackground', "Active background color of the title menu"), + false +); +colors.registerColor( + 'titleMenu.border', + { dark: activeBackground, hcDark: activeBackground, light: activeBackground, hcLight: activeBackground }, + localize('titleMenu-border', "Border color of the title menu"), + false +); +colors.registerColor( + 'titleMenu.activeForeground', + { dark: MENUBAR_SELECTION_FOREGROUND, hcDark: MENUBAR_SELECTION_FOREGROUND, light: MENUBAR_SELECTION_FOREGROUND, hcLight: MENUBAR_SELECTION_FOREGROUND }, + localize('titleMenu-activeForeground', "Active foreground color of the title menu"), + false +); From 9820e77175d315323621783e86de235110f60297 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 19:12:01 +0200 Subject: [PATCH 5/6] up font size to 12px --- src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 0ba8303e4d4..69725b8cd64 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -113,6 +113,7 @@ display: inline-block; width: 100%; text-align: center; + font-size: 12px; } .monaco-workbench .part.titlebar>.titlebar-container>.window-title>.title-menu .action-item.quickopen .action-label:HOVER { From e2ac771f9aa7b197112a1d41075ae0f7303e762f Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 12 May 2022 20:38:45 +0200 Subject: [PATCH 6/6] fix compile error --- src/vs/workbench/test/browser/workbenchTestServices.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index dfa77790841..9a1025debd8 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -159,6 +159,7 @@ import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; +import { ILayoutOffsetInfo } from 'vs/platform/layout/browser/layoutService'; export function createFileEditorInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined, undefined, undefined, undefined, undefined); @@ -567,6 +568,7 @@ export class TestLayoutService implements IWorkbenchLayoutService { openedDefaultEditors = false; dimension: IDimension = { width: 800, height: 600 }; + offset: ILayoutOffsetInfo = { top: 0, quickPickTop: 0 }; hasContainer = true; container: HTMLElement = window.document.body;