Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/actions/browser/buttonbar.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import { ButtonBar, IButton } from '../../../base/browser/ui/button/button.js';
7
import { createInstantHoverDelegate } from '../../../base/browser/ui/hover/hoverDelegateFactory.js';
8
import { ActionRunner, IAction, IActionRunner, SubmenuAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../base/common/actions.js';
9
import { Codicon } from '../../../base/common/codicons.js';
10
import { Emitter, Event } from '../../../base/common/event.js';
11
import { DisposableStore } from '../../../base/common/lifecycle.js';
12
import { ThemeIcon } from '../../../base/common/themables.js';
13
import { localize } from '../../../nls.js';
14
import { getActionBarActions } from './menuEntryActionViewItem.js';
15
import { IToolBarRenderOptions } from './toolbar.js';
16
import { MenuId, IMenuService, MenuItemAction, IMenuActionOptions } from '../common/actions.js';
17
import { IContextKeyService } from '../../contextkey/common/contextkey.js';
18
import { IContextMenuService } from '../../contextview/browser/contextView.js';
19
import { IHoverService } from '../../hover/browser/hover.js';
20
import { IKeybindingService } from '../../keybinding/common/keybinding.js';
21
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
22
23
export type IButtonConfigProvider = (action: IAction, index: number) => {
24
showIcon?: boolean;
25
showLabel?: boolean;
26
isSecondary?: boolean;
27
} | undefined;
28
29
export interface IWorkbenchButtonBarOptions {
30
telemetrySource?: string;
31
buttonConfigProvider?: IButtonConfigProvider;
32
}
33
34
export class WorkbenchButtonBar extends ButtonBar {
35
36
protected readonly _store = new DisposableStore();
37
protected readonly _updateStore = new DisposableStore();
38
39
private readonly _actionRunner: IActionRunner;
40
private readonly _onDidChange = new Emitter<this>();
41
readonly onDidChange: Event<this> = this._onDidChange.event;
42
43
44
constructor(
45
container: HTMLElement,
46
private readonly _options: IWorkbenchButtonBarOptions | undefined,
47
@IContextMenuService private readonly _contextMenuService: IContextMenuService,
48
@IKeybindingService private readonly _keybindingService: IKeybindingService,
49
@ITelemetryService telemetryService: ITelemetryService,
50
@IHoverService private readonly _hoverService: IHoverService,
51
) {
52
super(container);
53
54
this._actionRunner = this._store.add(new ActionRunner());
55
if (_options?.telemetrySource) {
56
this._actionRunner.onDidRun(e => {
57
telemetryService.publicLog2<WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification>(
58
'workbenchActionExecuted',
59
{ id: e.action.id, from: _options.telemetrySource! }
60
);
61
}, undefined, this._store);
62
}
63
}
64
65
override dispose() {
66
this._onDidChange.dispose();
67
this._updateStore.dispose();
68
this._store.dispose();
69
super.dispose();
70
}
71
72
update(actions: IAction[], secondary: IAction[]): void {
73
74
const conifgProvider: IButtonConfigProvider = this._options?.buttonConfigProvider ?? (() => ({ showLabel: true }));
75
76
this._updateStore.clear();
77
this.clear();
78
79
// Support instamt hover between buttons
80
const hoverDelegate = this._updateStore.add(createInstantHoverDelegate());
81
82
for (let i = 0; i < actions.length; i++) {
83
84
const secondary = i > 0;
85
const actionOrSubmenu = actions[i];
86
let action: IAction;
87
let btn: IButton;
88
let tooltip: string = '';
89
const kb = actionOrSubmenu instanceof SubmenuAction ? '' : this._keybindingService.lookupKeybinding(actionOrSubmenu.id);
90
if (kb) {
91
tooltip = localize('labelWithKeybinding', "{0} ({1})", actionOrSubmenu.tooltip || actionOrSubmenu.label, kb.getLabel());
92
} else {
93
tooltip = actionOrSubmenu.tooltip || actionOrSubmenu.label;
94
}
95
if (actionOrSubmenu instanceof SubmenuAction && actionOrSubmenu.actions.length > 0) {
96
const [first, ...rest] = actionOrSubmenu.actions;
97
action = <MenuItemAction>first;
98
btn = this.addButtonWithDropdown({
99
secondary: conifgProvider(action, i)?.isSecondary ?? secondary,
100
actionRunner: this._actionRunner,
101
actions: rest,
102
contextMenuProvider: this._contextMenuService,
103
ariaLabel: tooltip,
104
supportIcons: true,
105
});
106
} else {
107
action = actionOrSubmenu;
108
btn = this.addButton({
109
secondary: conifgProvider(action, i)?.isSecondary ?? secondary,
110
ariaLabel: tooltip,
111
supportIcons: true,
112
});
113
}
114
115
btn.enabled = action.enabled;
116
btn.checked = action.checked ?? false;
117
btn.element.classList.add('default-colors');
118
const showLabel = conifgProvider(action, i)?.showLabel ?? true;
119
if (showLabel) {
120
btn.label = action.label;
121
} else {
122
btn.element.classList.add('monaco-text-button');
123
}
124
if (conifgProvider(action, i)?.showIcon) {
125
if (action instanceof MenuItemAction && ThemeIcon.isThemeIcon(action.item.icon)) {
126
if (!showLabel) {
127
btn.icon = action.item.icon;
128
} else {
129
// this is REALLY hacky but combining a codicon and normal text is ugly because
130
// the former define a font which doesn't work for text
131
btn.label = `$(${action.item.icon.id}) ${action.label}`;
132
}
133
} else if (action.class) {
134
btn.element.classList.add(...action.class.split(' '));
135
}
136
}
137
138
this._updateStore.add(this._hoverService.setupManagedHover(hoverDelegate, btn.element, tooltip));
139
this._updateStore.add(btn.onDidClick(async () => {
140
this._actionRunner.run(action);
141
}));
142
}
143
144
if (secondary.length > 0) {
145
146
const btn = this.addButton({
147
secondary: true,
148
ariaLabel: localize('moreActions', "More Actions")
149
});
150
151
btn.icon = Codicon.dropDownButton;
152
btn.element.classList.add('default-colors', 'monaco-text-button');
153
154
btn.enabled = true;
155
this._updateStore.add(this._hoverService.setupManagedHover(hoverDelegate, btn.element, localize('moreActions', "More Actions")));
156
this._updateStore.add(btn.onDidClick(async () => {
157
this._contextMenuService.showContextMenu({
158
getAnchor: () => btn.element,
159
getActions: () => secondary,
160
actionRunner: this._actionRunner,
161
onHide: () => btn.element.setAttribute('aria-expanded', 'false')
162
});
163
btn.element.setAttribute('aria-expanded', 'true');
164
165
}));
166
}
167
this._onDidChange.fire(this);
168
}
169
}
170
171
export interface IMenuWorkbenchButtonBarOptions extends IWorkbenchButtonBarOptions {
172
menuOptions?: IMenuActionOptions;
173
174
toolbarOptions?: IToolBarRenderOptions;
175
}
176
177
export class MenuWorkbenchButtonBar extends WorkbenchButtonBar {
178
179
constructor(
180
container: HTMLElement,
181
menuId: MenuId,
182
options: IMenuWorkbenchButtonBarOptions | undefined,
183
@IMenuService menuService: IMenuService,
184
@IContextKeyService contextKeyService: IContextKeyService,
185
@IContextMenuService contextMenuService: IContextMenuService,
186
@IKeybindingService keybindingService: IKeybindingService,
187
@ITelemetryService telemetryService: ITelemetryService,
188
@IHoverService hoverService: IHoverService,
189
) {
190
super(container, options, contextMenuService, keybindingService, telemetryService, hoverService);
191
192
const menu = menuService.createMenu(menuId, contextKeyService);
193
this._store.add(menu);
194
195
const update = () => {
196
197
this.clear();
198
199
const actions = getActionBarActions(
200
menu.getActions(options?.menuOptions),
201
options?.toolbarOptions?.primaryGroup
202
);
203
204
super.update(actions.primary, actions.secondary);
205
};
206
this._store.add(menu.onDidChange(update));
207
update();
208
}
209
210
override dispose() {
211
super.dispose();
212
}
213
214
override update(_actions: IAction[]): void {
215
throw new Error('Use Menu or WorkbenchButtonBar');
216
}
217
}
218
219