From 67c889e39416c958fc7cb84e4814df2225c22852 Mon Sep 17 00:00:00 2001 From: SteVen Batten <6561887+sbatten@users.noreply.github.com> Date: Thu, 14 Jan 2021 16:45:41 -0800 Subject: [PATCH] Merge and restructure menu (#114383) * move home menu into compact menu * add web app icon to titlebar * add TODO --- .../parts/activitybar/activitybarPart.ts | 3 +- .../parts/titlebar/media/titlebarpart.css | 25 +++++++- .../browser/parts/titlebar/menubarControl.ts | 60 +++++++++++++++++-- .../browser/parts/titlebar/titlebarPart.ts | 34 ++++++++++- .../parts/titlebar/titlebarPart.ts | 4 +- 5 files changed, 115 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 546ac57e232..af45b2bd3de 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -487,7 +487,8 @@ export class ActivitybarPart extends Part implements IActivityBarService { // Home action bar const homeIndicator = this.environmentService.options?.homeIndicator; - if (homeIndicator) { + // TODO @sbatten remove the fake setting and associated code + if (homeIndicator && this.configurationService.getValue('window.showHomeIndicator')) { let codicon = iconRegistry.get(homeIndicator.icon); if (!codicon) { codicon = Codicon.code; diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index a4c34e45041..7ac774cb7c6 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -85,11 +85,34 @@ height: 100%; position: relative; z-index: 3000; + flex-shrink: 0; +} + +.monaco-workbench .part.titlebar > .window-appicon:not(.codicon) { background-image: url('../../../media/code-icon.svg'); background-repeat: no-repeat; background-position: center center; background-size: 16px; - flex-shrink: 0; +} + +.monaco-workbench .part.titlebar .window-appicon > .home-bar-icon-badge { + position: absolute; + right: 9px; + bottom: 6px; + width: 8px; + height: 8px; + z-index: 1; /* on top of home indicator */ + background-image: url('../../../media/code-icon.svg'); + background-repeat: no-repeat; + background-position: center center; + background-size: 8px; + pointer-events: none; + border-top: 1px solid transparent; + border-left: 1px solid transparent; +} + +.monaco-workbench .part.titlebar > .window-appicon.codicon { + line-height: 30px; } .monaco-workbench.fullscreen .part.titlebar > .window-appicon { diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index 98fc256def0..67798d8844b 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -4,11 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu, SubmenuItemAction, registerAction2, Action2, MenuRegistry, MenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, IThemeService } from 'vs/platform/theme/common/themeService'; import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IAction, Action, SubmenuAction, Separator } from 'vs/base/common/actions'; +import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IAction, Action, SubmenuAction, Separator, toAction } from 'vs/base/common/actions'; import * as DOM from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { isMacintosh, isWeb, isIOS, isNative } from 'vs/base/common/platform'; @@ -326,6 +326,16 @@ export class CustomMenubarControl extends MenubarControl { this.registerActions(); + // Register web menu actions to the file menu when its in the title + this.getWebNavigationMenuItemActions().forEach(actionItem => { + this._register(MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + command: actionItem.item, + title: actionItem.item.title, + group: 'z_Web', + when: ContextKeyExpr.and(IsWebContext, ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) + })); + }); + registerThemingParticipant((theme, collector) => { const menubarActiveWindowFgColor = theme.getColor(TITLE_BAR_ACTIVE_FOREGROUND); if (menubarActiveWindowFgColor) { @@ -662,6 +672,27 @@ export class CustomMenubarControl extends MenubarControl { } } + private getWebNavigationMenuItemActions(): MenuItemAction[] { + if (!isWeb) { + return []; // only for web + } + + const webNavigationActions = []; + const webNavigationMenu = this.menuService.createMenu(MenuId.MenubarHomeMenu, this.contextKeyService); + for (const groups of webNavigationMenu.getActions()) { + const [, actions] = groups; + for (const action of actions) { + if (action instanceof MenuItemAction) { + webNavigationActions.push(action); + } + } + } + + webNavigationMenu.dispose(); + + return webNavigationActions; + } + private getMenuBarOptions(): IMenuBarOptions { return { enableMnemonics: this.currentEnableMenuBarMnemonics, @@ -669,7 +700,28 @@ export class CustomMenubarControl extends MenubarControl { visibility: this.currentMenubarVisibility, getKeybinding: (action) => this.keybindingService.lookupKeybinding(action.id), alwaysOnMnemonics: this.alwaysOnMnemonics, - compactMode: this.currentCompactMenuMode + compactMode: this.currentCompactMenuMode, + getCompactMenuActions: () => { + if (!isWeb) { + return []; // only for web + } + + const webNavigationActions: IAction[] = []; + const href = this.environmentService.options?.homeIndicator?.href; + if (href) { + webNavigationActions.push(toAction({ id: 'goHome', label: nls.localize('goHome', "Go Home"), run: () => window.location.href = href })); + } + + const otherActions = this.getWebNavigationMenuItemActions().map(action => { + const title = typeof action.item.title === 'string' + ? action.item.title + : action.item.title.mnemonicTitle ?? action.item.title.value; + return new Action(action.id, mnemonicMenuLabel(title), action.class, action.enabled, () => this.commandService.executeCommand(action.id)); + }); + + webNavigationActions.push(...otherActions); + return webNavigationActions; + } }; } diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index d443d4895ea..52a1f2f4597 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -25,7 +25,7 @@ import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform' import { URI } from 'vs/base/common/uri'; import { Color } from 'vs/base/common/color'; import { trim } from 'vs/base/common/strings'; -import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { EventType, EventHelper, Dimension, isAncestor, append, $, addDisposableListener, runAtThisOrScheduleAtNextAnimationFrame, prepend } from 'vs/base/browser/dom'; import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menubarControl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { template } from 'vs/base/common/labels'; @@ -41,6 +41,7 @@ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { Codicon, iconRegistry } from 'vs/base/common/codicons'; export class TitlebarPart extends Part implements ITitleService { @@ -65,6 +66,8 @@ export class TitlebarPart extends Part implements ITitleService { protected title!: HTMLElement; protected customMenubar: CustomMenubarControl | undefined; + protected appIcon: HTMLElement | undefined; + private appIconBadge: HTMLElement | undefined; protected menubar?: HTMLElement; protected lastLayoutDimensions: Dimension | undefined; private titleBarStyle: 'native' | 'custom'; @@ -341,6 +344,28 @@ export class TitlebarPart extends Part implements ITitleService { createContentArea(parent: HTMLElement): HTMLElement { this.element = parent; + // App Icon (Native Windows/Linux and Web) + if (!isMacintosh || isWeb) { + this.appIcon = prepend(this.element, $('a.window-appicon')); + + // Web-only home indicator and menu + if (isWeb) { + const homeIndicator = this.environmentService.options?.homeIndicator; + if (homeIndicator) { + let codicon = iconRegistry.get(homeIndicator.icon); + if (!codicon) { + codicon = Codicon.code; + } + + this.appIcon.setAttribute('href', homeIndicator.href); + this.appIcon.classList.add(...codicon.classNamesArray); + this.appIconBadge = document.createElement('div'); + this.appIconBadge.classList.add('home-bar-icon-badge'); + this.appIcon.appendChild(this.appIconBadge); + } + } + } + // Menubar: install a custom menu bar depending on configuration // and when not in activity bar if (this.titleBarStyle !== 'native' @@ -407,6 +432,11 @@ export class TitlebarPart extends Part implements ITitleService { return color.isOpaque() ? color : color.makeOpaque(WORKBENCH_BACKGROUND(theme)); }) || ''; this.element.style.backgroundColor = titleBackground; + + if (this.appIconBadge) { + this.appIconBadge.style.backgroundColor = titleBackground; + } + if (titleBackground && Color.fromHex(titleBackground).isLighter()) { this.element.classList.add('light'); } else { @@ -441,7 +471,7 @@ export class TitlebarPart extends Part implements ITitleService { protected adjustTitleMarginToCenter(): void { if (this.customMenubar && this.menubar) { - const leftMarker = this.menubar.clientWidth + 10; + const leftMarker = (this.appIcon ? this.appIcon.clientWidth : 0) + this.menubar.clientWidth + 10; const rightMarker = this.element.clientWidth - 10; // Not enough space to center the titlebar within window, diff --git a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts index fb48aeb713d..c2258de1123 100644 --- a/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/electron-sandbox/parts/titlebar/titlebarPart.ts @@ -27,7 +27,6 @@ import { Codicon } from 'vs/base/common/codicons'; import { NativeMenubarControl } from 'vs/workbench/electron-sandbox/parts/titlebar/menubarControl'; export class TitlebarPart extends BrowserTitleBarPart { - private appIcon: HTMLElement | undefined; private windowControls: HTMLElement | undefined; private maxRestoreControl: HTMLElement | undefined; private dragRegion: HTMLElement | undefined; @@ -171,8 +170,7 @@ export class TitlebarPart extends BrowserTitleBarPart { } // App Icon (Native Windows/Linux) - if (!isMacintosh) { - this.appIcon = DOM.prepend(this.element, DOM.$('div.window-appicon')); + if (this.appIcon) { this.onUpdateAppIconDragBehavior(); this._register(DOM.addDisposableListener(this.appIcon, DOM.EventType.DBLCLICK, (e => { -- GitLab