From 3f58cfd91fabc858cfa095a2973a02f0e6a43b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20K=C3=BChner?= Date: Wed, 25 Mar 2026 22:13:54 +0100 Subject: [PATCH] support icons in context menus --- src/vs/base/browser/ui/menu/menu.ts | 42 ++++++++++++++++++++++++----- 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/src/vs/base/browser/ui/menu/menu.ts b/src/vs/base/browser/ui/menu/menu.ts index bbadbf1d73b4c..8ef194fc4d2b9 100644 --- a/src/vs/base/browser/ui/menu/menu.ts +++ b/src/vs/base/browser/ui/menu/menu.ts @@ -412,6 +412,7 @@ export class Menu extends ActionBar { enableMnemonics: options.enableMnemonics, useEventAsContext: options.useEventAsContext, keybinding: keybindingLabel, + icon: !!action.class, }; const menuActionViewItem = new BaseMenuActionViewItem(options.context, action, menuItemOptions, this.menuStyles); @@ -447,6 +448,7 @@ class BaseMenuActionViewItem extends BaseActionViewItem { private runOnceToEnableMouseUp: RunOnceScheduler; private label: HTMLElement | undefined; private check: HTMLElement | undefined; + private iconElement: HTMLElement | undefined; private mnemonic: string | undefined; private cssClass: string; @@ -546,6 +548,9 @@ class BaseMenuActionViewItem extends BaseActionViewItem { this.check = append(this.item, $('span.menu-item-check' + ThemeIcon.asCSSSelector(Codicon.menuSelection))); this.check.setAttribute('role', 'none'); + this.iconElement = append(this.item, $('span.menu-item-icon')); + this.iconElement.setAttribute('role', 'none'); + this.label = append(this.item, $('span.action-label')); if (this.options.label && this.options.keybinding) { @@ -640,18 +645,15 @@ class BaseMenuActionViewItem extends BaseActionViewItem { } protected override updateClass(): void { - if (this.cssClass && this.item) { - this.item.classList.remove(...this.cssClass.split(' ')); + if (this.cssClass && this.iconElement) { + this.iconElement.classList.remove(...this.cssClass.split(' ')); } - if (this.options.icon && this.label) { + if (this.options.icon && this.iconElement) { this.cssClass = this.action.class || ''; - this.label.classList.add('icon'); if (this.cssClass) { - this.label.classList.add(...this.cssClass.split(' ')); + this.iconElement.classList.add(...this.cssClass.split(' ')); } this.updateEnabled(); - } else if (this.label) { - this.label.classList.remove('icon'); } } @@ -717,6 +719,10 @@ class BaseMenuActionViewItem extends BaseActionViewItem { if (this.check) { this.check.style.color = fgColor ?? ''; } + + if (this.iconElement) { + this.iconElement.style.color = fgColor ?? ''; + } } } @@ -1189,6 +1195,10 @@ ${formatRule(Codicon.menuSubmenu)} opacity: 0.4; } +.monaco-menu .monaco-action-bar.vertical .action-item.disabled .menu-item-icon { + opacity: 0.4; +} + .monaco-menu .monaco-action-bar.vertical .action-label:not(.separator) { display: inline-block; box-sizing: border-box; @@ -1227,6 +1237,19 @@ ${formatRule(Codicon.menuSubmenu)} height: 100%; } +.monaco-menu .monaco-action-bar.vertical .menu-item-icon { + position: absolute; + width: 1em; + height: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +.monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-icon { + display: none; +} + .monaco-menu .monaco-action-bar.vertical .action-menu-item.checked .menu-item-check { visibility: visible; display: flex; @@ -1288,6 +1311,11 @@ ${formatRule(Codicon.menuSubmenu)} width: 2em; } +.monaco-menu .monaco-action-bar.vertical .menu-item-icon { + font-size: inherit; + width: 2em; +} + .monaco-menu .monaco-action-bar.vertical .action-label.separator { font-size: inherit; margin: 5px 0 !important;