Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/browser/composite.ts
3294 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 { IAction, IActionRunner, ActionRunner } from '../../base/common/actions.js';
7
import { Component } from '../common/component.js';
8
import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js';
9
import { IComposite, ICompositeControl } from '../common/composite.js';
10
import { Event, Emitter } from '../../base/common/event.js';
11
import { IThemeService } from '../../platform/theme/common/themeService.js';
12
import { IConstructorSignature, IInstantiationService } from '../../platform/instantiation/common/instantiation.js';
13
import { trackFocus, Dimension, IDomPosition } from '../../base/browser/dom.js';
14
import { IStorageService } from '../../platform/storage/common/storage.js';
15
import { Disposable } from '../../base/common/lifecycle.js';
16
import { assertReturnsDefined } from '../../base/common/types.js';
17
import { IActionViewItem } from '../../base/browser/ui/actionbar/actionbar.js';
18
import { MenuId } from '../../platform/actions/common/actions.js';
19
import { IBoundarySashes } from '../../base/browser/ui/sash/sash.js';
20
import { IBaseActionViewItemOptions } from '../../base/browser/ui/actionbar/actionViewItems.js';
21
22
/**
23
* Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
24
* can be open in the sidebar, and only one composite can be open in the panel.
25
*
26
* Each composite has a minimized representation that is good enough to provide some
27
* information about the state of the composite data.
28
*
29
* The workbench will keep a composite alive after it has been created and show/hide it based on
30
* user interaction. The lifecycle of a composite goes in the order create(), setVisible(true|false),
31
* layout(), focus(), dispose(). During use of the workbench, a composite will often receive a setVisible,
32
* layout and focus call, but only one create and dispose call.
33
*/
34
export abstract class Composite extends Component implements IComposite {
35
36
private readonly _onTitleAreaUpdate = this._register(new Emitter<void>());
37
readonly onTitleAreaUpdate = this._onTitleAreaUpdate.event;
38
39
protected _onDidFocus: Emitter<void> | undefined;
40
get onDidFocus(): Event<void> {
41
if (!this._onDidFocus) {
42
this._onDidFocus = this.registerFocusTrackEvents().onDidFocus;
43
}
44
45
return this._onDidFocus.event;
46
}
47
48
private _onDidBlur: Emitter<void> | undefined;
49
get onDidBlur(): Event<void> {
50
if (!this._onDidBlur) {
51
this._onDidBlur = this.registerFocusTrackEvents().onDidBlur;
52
}
53
54
return this._onDidBlur.event;
55
}
56
57
private _hasFocus = false;
58
hasFocus(): boolean {
59
return this._hasFocus;
60
}
61
62
private registerFocusTrackEvents(): { onDidFocus: Emitter<void>; onDidBlur: Emitter<void> } {
63
const container = assertReturnsDefined(this.getContainer());
64
const focusTracker = this._register(trackFocus(container));
65
66
const onDidFocus = this._onDidFocus = this._register(new Emitter<void>());
67
this._register(focusTracker.onDidFocus(() => {
68
this._hasFocus = true;
69
70
onDidFocus.fire();
71
}));
72
73
const onDidBlur = this._onDidBlur = this._register(new Emitter<void>());
74
this._register(focusTracker.onDidBlur(() => {
75
this._hasFocus = false;
76
77
onDidBlur.fire();
78
}));
79
80
return { onDidFocus, onDidBlur };
81
}
82
83
protected actionRunner: IActionRunner | undefined;
84
85
private visible = false;
86
private parent: HTMLElement | undefined;
87
88
constructor(
89
id: string,
90
protected readonly telemetryService: ITelemetryService,
91
themeService: IThemeService,
92
storageService: IStorageService
93
) {
94
super(id, themeService, storageService);
95
}
96
97
getTitle(): string | undefined {
98
return undefined;
99
}
100
101
/**
102
* Note: Clients should not call this method, the workbench calls this
103
* method. Calling it otherwise may result in unexpected behavior.
104
*
105
* Called to create this composite on the provided parent. This method is only
106
* called once during the lifetime of the workbench.
107
* Note that DOM-dependent calculations should be performed from the setVisible()
108
* call. Only then the composite will be part of the DOM.
109
*/
110
create(parent: HTMLElement): void {
111
this.parent = parent;
112
}
113
114
/**
115
* Returns the container this composite is being build in.
116
*/
117
getContainer(): HTMLElement | undefined {
118
return this.parent;
119
}
120
121
/**
122
* Note: Clients should not call this method, the workbench calls this
123
* method. Calling it otherwise may result in unexpected behavior.
124
*
125
* Called to indicate that the composite has become visible or hidden. This method
126
* is called more than once during workbench lifecycle depending on the user interaction.
127
* The composite will be on-DOM if visible is set to true and off-DOM otherwise.
128
*
129
* Typically this operation should be fast though because setVisible might be called many times during a session.
130
* If there is a long running operation it is fine to have it running in the background asyncly and return before.
131
*/
132
setVisible(visible: boolean): void {
133
if (this.visible !== !!visible) {
134
this.visible = visible;
135
}
136
}
137
138
/**
139
* Called when this composite should receive keyboard focus.
140
*/
141
focus(): void {
142
// Subclasses can implement
143
}
144
145
/**
146
* Layout the contents of this composite using the provided dimensions.
147
*/
148
abstract layout(dimension: Dimension, position?: IDomPosition): void;
149
150
/**
151
* Set boundary sashes for this composite. These are used to create
152
* draggable corner areas with inner sashes.
153
*/
154
abstract setBoundarySashes(sashes: IBoundarySashes): void;
155
156
/**
157
*
158
* @returns the action runner for this composite
159
*/
160
getMenuIds(): readonly MenuId[] {
161
return [];
162
}
163
164
/**
165
* Returns an array of actions to show in the action bar of the composite.
166
*/
167
getActions(): readonly IAction[] {
168
return [];
169
}
170
171
/**
172
* Returns an array of actions to show in the action bar of the composite
173
* in a less prominent way then action from getActions.
174
*/
175
getSecondaryActions(): readonly IAction[] {
176
return [];
177
}
178
179
/**
180
* Returns an array of actions to show in the context menu of the composite
181
*/
182
getContextMenuActions(): readonly IAction[] {
183
return [];
184
}
185
186
/**
187
* For any of the actions returned by this composite, provide an IActionViewItem in
188
* cases where the implementor of the composite wants to override the presentation
189
* of an action. Returns undefined to indicate that the action is not rendered through
190
* an action item.
191
*/
192
getActionViewItem(action: IAction, options: IBaseActionViewItemOptions): IActionViewItem | undefined {
193
return undefined;
194
}
195
196
/**
197
* Provide a context to be passed to the toolbar.
198
*/
199
getActionsContext(): unknown {
200
return null;
201
}
202
203
/**
204
* Returns the instance of IActionRunner to use with this composite for the
205
* composite tool bar.
206
*/
207
getActionRunner(): IActionRunner {
208
if (!this.actionRunner) {
209
this.actionRunner = this._register(new ActionRunner());
210
}
211
212
return this.actionRunner;
213
}
214
215
/**
216
* Method for composite implementors to indicate to the composite container that the title or the actions
217
* of the composite have changed. Calling this method will cause the container to ask for title (getTitle())
218
* and actions (getActions(), getSecondaryActions()) if the composite is visible or the next time the composite
219
* gets visible.
220
*/
221
protected updateTitleArea(): void {
222
this._onTitleAreaUpdate.fire();
223
}
224
225
/**
226
* Returns true if this composite is currently visible and false otherwise.
227
*/
228
isVisible(): boolean {
229
return this.visible;
230
}
231
232
/**
233
* Returns the underlying composite control or `undefined` if it is not accessible.
234
*/
235
getControl(): ICompositeControl | undefined {
236
return undefined;
237
}
238
}
239
240
/**
241
* A composite descriptor is a lightweight descriptor of a composite in the workbench.
242
*/
243
export abstract class CompositeDescriptor<T extends Composite> {
244
245
constructor(
246
private readonly ctor: IConstructorSignature<T>,
247
readonly id: string,
248
readonly name: string,
249
readonly cssClass?: string,
250
readonly order?: number,
251
readonly requestedIndex?: number,
252
) { }
253
254
instantiate(instantiationService: IInstantiationService): T {
255
return instantiationService.createInstance(this.ctor);
256
}
257
}
258
259
export abstract class CompositeRegistry<T extends Composite> extends Disposable {
260
261
private readonly _onDidRegister = this._register(new Emitter<CompositeDescriptor<T>>());
262
readonly onDidRegister = this._onDidRegister.event;
263
264
private readonly _onDidDeregister = this._register(new Emitter<CompositeDescriptor<T>>());
265
readonly onDidDeregister = this._onDidDeregister.event;
266
267
private readonly composites: CompositeDescriptor<T>[] = [];
268
269
protected registerComposite(descriptor: CompositeDescriptor<T>): void {
270
if (this.compositeById(descriptor.id)) {
271
return;
272
}
273
274
this.composites.push(descriptor);
275
this._onDidRegister.fire(descriptor);
276
}
277
278
protected deregisterComposite(id: string): void {
279
const descriptor = this.compositeById(id);
280
if (!descriptor) {
281
return;
282
}
283
284
this.composites.splice(this.composites.indexOf(descriptor), 1);
285
this._onDidDeregister.fire(descriptor);
286
}
287
288
getComposite(id: string): CompositeDescriptor<T> | undefined {
289
return this.compositeById(id);
290
}
291
292
protected getComposites(): CompositeDescriptor<T>[] {
293
return this.composites.slice(0);
294
}
295
296
private compositeById(id: string): CompositeDescriptor<T> | undefined {
297
return this.composites.find(composite => composite.id === id);
298
}
299
}
300
301