mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 18:46:40 -05:00
Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 (#14883)
* Merge from vscode bead496a613e475819f89f08e9e882b841bc1fe8 * Bump distro * Upgrade GCC to 4.9 due to yarn install errors * Update build image * Fix bootstrap base url * Bump distro * Fix build errors * Update source map file * Disable checkbox for blocking migration issues (#15131) * disable checkbox for blocking issues * wip * disable checkbox fixes * fix strings * Remove duplicate tsec command * Default to off for tab color if settings not present * re-skip failing tests * Fix mocha error * Bump sqlite version & fix notebooks search view * Turn off esbuild warnings * Update esbuild log level * Fix overflowactionbar tests * Fix ts-ignore in dropdown tests * cleanup/fixes * Fix hygiene * Bundle in entire zone.js module * Remove extra constructor param * bump distro for web compile break * bump distro for web compile break v2 * Undo log level change * New distro * Fix integration test scripts * remove the "no yarn.lock changes" workflow * fix scripts v2 * Update unit test scripts * Ensure ads-kerberos2 updates in .vscodeignore * Try fix unit tests * Upload crash reports * remove nogpu * always upload crashes * Use bash script * Consolidate data/ext dir names * Create in tmp directory Co-authored-by: chlafreniere <hichise@gmail.com> Co-authored-by: Christopher Suh <chsuh@microsoft.com> Co-authored-by: chgagnon <chgagnon@microsoft.com>
This commit is contained in:
14
src/vs/platform/actions/browser/menuEntryActionViewItem.css
Normal file
14
src/vs/platform/actions/browser/menuEntryActionViewItem.css
Normal file
@@ -0,0 +1,14 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
.monaco-action-bar .action-item.menu-entry .action-label {
|
||||
background-image: var(--menu-entry-icon-light);
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.vs-dark .monaco-action-bar .action-item.menu-entry .action-label,
|
||||
.hc-black .monaco-action-bar .action-item.menu-entry .action-label {
|
||||
background-image: var(--menu-entry-icon-dark);
|
||||
}
|
||||
@@ -3,10 +3,11 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createCSSRule, asCSSUrl, ModifierKeyEmitter } from 'vs/base/browser/dom';
|
||||
import 'vs/css!./menuEntryActionViewItem';
|
||||
import { asCSSUrl, createCSSRule, ModifierKeyEmitter } from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { IAction, Separator } from 'vs/base/common/actions';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator';
|
||||
import { IdGenerator } from 'vs/base/common/idGenerator'; // {{SQL CARBON EDIT}}
|
||||
import { IDisposable, toDisposable, MutableDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; // {{SQL CARBON EDIT}}
|
||||
import { localize } from 'vs/nls';
|
||||
import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemAction, Icon } from 'vs/platform/actions/common/actions';
|
||||
@@ -17,18 +18,20 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
|
||||
import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem';
|
||||
import { isWindows, isLinux } from 'vs/base/common/platform';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable {
|
||||
const groups = menu.getActions(options);
|
||||
const useAlternativeActions = ModifierKeyEmitter.getInstance().keyStatus.altKey;
|
||||
const modifierKeyEmitter = ModifierKeyEmitter.getInstance();
|
||||
const useAlternativeActions = modifierKeyEmitter.keyStatus.altKey || ((isWindows || isLinux) && modifierKeyEmitter.keyStatus.shiftKey);
|
||||
fillInActions(groups, target, useAlternativeActions, isPrimaryGroup);
|
||||
return asDisposable(groups);
|
||||
}
|
||||
|
||||
export function createAndFillInActionBarActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean): IDisposable {
|
||||
export function createAndFillInActionBarActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, isPrimaryGroup?: (group: string) => boolean, primaryMaxCount?: number): IDisposable {
|
||||
const groups = menu.getActions(options);
|
||||
// Action bars handle alternative actions on their own so the alternative actions should be ignored
|
||||
fillInActions(groups, target, false, isPrimaryGroup);
|
||||
fillInActions(groups, target, false, isPrimaryGroup, primaryMaxCount);
|
||||
return asDisposable(groups);
|
||||
}
|
||||
|
||||
@@ -43,32 +46,43 @@ function asDisposable(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemActio
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}} add export modifier
|
||||
export function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemAction | SubmenuItemAction>]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation'): void {
|
||||
for (let tuple of groups) {
|
||||
let [group, actions] = tuple;
|
||||
export function fillInActions(groups: ReadonlyArray<[string, ReadonlyArray<MenuItemAction | SubmenuItemAction>]>, target: IAction[] | { primary: IAction[]; secondary: IAction[]; }, useAlternativeActions: boolean, isPrimaryGroup: (group: string) => boolean = group => group === 'navigation', primaryMaxCount: number = Number.MAX_SAFE_INTEGER): void {
|
||||
|
||||
let primaryBucket: IAction[];
|
||||
let secondaryBucket: IAction[];
|
||||
if (Array.isArray(target)) {
|
||||
primaryBucket = target;
|
||||
secondaryBucket = target;
|
||||
} else {
|
||||
primaryBucket = target.primary;
|
||||
secondaryBucket = target.secondary;
|
||||
}
|
||||
|
||||
for (let [group, actions] of groups) {
|
||||
if (useAlternativeActions) {
|
||||
actions = actions.map(a => (a instanceof MenuItemAction) && !!a.alt ? a.alt : a);
|
||||
}
|
||||
|
||||
if (isPrimaryGroup(group)) {
|
||||
const to = Array.isArray(target) ? target : target.primary;
|
||||
|
||||
to.unshift(...actions);
|
||||
primaryBucket.unshift(...actions);
|
||||
} else {
|
||||
const to = Array.isArray(target) ? target : target.secondary;
|
||||
|
||||
if (to.length > 0) {
|
||||
to.push(new Separator());
|
||||
if (secondaryBucket.length > 0) {
|
||||
secondaryBucket.push(new Separator());
|
||||
}
|
||||
|
||||
to.push(...actions);
|
||||
secondaryBucket.push(...actions);
|
||||
}
|
||||
}
|
||||
|
||||
// overflow items from the primary group into the secondary bucket
|
||||
if (primaryBucket !== secondaryBucket && primaryBucket.length > primaryMaxCount) {
|
||||
const overflow = primaryBucket.splice(primaryMaxCount, primaryBucket.length - primaryMaxCount);
|
||||
secondaryBucket.unshift(...overflow, new Separator());
|
||||
}
|
||||
}
|
||||
|
||||
const ids = new IdGenerator('menu-item-action-item-icon-');
|
||||
const ids = new IdGenerator('menu-item-action-item-icon-'); // {{SQL CARBON EDIT}} - add back since custom toolbar menu is using below
|
||||
|
||||
const ICON_PATH_TO_CSS_RULES = new Map<string /* path*/, string /* CSS rule */>();
|
||||
const ICON_PATH_TO_CSS_RULES = new Map<string /* path*/, string /* CSS rule */>(); // {{SQL CARBON EDIT}} - add back since custom toolbar menu is using below
|
||||
|
||||
export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
|
||||
@@ -85,7 +99,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
this._altKey = ModifierKeyEmitter.getInstance();
|
||||
}
|
||||
|
||||
protected get _commandAction(): IAction {
|
||||
protected get _commandAction(): MenuItemAction {
|
||||
return this._wantsAltCommand && (<MenuItemAction>this._action).alt || this._action;
|
||||
}
|
||||
|
||||
@@ -93,12 +107,14 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.actionRunner.run(this._commandAction, this._context)
|
||||
.then(undefined, err => this._notificationService.error(err));
|
||||
this.actionRunner
|
||||
.run(this._commandAction, this._context)
|
||||
.catch(err => this._notificationService.error(err));
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
container.classList.add('menu-entry');
|
||||
|
||||
this._updateItemClass(this._action.item);
|
||||
|
||||
@@ -167,46 +183,39 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
protected _updateItemClass(item: ICommandAction): void { // {{SQL CARBON EDIT}} make it overwritable
|
||||
this._itemClassDispose.value = undefined;
|
||||
|
||||
const { element, label } = this;
|
||||
if (!element || !label) {
|
||||
return;
|
||||
}
|
||||
|
||||
const icon = this._commandAction.checked && (item.toggled as { icon?: Icon })?.icon ? (item.toggled as { icon: Icon }).icon : item.icon;
|
||||
|
||||
if (!icon) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ThemeIcon.isThemeIcon(icon)) {
|
||||
// theme icons
|
||||
const iconClass = ThemeIcon.asClassName(icon);
|
||||
if (this.label && iconClass) {
|
||||
this.label.classList.add(...iconClass.split(' '));
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
this.label.classList.remove(...iconClass.split(' '));
|
||||
}
|
||||
});
|
||||
label.classList.add(...iconClass.split(' '));
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
label.classList.remove(...iconClass.split(' '));
|
||||
});
|
||||
|
||||
} else {
|
||||
// icon path/url
|
||||
if (icon.light) {
|
||||
label.style.setProperty('--menu-entry-icon-light', asCSSUrl(icon.light));
|
||||
}
|
||||
|
||||
} else if (icon) {
|
||||
// icon path
|
||||
let iconClass: string;
|
||||
|
||||
if (icon.dark?.scheme) {
|
||||
|
||||
const iconPathMapKey = icon.dark.toString();
|
||||
|
||||
if (ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(icon.light || icon.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(icon.dark)}`);
|
||||
ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
if (this.label) {
|
||||
this.label.classList.add('icon', ...iconClass.split(' '));
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
this.label.classList.remove('icon', ...iconClass.split(' '));
|
||||
}
|
||||
});
|
||||
}
|
||||
if (icon.dark) {
|
||||
label.style.setProperty('--menu-entry-icon-dark', asCSSUrl(icon.dark));
|
||||
}
|
||||
label.classList.add('icon');
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
label.classList.remove('icon');
|
||||
label.style.removeProperty('--menu-entry-icon-light');
|
||||
label.style.removeProperty('--menu-entry-icon-dark');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,30 +224,42 @@ export class SubmenuEntryActionViewItem extends DropdownMenuActionViewItem {
|
||||
|
||||
constructor(
|
||||
action: SubmenuItemAction,
|
||||
@INotificationService _notificationService: INotificationService,
|
||||
@IContextMenuService _contextMenuService: IContextMenuService
|
||||
@IContextMenuService contextMenuService: IContextMenuService
|
||||
) {
|
||||
let classNames: string | string[] | undefined;
|
||||
super(action, { getActions: () => action.actions }, contextMenuService, {
|
||||
menuAsChild: true,
|
||||
classNames: ThemeIcon.isThemeIcon(action.item.icon) ? ThemeIcon.asClassName(action.item.icon) : undefined,
|
||||
});
|
||||
}
|
||||
|
||||
if (action.item.icon) {
|
||||
if (ThemeIcon.isThemeIcon(action.item.icon)) {
|
||||
classNames = ThemeIcon.asClassName(action.item.icon)!;
|
||||
} else if (action.item.icon.dark?.scheme) {
|
||||
const iconPathMapKey = action.item.icon.dark.toString();
|
||||
|
||||
if (ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
classNames = ['icon', ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!];
|
||||
} else {
|
||||
const className = ids.nextId();
|
||||
classNames = ['icon', className];
|
||||
createCSSRule(`.icon.${className}`, `background-image: ${asCSSUrl(action.item.icon.light || action.item.icon.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${className}, .hc-black .icon.${className}`, `background-image: ${asCSSUrl(action.item.icon.dark)}`);
|
||||
ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, className);
|
||||
render(container: HTMLElement): void {
|
||||
super.render(container);
|
||||
if (this.element) {
|
||||
container.classList.add('menu-entry');
|
||||
const { icon } = (<SubmenuItemAction>this._action).item;
|
||||
if (icon && !ThemeIcon.isThemeIcon(icon)) {
|
||||
this.element.classList.add('icon');
|
||||
if (icon.light) {
|
||||
this.element.style.setProperty('--menu-entry-icon-light', asCSSUrl(icon.light));
|
||||
}
|
||||
if (icon.dark) {
|
||||
this.element.style.setProperty('--menu-entry-icon-dark', asCSSUrl(icon.dark));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super(action, action.actions, _contextMenuService, { classNames: classNames, menuAsChild: true });
|
||||
/**
|
||||
* Creates action view items for menu actions or submenu actions.
|
||||
*/
|
||||
export function createActionViewItem(instaService: IInstantiationService, action: IAction): undefined | MenuEntryActionViewItem | SubmenuEntryActionViewItem {
|
||||
if (action instanceof MenuItemAction) {
|
||||
return instaService.createInstance(MenuEntryActionViewItem, action);
|
||||
} else if (action instanceof SubmenuItemAction) {
|
||||
return instaService.createInstance(SubmenuEntryActionViewItem, action);
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,22 +16,36 @@ import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { CSSIcon } from 'vs/base/common/codicons';
|
||||
|
||||
export interface ILocalizedString {
|
||||
/**
|
||||
* The localized value of the string.
|
||||
*/
|
||||
value: string;
|
||||
/**
|
||||
* The original (non localized value of the string)
|
||||
*/
|
||||
original: string;
|
||||
}
|
||||
|
||||
export interface ICommandActionTitle extends ILocalizedString {
|
||||
/**
|
||||
* The title with a mnemonic designation. && precedes the mnemonic.
|
||||
*/
|
||||
mnemonicTitle?: string;
|
||||
}
|
||||
|
||||
export type Icon = { dark?: URI; light?: URI; } | ThemeIcon;
|
||||
|
||||
export interface ICommandAction {
|
||||
id: string;
|
||||
title: string | ILocalizedString;
|
||||
title: string | ICommandActionTitle;
|
||||
category?: string | ILocalizedString;
|
||||
tooltip?: string | ILocalizedString;
|
||||
tooltip?: string;
|
||||
icon?: Icon;
|
||||
precondition?: ContextKeyExpression;
|
||||
toggled?: ContextKeyExpression | { condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString };
|
||||
toggled?: ContextKeyExpression | { condition: ContextKeyExpression, icon?: Icon, tooltip?: string; };
|
||||
}
|
||||
|
||||
export type ISerializableCommandAction = UriDto<ICommandAction>;
|
||||
@@ -45,7 +59,7 @@ export interface IMenuItem {
|
||||
}
|
||||
|
||||
export interface ISubmenuItem {
|
||||
title: string | ILocalizedString;
|
||||
title: string | ICommandActionTitle;
|
||||
submenu: MenuId;
|
||||
icon?: Icon;
|
||||
when?: ContextKeyExpression;
|
||||
@@ -112,6 +126,7 @@ export class MenuId {
|
||||
static readonly TunnelInline = new MenuId('TunnelInline');
|
||||
static readonly TunnelTitle = new MenuId('TunnelTitle');
|
||||
static readonly ViewItemContext = new MenuId('ViewItemContext');
|
||||
static readonly ViewContainerTitle = new MenuId('ViewContainerTitle');
|
||||
static readonly ViewContainerTitleContext = new MenuId('ViewContainerTitleContext');
|
||||
static readonly ViewTitle = new MenuId('ViewTitle');
|
||||
static readonly ViewTitleContext = new MenuId('ViewTitleContext');
|
||||
@@ -141,6 +156,7 @@ export class MenuId {
|
||||
static readonly TimelineTitle = new MenuId('TimelineTitle');
|
||||
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
|
||||
static readonly AccountsContext = new MenuId('AccountsContext');
|
||||
static readonly PanelTitle = new MenuId('PanelTitle');
|
||||
|
||||
readonly id: number;
|
||||
readonly _debugName: string;
|
||||
@@ -157,7 +173,7 @@ export interface IMenuActionOptions {
|
||||
}
|
||||
|
||||
export interface IMenu extends IDisposable {
|
||||
readonly onDidChange: Event<IMenu | undefined>;
|
||||
readonly onDidChange: Event<IMenu>;
|
||||
getActions(options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][];
|
||||
}
|
||||
|
||||
@@ -182,7 +198,7 @@ export interface IMenuRegistry {
|
||||
addCommand(userCommand: ICommandAction): IDisposable;
|
||||
getCommand(id: string): ICommandAction | undefined;
|
||||
getCommands(): ICommandsMap;
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem }>): IDisposable;
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem; }>): IDisposable;
|
||||
appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;
|
||||
getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;
|
||||
}
|
||||
@@ -233,7 +249,7 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
|
||||
return this.appendMenuItems(Iterable.single({ id, item }));
|
||||
}
|
||||
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem }>): IDisposable {
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem; }>): IDisposable {
|
||||
|
||||
const changedIds = new Set<MenuId>();
|
||||
const toRemove = new LinkedList<Function>();
|
||||
@@ -343,62 +359,76 @@ export class SubmenuItemAction extends SubmenuAction {
|
||||
}
|
||||
}
|
||||
|
||||
export class MenuItemAction extends ExecuteCommandAction {
|
||||
// implements IAction, does NOT extend Action, so that no one
|
||||
// subscribes to events of Action or modified properties
|
||||
export class MenuItemAction implements IAction {
|
||||
|
||||
readonly item: ICommandAction;
|
||||
readonly alt: MenuItemAction | undefined;
|
||||
private readonly _options: IMenuActionOptions | undefined;
|
||||
|
||||
private _options: IMenuActionOptions;
|
||||
readonly id: string;
|
||||
|
||||
// {{SQL CARBON EDIT}} -- remove readonly since notebook component sets these
|
||||
label: string;
|
||||
tooltip: string;
|
||||
readonly class: string | undefined;
|
||||
readonly enabled: boolean;
|
||||
readonly checked: boolean;
|
||||
readonly expanded: boolean = false;
|
||||
|
||||
constructor(
|
||||
item: ICommandAction,
|
||||
alt: ICommandAction | undefined,
|
||||
options: IMenuActionOptions,
|
||||
options: IMenuActionOptions | undefined,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@ICommandService commandService: ICommandService
|
||||
@ICommandService private _commandService: ICommandService
|
||||
) {
|
||||
typeof item.title === 'string' ? super(item.id, item.title, commandService) : super(item.id, item.title.value, commandService);
|
||||
|
||||
this._cssClass = undefined;
|
||||
this._enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);
|
||||
this._tooltip = item.tooltip ? typeof item.tooltip === 'string' ? item.tooltip : item.tooltip.value : undefined;
|
||||
this.id = item.id;
|
||||
this.label = typeof item.title === 'string' ? item.title : item.title.value;
|
||||
this.tooltip = item.tooltip ?? '';
|
||||
this.enabled = !item.precondition || contextKeyService.contextMatchesRules(item.precondition);
|
||||
this.checked = false;
|
||||
|
||||
if (item.toggled) {
|
||||
const toggled = ((item.toggled as { condition: ContextKeyExpression }).condition ? item.toggled : { condition: item.toggled }) as {
|
||||
condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString
|
||||
const toggled = ((item.toggled as { condition: ContextKeyExpression; }).condition ? item.toggled : { condition: item.toggled }) as {
|
||||
condition: ContextKeyExpression, icon?: Icon, tooltip?: string | ILocalizedString;
|
||||
};
|
||||
this._checked = contextKeyService.contextMatchesRules(toggled.condition);
|
||||
if (this._checked && toggled.tooltip) {
|
||||
this._tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;
|
||||
this.checked = contextKeyService.contextMatchesRules(toggled.condition);
|
||||
if (this.checked && toggled.tooltip) {
|
||||
this.tooltip = typeof toggled.tooltip === 'string' ? toggled.tooltip : toggled.tooltip.value;
|
||||
}
|
||||
}
|
||||
|
||||
this._options = options || {};
|
||||
|
||||
this.item = item;
|
||||
this.alt = alt ? new MenuItemAction(alt, undefined, this._options, contextKeyService, commandService) : undefined;
|
||||
this.alt = alt ? new MenuItemAction(alt, undefined, options, contextKeyService, _commandService) : undefined;
|
||||
this._options = options;
|
||||
if (ThemeIcon.isThemeIcon(item.icon)) {
|
||||
this.class = CSSIcon.asClassName(item.icon);
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
if (this.alt) {
|
||||
this.alt.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
// there is NOTHING to dispose and the MenuItemAction should
|
||||
// never have anything to dispose as it is a convenience type
|
||||
// to bridge into the rendering world.
|
||||
}
|
||||
|
||||
run(...args: any[]): Promise<any> {
|
||||
let runArgs: any[] = [];
|
||||
|
||||
if (this._options.arg) {
|
||||
if (this._options?.arg) {
|
||||
runArgs = [...runArgs, this._options.arg];
|
||||
}
|
||||
|
||||
if (this._options.shouldForwardArgs) {
|
||||
if (this._options?.shouldForwardArgs) {
|
||||
runArgs = [...runArgs, ...args];
|
||||
}
|
||||
|
||||
return super.run(...runArgs);
|
||||
return this._commandService.executeCommand(this.id, ...runArgs);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
export class SyncActionDescriptor {
|
||||
@@ -411,7 +441,7 @@ export class SyncActionDescriptor {
|
||||
private readonly _keybindingContext: ContextKeyExpression | undefined;
|
||||
private readonly _keybindingWeight: number | undefined;
|
||||
|
||||
public static create<Services extends BrandedService[]>(ctor: { new(id: string, label: string, ...services: Services): Action },
|
||||
public static create<Services extends BrandedService[]>(ctor: { new(id: string, label: string, ...services: Services): Action; },
|
||||
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpression, keybindingWeight?: number
|
||||
): SyncActionDescriptor {
|
||||
return new SyncActionDescriptor(ctor as IConstructorSignature2<string, string | undefined, Action>, id, label, keybindings, keybindingContext, keybindingWeight);
|
||||
@@ -478,7 +508,7 @@ export interface IAction2Options extends ICommandAction {
|
||||
/**
|
||||
* One or many menu items.
|
||||
*/
|
||||
menu?: OneOrN<{ id: MenuId } & Omit<IMenuItem, 'command'>>;
|
||||
menu?: OneOrN<{ id: MenuId; } & Omit<IMenuItem, 'command'>>;
|
||||
|
||||
/**
|
||||
* One keybinding.
|
||||
@@ -497,7 +527,7 @@ export abstract class Action2 {
|
||||
abstract run(accessor: ServicesAccessor, ...args: any[]): any;
|
||||
}
|
||||
|
||||
export function registerAction2(ctor: { new(): Action2 }): IDisposable {
|
||||
export function registerAction2(ctor: { new(): Action2; }): IDisposable {
|
||||
const disposables = new DisposableStore();
|
||||
const action = new ctor();
|
||||
|
||||
|
||||
@@ -3,11 +3,12 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction, ILocalizedString } from 'vs/platform/actions/common/actions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKeyChangeEvent, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
export class MenuService implements IMenuService {
|
||||
|
||||
@@ -29,9 +30,11 @@ type MenuItemGroup = [string, Array<IMenuItem | ISubmenuItem>];
|
||||
|
||||
class Menu implements IMenu {
|
||||
|
||||
private readonly _onDidChange = new Emitter<IMenu | undefined>();
|
||||
private readonly _dispoables = new DisposableStore();
|
||||
|
||||
private readonly _onDidChange = new Emitter<IMenu>();
|
||||
readonly onDidChange: Event<IMenu> = this._onDidChange.event;
|
||||
|
||||
private _menuGroups: MenuItemGroup[] = [];
|
||||
private _contextKeys: Set<string> = new Set();
|
||||
|
||||
@@ -45,19 +48,23 @@ class Menu implements IMenu {
|
||||
|
||||
// rebuild this menu whenever the menu registry reports an
|
||||
// event for this MenuId
|
||||
this._dispoables.add(Event.debounce(
|
||||
Event.filter(MenuRegistry.onDidChangeMenu, set => set.has(this._id)),
|
||||
() => { },
|
||||
50
|
||||
)(this._build, this));
|
||||
const rebuildMenuSoon = new RunOnceScheduler(() => this._build(), 50);
|
||||
this._dispoables.add(rebuildMenuSoon);
|
||||
this._dispoables.add(MenuRegistry.onDidChangeMenu(e => {
|
||||
if (e.has(_id)) {
|
||||
rebuildMenuSoon.schedule();
|
||||
}
|
||||
}));
|
||||
|
||||
// when context keys change we need to check if the menu also
|
||||
// has changed
|
||||
this._dispoables.add(Event.debounce<IContextKeyChangeEvent, boolean>(
|
||||
this._contextKeyService.onDidChangeContext,
|
||||
(last, event) => last || event.affectsSome(this._contextKeys),
|
||||
50
|
||||
)(e => e && this._onDidChange.fire(undefined), this));
|
||||
const fireChangeSoon = new RunOnceScheduler(() => this._onDidChange.fire(this), 50);
|
||||
this._dispoables.add(fireChangeSoon);
|
||||
this._dispoables.add(_contextKeyService.onDidChangeContext(e => {
|
||||
if (e.affectsSome(this._contextKeys)) {
|
||||
fireChangeSoon.schedule();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@@ -88,25 +95,22 @@ class Menu implements IMenu {
|
||||
// keep keys for eventing
|
||||
Menu._fillInKbExprKeys(item.when, this._contextKeys);
|
||||
|
||||
// keep precondition keys for event if applicable
|
||||
if (isIMenuItem(item) && item.command.precondition) {
|
||||
Menu._fillInKbExprKeys(item.command.precondition, this._contextKeys);
|
||||
}
|
||||
|
||||
// keep toggled keys for event if applicable
|
||||
if (isIMenuItem(item) && item.command.toggled) {
|
||||
const toggledExpression: ContextKeyExpression = (item.command.toggled as { condition: ContextKeyExpression }).condition || item.command.toggled as ContextKeyExpression; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
Menu._fillInKbExprKeys(toggledExpression, this._contextKeys);
|
||||
if (isIMenuItem(item)) {
|
||||
// keep precondition keys for event if applicable
|
||||
if (item.command.precondition) {
|
||||
Menu._fillInKbExprKeys(item.command.precondition, this._contextKeys);
|
||||
}
|
||||
// keep toggled keys for event if applicable
|
||||
if (item.command.toggled) {
|
||||
const toggledExpression: any = (item.command.toggled as { condition: ContextKeyExpression }).condition || item.command.toggled;
|
||||
Menu._fillInKbExprKeys(toggledExpression, this._contextKeys);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._onDidChange.fire(this);
|
||||
}
|
||||
|
||||
get onDidChange(): Event<IMenu | undefined> {
|
||||
return this._onDidChange.event;
|
||||
}
|
||||
|
||||
getActions(options: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][] {
|
||||
getActions(options?: IMenuActionOptions): [string, Array<MenuItemAction | SubmenuItemAction>][] {
|
||||
const result: [string, Array<MenuItemAction | SubmenuItemAction>][] = [];
|
||||
for (let group of this._menuGroups) {
|
||||
const [id, items] = group;
|
||||
|
||||
Reference in New Issue
Block a user