composite.ts 8.2 KB
Newer Older
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

J
Johannes Rieken 已提交
6 7 8
import { TPromise } from 'vs/base/common/winjs.base';
import { IAction, IActionRunner, ActionRunner } from 'vs/base/common/actions';
import { IActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
B
Benjamin Pasero 已提交
9
import { Component } from 'vs/workbench/common/component';
J
Johannes Rieken 已提交
10
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
B
Benjamin Pasero 已提交
11
import { IComposite, ICompositeControl } from 'vs/workbench/common/composite';
M
Matt Bierner 已提交
12
import { Event, Emitter } from 'vs/base/common/event';
13
import { IThemeService } from 'vs/platform/theme/common/themeService';
14
import { IConstructorSignature0, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
B
Benjamin Pasero 已提交
15
import { trackFocus, Dimension } from 'vs/base/browser/dom';
16 17

/**
I
isidor 已提交
18 19 20
 * Composites are layed out in the sidebar and panel part of the workbench. At a time only one composite
 * can be open in the sidebar, and only one composite can be open in the panel.
 * Each composite has a minimized representation that is good enough to provide some
21 22 23 24 25 26
 * information about the state of the composite data.
 * The workbench will keep a composite alive after it has been created and show/hide it based on
 * user interaction. The lifecycle of a composite goes in the order create(), setVisible(true|false),
 * layout(), focus(), dispose(). During use of the workbench, a composite will often receive a setVisible,
 * layout and focus call, but only one create and dispose call.
 */
B
Benjamin Pasero 已提交
27
export abstract class Composite extends Component implements IComposite {
M
Matt Bierner 已提交
28

B
Benjamin Pasero 已提交
29 30
	private readonly _onTitleAreaUpdate: Emitter<void> = this._register(new Emitter<void>());
	get onTitleAreaUpdate(): Event<void> { return this._onTitleAreaUpdate.event; }
31

B
Benjamin Pasero 已提交
32 33 34
	private _onDidFocus: Emitter<void>;
	get onDidFocus(): Event<void> {
		if (!this._onDidFocus) {
S
Sandeep Somavarapu 已提交
35 36 37 38
			this._registerFocusTrackEvents();
		}
		return this._onDidFocus.event;
	}
B
Benjamin Pasero 已提交
39

S
Sandeep Somavarapu 已提交
40 41 42 43
	private _onDidBlur: Emitter<void>;
	get onDidBlur(): Event<void> {
		if (!this._onDidBlur) {
			this._registerFocusTrackEvents();
B
Benjamin Pasero 已提交
44
		}
S
Sandeep Somavarapu 已提交
45 46
		return this._onDidBlur.event;
	}
B
Benjamin Pasero 已提交
47

S
Sandeep Somavarapu 已提交
48 49 50 51 52 53 54
	private _registerFocusTrackEvents(): void {
		this._onDidFocus = this._register(new Emitter<void>());
		this._onDidBlur = this._register(new Emitter<void>());

		const focusTracker = this._register(trackFocus(this.getContainer()));
		this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
		this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
B
Benjamin Pasero 已提交
55
	}
56 57 58

	protected actionRunner: IActionRunner;

B
Benjamin Pasero 已提交
59 60 61
	private visible: boolean;
	private parent: HTMLElement;

62 63 64
	/**
	 * Create a new composite with the given ID and context.
	 */
65 66 67
	constructor(
		id: string,
		private _telemetryService: ITelemetryService,
B
Benjamin Pasero 已提交
68
		themeService: IThemeService
69 70
	) {
		super(id, themeService);
71 72 73 74

		this.visible = false;
	}

B
Benjamin Pasero 已提交
75
	getTitle(): string {
76 77 78
		return null;
	}

B
Benjamin Pasero 已提交
79
	protected get telemetryService(): ITelemetryService {
80 81 82 83
		return this._telemetryService;
	}

	/**
B
Benjamin Pasero 已提交
84
	 * Note: Clients should not call this method, the workbench calls this
85 86
	 * method. Calling it otherwise may result in unexpected behavior.
	 *
87
	 * Called to create this composite on the provided parent. This method is only
88 89 90 91
	 * called once during the lifetime of the workbench.
	 * Note that DOM-dependent calculations should be performed from the setVisible()
	 * call. Only then the composite will be part of the DOM.
	 */
B
Benjamin Pasero 已提交
92
	create(parent: HTMLElement): TPromise<void> {
93 94
		this.parent = parent;

95
		return Promise.resolve(null);
96 97
	}

B
Benjamin Pasero 已提交
98
	updateStyles(): void {
B
Benjamin Pasero 已提交
99 100 101
		super.updateStyles();
	}

102 103 104
	/**
	 * Returns the container this composite is being build in.
	 */
B
Benjamin Pasero 已提交
105
	getContainer(): HTMLElement {
106 107 108 109
		return this.parent;
	}

	/**
B
Benjamin Pasero 已提交
110
	 * Note: Clients should not call this method, the workbench calls this
111 112 113 114 115 116 117 118 119 120
	 * method. Calling it otherwise may result in unexpected behavior.
	 *
	 * Called to indicate that the composite has become visible or hidden. This method
	 * is called more than once during workbench lifecycle depending on the user interaction.
	 * The composite will be on-DOM if visible is set to true and off-DOM otherwise.
	 *
	 * The returned promise is complete when the composite is visible. As such it is valid
	 * to do a long running operation from this call. Typically this operation should be
	 * fast though because setVisible might be called many times during a session.
	 */
B
Benjamin Pasero 已提交
121
	setVisible(visible: boolean): TPromise<void> {
122 123
		this.visible = visible;

124
		return Promise.resolve(null);
125 126 127 128 129
	}

	/**
	 * Called when this composite should receive keyboard focus.
	 */
B
Benjamin Pasero 已提交
130
	focus(): void {
131 132 133 134 135 136
		// Subclasses can implement
	}

	/**
	 * Layout the contents of this composite using the provided dimensions.
	 */
B
Benjamin Pasero 已提交
137
	abstract layout(dimension: Dimension): void;
138 139 140 141

	/**
	 * Returns an array of actions to show in the action bar of the composite.
	 */
B
Benjamin Pasero 已提交
142
	getActions(): IAction[] {
143 144 145 146 147 148 149
		return [];
	}

	/**
	 * Returns an array of actions to show in the action bar of the composite
	 * in a less prominent way then action from getActions.
	 */
B
Benjamin Pasero 已提交
150
	getSecondaryActions(): IAction[] {
151 152 153
		return [];
	}

154 155 156
	/**
	 * Returns an array of actions to show in the context menu of the composite
	 */
B
Benjamin Pasero 已提交
157
	getContextMenuActions(): IAction[] {
158 159 160
		return [];
	}

161 162 163 164 165 166
	/**
	 * For any of the actions returned by this composite, provide an IActionItem in
	 * cases where the implementor of the composite wants to override the presentation
	 * of an action. Returns null to indicate that the action is not rendered through
	 * an action item.
	 */
B
Benjamin Pasero 已提交
167
	getActionItem(action: IAction): IActionItem {
168 169 170 171 172 173 174
		return null;
	}

	/**
	 * Returns the instance of IActionRunner to use with this composite for the
	 * composite tool bar.
	 */
B
Benjamin Pasero 已提交
175
	getActionRunner(): IActionRunner {
176 177 178 179 180 181 182 183 184 185 186 187 188 189
		if (!this.actionRunner) {
			this.actionRunner = new ActionRunner();
		}

		return this.actionRunner;
	}

	/**
	 * Method for composite implementors to indicate to the composite container that the title or the actions
	 * of the composite have changed. Calling this method will cause the container to ask for title (getTitle())
	 * and actions (getActions(), getSecondaryActions()) if the composite is visible or the next time the composite
	 * gets visible.
	 */
	protected updateTitleArea(): void {
190
		this._onTitleAreaUpdate.fire();
191 192 193 194 195
	}

	/**
	 * Returns true if this composite is currently visible and false otherwise.
	 */
B
Benjamin Pasero 已提交
196
	isVisible(): boolean {
197 198 199 200 201 202
		return this.visible;
	}

	/**
	 * Returns the underlying composite control or null if it is not accessible.
	 */
B
Benjamin Pasero 已提交
203
	getControl(): ICompositeControl {
204 205 206
		return null;
	}
}
I
isidor 已提交
207 208

/**
B
Benjamin Pasero 已提交
209
 * A composite descriptor is a leightweight descriptor of a composite in the workbench.
I
isidor 已提交
210
 */
211
export abstract class CompositeDescriptor<T extends Composite> {
B
Benjamin Pasero 已提交
212 213 214 215 216 217
	id: string;
	name: string;
	cssClass: string;
	order: number;
	keybindingId: string;
	enabled: boolean;
I
isidor 已提交
218

219
	private ctor: IConstructorSignature0<T>;
I
isidor 已提交
220

I
isidor 已提交
221
	constructor(ctor: IConstructorSignature0<T>, id: string, name: string, cssClass?: string, order?: number, keybindingId?: string, ) {
222
		this.ctor = ctor;
I
isidor 已提交
223 224 225 226
		this.id = id;
		this.name = name;
		this.cssClass = cssClass;
		this.order = order;
227
		this.enabled = true;
I
isidor 已提交
228
		this.keybindingId = keybindingId;
I
isidor 已提交
229
	}
230

B
Benjamin Pasero 已提交
231
	instantiate(instantiationService: IInstantiationService): T {
232 233
		return instantiationService.createInstance(this.ctor);
	}
I
isidor 已提交
234
}
I
isidor 已提交
235

236
export abstract class CompositeRegistry<T extends Composite> {
237 238

	private readonly _onDidRegister: Emitter<CompositeDescriptor<T>> = new Emitter<CompositeDescriptor<T>>();
B
Benjamin Pasero 已提交
239
	get onDidRegister(): Event<CompositeDescriptor<T>> { return this._onDidRegister.event; }
I
isidor 已提交
240

B
Benjamin Pasero 已提交
241
	private composites: CompositeDescriptor<T>[] = [];
I
isidor 已提交
242 243

	protected registerComposite(descriptor: CompositeDescriptor<T>): void {
I
isidor 已提交
244
		if (this.compositeById(descriptor.id) !== null) {
I
isidor 已提交
245 246 247
			return;
		}

B
Benjamin Pasero 已提交
248
		this.composites.push(descriptor);
249
		this._onDidRegister.fire(descriptor);
I
isidor 已提交
250 251
	}

B
Benjamin Pasero 已提交
252
	getComposite(id: string): CompositeDescriptor<T> {
I
isidor 已提交
253
		return this.compositeById(id);
I
isidor 已提交
254 255
	}

B
Benjamin Pasero 已提交
256 257
	protected getComposites(): CompositeDescriptor<T>[] {
		return this.composites.slice(0);
I
isidor 已提交
258 259
	}

I
isidor 已提交
260
	private compositeById(id: string): CompositeDescriptor<T> {
B
Benjamin Pasero 已提交
261 262 263
		for (let i = 0; i < this.composites.length; i++) {
			if (this.composites[i].id === id) {
				return this.composites[i];
I
isidor 已提交
264 265 266 267 268
			}
		}

		return null;
	}
269
}