activitybarActions.ts 22.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import 'vs/css!./media/activityaction';
import nls = require('vs/nls');
10 11
import DOM = require('vs/base/browser/dom');
import { TPromise } from 'vs/base/common/winjs.base';
J
Johannes Rieken 已提交
12 13
import { Builder, $ } from 'vs/base/browser/builder';
import { DelayedDragHandler } from 'vs/base/browser/dnd';
14
import { Action } from 'vs/base/common/actions';
15
import { BaseActionItem, Separator, IBaseActionItemOptions } from 'vs/base/browser/ui/actionbar/actionbar';
16
import { IActivityBarService, ProgressBadge, TextBadge, NumberBadge, IconBadge, IBadge } from 'vs/workbench/services/activity/common/activityBarService';
J
Johannes Rieken 已提交
17
import Event, { Emitter } from 'vs/base/common/event';
18 19 20 21 22
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
B
Benjamin Pasero 已提交
23
import { IActivity, IGlobalActivity } from 'vs/workbench/browser/activity';
24
import { dispose } from 'vs/base/common/lifecycle';
25
import { IViewletService, } from 'vs/workbench/services/viewlet/browser/viewlet';
26
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
27
import { IThemeService, ITheme, registerThemingParticipant, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
B
Benjamin Pasero 已提交
28
import { ACTIVITY_BAR_BADGE_FOREGROUND, ACTIVITY_BAR_BADGE_BACKGROUND, ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND, ACTIVITY_BAR_FOREGROUND } from 'vs/workbench/common/theme';
29
import { contrastBorder, activeContrastBorder, focusBorder } from 'vs/platform/theme/common/colorRegistry';
B
Benjamin Pasero 已提交
30
import { StandardMouseEvent } from "vs/base/browser/mouseEvent";
31 32
import { KeyCode } from "vs/base/common/keyCodes";
import { StandardKeyboardEvent } from "vs/base/browser/keyboardEvent";
B
Benjamin Pasero 已提交
33 34 35 36 37

export interface IViewletActivity {
	badge: IBadge;
	clazz: string;
}
E
Erich Gamma 已提交
38 39 40

export class ActivityAction extends Action {
	private badge: IBadge;
41
	private _onDidChangeBadge = new Emitter<this>();
E
Erich Gamma 已提交
42

43 44
	constructor(private _activity: IActivity) {
		super(_activity.id, _activity.name, _activity.cssClass);
E
Erich Gamma 已提交
45 46 47 48

		this.badge = null;
	}

B
Benjamin Pasero 已提交
49 50 51 52
	public get activity(): IActivity {
		return this._activity;
	}

53 54 55 56
	public get onDidChangeBadge(): Event<this> {
		return this._onDidChangeBadge.event;
	}

E
Erich Gamma 已提交
57 58
	public activate(): void {
		if (!this.checked) {
59
			this._setChecked(true);
E
Erich Gamma 已提交
60 61 62 63 64
		}
	}

	public deactivate(): void {
		if (this.checked) {
65
			this._setChecked(false);
E
Erich Gamma 已提交
66 67 68 69 70 71 72 73 74
		}
	}

	public getBadge(): IBadge {
		return this.badge;
	}

	public setBadge(badge: IBadge): void {
		this.badge = badge;
75
		this._onDidChangeBadge.fire(this);
E
Erich Gamma 已提交
76 77 78
	}
}

79 80 81 82 83 84 85 86 87 88 89
export class ViewletActivityAction extends ActivityAction {

	private static preventDoubleClickDelay = 300;

	private lastRun: number = 0;

	constructor(
		private viewlet: ViewletDescriptor,
		@IViewletService private viewletService: IViewletService,
		@IPartService private partService: IPartService
	) {
90
		super(viewlet);
91 92 93 94 95 96 97 98 99
	}

	public run(event): TPromise<any> {
		if (event instanceof MouseEvent && event.button === 2) {
			return TPromise.as(false); // do not run on right click
		}

		// prevent accident trigger on a doubleclick (to help nervous people)
		const now = Date.now();
100
		if (now > this.lastRun /* https://github.com/Microsoft/vscode/issues/25830 */ && now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) {
101 102 103 104 105 106 107 108 109
			return TPromise.as(true);
		}
		this.lastRun = now;

		const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
		const activeViewlet = this.viewletService.getActiveViewlet();

		// Hide sidebar if selected viewlet already visible
		if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
110
			return this.partService.setSideBarHidden(true);
111 112
		}

113 114
		return this.viewletService.openViewlet(this.viewlet.id, true)
			.then(() => this.activate());
115 116 117
	}
}

118
export class ActivityActionItem extends BaseActionItem {
B
Benjamin Pasero 已提交
119
	protected $container: Builder;
120 121 122
	protected $label: Builder;
	protected $badge: Builder;

B
Benjamin Pasero 已提交
123 124
	private $badgeContent: Builder;
	private mouseUpTimeout: number;
125 126 127 128 129 130 131 132 133

	constructor(
		action: ActivityAction,
		options: IBaseActionItemOptions,
		@IThemeService protected themeService: IThemeService
	) {
		super(null, action, options);

		this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose);
J
Joao Moreno 已提交
134
		action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
135 136
	}

B
Benjamin Pasero 已提交
137 138 139 140
	protected get activity(): IActivity {
		return (this._action as ActivityAction).activity;
	}

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
	protected updateStyles(): void {
		const theme = this.themeService.getTheme();

		// Label
		if (this.$label) {
			const background = theme.getColor(ACTIVITY_BAR_FOREGROUND);

			this.$label.style('background-color', background ? background.toString() : null);
		}

		// Badge
		if (this.$badgeContent) {
			const badgeForeground = theme.getColor(ACTIVITY_BAR_BADGE_FOREGROUND);
			const badgeBackground = theme.getColor(ACTIVITY_BAR_BADGE_BACKGROUND);
			const contrastBorderColor = theme.getColor(contrastBorder);

			this.$badgeContent.style('color', badgeForeground ? badgeForeground.toString() : null);
			this.$badgeContent.style('background-color', badgeBackground ? badgeBackground.toString() : null);

			this.$badgeContent.style('border-style', contrastBorderColor ? 'solid' : null);
			this.$badgeContent.style('border-width', contrastBorderColor ? '1px' : null);
			this.$badgeContent.style('border-color', contrastBorderColor ? contrastBorderColor.toString() : null);
		}
	}

	public render(container: HTMLElement): void {
		super.render(container);

B
Benjamin Pasero 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
		// Make the container tab-able for keyboard navigation
		this.$container = $(container).attr({
			tabIndex: '0',
			role: 'button',
			title: this.activity.name
		});

		// Try hard to prevent keyboard only focus feedback when using mouse
		this.$container.on(DOM.EventType.MOUSE_DOWN, () => {
			this.$container.addClass('clicked');
		});

		this.$container.on(DOM.EventType.MOUSE_UP, () => {
			if (this.mouseUpTimeout) {
				clearTimeout(this.mouseUpTimeout);
			}

			this.mouseUpTimeout = setTimeout(() => {
				this.$container.removeClass('clicked');
			}, 800); // delayed to prevent focus feedback from showing on mouse up
		});
J
Joao Moreno 已提交
190

191 192 193 194 195 196
		// Label
		this.$label = $('a.action-label').appendTo(this.builder);
		if (this.activity.cssClass) {
			this.$label.addClass(this.activity.cssClass);
		}

J
Joao Moreno 已提交
197
		this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => {
198 199 200 201 202 203 204 205
			this.$badgeContent = badge.div({ 'class': 'badge-content' });
		});

		this.$badge.hide();

		this.updateStyles();
	}

206 207 208 209
	private onThemeChange(theme: ITheme): void {
		this.updateStyles();
	}

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
	public setBadge(badge: IBadge): void {
		this.updateBadge(badge);
	}

	protected updateBadge(badge: IBadge): void {
		this.$badgeContent.empty();
		this.$badge.hide();

		if (badge) {

			// Number
			if (badge instanceof NumberBadge) {
				if (badge.number) {
					this.$badgeContent.text(badge.number > 99 ? '99+' : badge.number.toString());
					this.$badge.show();
				}
			}

			// Text
			else if (badge instanceof TextBadge) {
				this.$badgeContent.text(badge.text);
				this.$badge.show();
			}

			// Text
			else if (badge instanceof IconBadge) {
				this.$badge.show();
			}

			// Progress
			else if (badge instanceof ProgressBadge) {
				this.$badge.show();
			}

			this.$label.attr('aria-label', `${this.activity.name} - ${badge.getDescription()}`);
		}
	}

J
Joao Moreno 已提交
248 249 250 251 252 253 254
	private handleBadgeChangeEvenet(): void {
		const action = this.getAction();
		if (action instanceof ActivityAction) {
			this.updateBadge(action.getBadge());
		}
	}

255 256
	public dispose(): void {
		super.dispose();
B
Benjamin Pasero 已提交
257 258 259 260 261

		if (this.mouseUpTimeout) {
			clearTimeout(this.mouseUpTimeout);
		}

262 263
		this.$badge.destroy();
	}
264 265
}

266
export class ViewletActionItem extends ActivityActionItem {
267 268 269

	private static manageExtensionAction: ManageExtensionAction;
	private static toggleViewletPinnedAction: ToggleViewletPinnedAction;
270
	private static draggedViewlet: ViewletDescriptor;
271

E
Erich Gamma 已提交
272 273
	private _keybinding: string;
	private cssClass: string;
274

275
	constructor(
276
		private action: ViewletActivityAction,
277
		@IContextMenuService private contextMenuService: IContextMenuService,
278
		@IActivityBarService private activityBarService: IActivityBarService,
279
		@IKeybindingService private keybindingService: IKeybindingService,
B
Benjamin Pasero 已提交
280
		@IInstantiationService instantiationService: IInstantiationService,
281
		@IThemeService themeService: IThemeService
282
	) {
283
		super(action, { draggable: true }, themeService);
E
Erich Gamma 已提交
284 285

		this.cssClass = action.class;
286
		this._keybinding = this.getKeybindingLabel(this.viewlet.id);
287

288 289
		if (!ViewletActionItem.manageExtensionAction) {
			ViewletActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
290 291
		}

292 293
		if (!ViewletActionItem.toggleViewletPinnedAction) {
			ViewletActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0);
294
		}
B
Benjamin Pasero 已提交
295 296
	}

B
Benjamin Pasero 已提交
297 298 299 300
	private get viewlet(): ViewletDescriptor {
		return this.action.activity as ViewletDescriptor;
	}

301
	private getKeybindingLabel(id: string): string {
302
		const kb = this.keybindingService.lookupKeybinding(id);
B
Benjamin Pasero 已提交
303
		if (kb) {
304
			return kb.getLabel();
305 306 307
		}

		return null;
E
Erich Gamma 已提交
308 309 310 311 312
	}

	public render(container: HTMLElement): void {
		super.render(container);

313
		this.$container.on('contextmenu', e => {
314 315 316
			DOM.EventHelper.stop(e, true);

			this.showContextMenu(container);
317
		});
318

319
		// Allow to drag
320
		this.$container.on(DOM.EventType.DRAG_START, (e: DragEvent) => {
321 322 323 324 325 326 327 328 329 330 331
			e.dataTransfer.effectAllowed = 'move';
			this.setDraggedViewlet(this.viewlet);

			// Trigger the action even on drag start to prevent clicks from failing that started a drag
			if (!this.getAction().checked) {
				this.getAction().run();
			}
		});

		// Drag enter
		let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470
332
		this.$container.on(DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
333
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
334 335
			if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
				counter++;
336
				this.updateFromDragging(container, true);
337 338 339 340
			}
		});

		// Drag leave
341
		this.$container.on(DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
342
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
343 344 345
			if (draggedViewlet) {
				counter--;
				if (counter === 0) {
346
					this.updateFromDragging(container, false);
347 348 349 350 351
				}
			}
		});

		// Drag end
352
		this.$container.on(DOM.EventType.DRAG_END, (e: DragEvent) => {
353
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
354 355
			if (draggedViewlet) {
				counter = 0;
356
				this.updateFromDragging(container, false);
357

358
				ViewletActionItem.clearDraggedViewlet();
359 360 361 362
			}
		});

		// Drop
363
		this.$container.on(DOM.EventType.DROP, (e: DragEvent) => {
364 365
			DOM.EventHelper.stop(e, true);

366
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
367
			if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
368
				this.updateFromDragging(container, false);
369
				ViewletActionItem.clearDraggedViewlet();
370 371 372 373

				this.activityBarService.move(draggedViewlet.id, this.viewlet.id);
			}
		});
B
Benjamin Pasero 已提交
374

375 376 377 378 379
		// Keybinding
		this.keybinding = this._keybinding; // force update

		// Activate on drag over to reveal targets
		[this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => {
380
			if (!ViewletActionItem.getDraggedViewlet() && !this.getAction().checked) {
381 382 383 384
				this.getAction().run();
			}
		}));

B
Benjamin Pasero 已提交
385
		this.updateStyles();
386 387
	}

388 389 390 391 392 393 394
	private updateFromDragging(element: HTMLElement, isDragging: boolean): void {
		const theme = this.themeService.getTheme();
		const dragBackground = theme.getColor(ACTIVITY_BAR_DRAG_AND_DROP_BACKGROUND);

		element.style.backgroundColor = isDragging && dragBackground ? dragBackground.toString() : null;
	}

B
Benjamin Pasero 已提交
395
	public static getDraggedViewlet(): ViewletDescriptor {
396
		return ViewletActionItem.draggedViewlet;
397 398 399
	}

	private setDraggedViewlet(viewlet: ViewletDescriptor): void {
400
		ViewletActionItem.draggedViewlet = viewlet;
401 402
	}

B
Benjamin Pasero 已提交
403
	public static clearDraggedViewlet(): void {
404
		ViewletActionItem.draggedViewlet = void 0;
E
Erich Gamma 已提交
405 406
	}

407
	private showContextMenu(container: HTMLElement): void {
408
		const actions: Action[] = [ViewletActionItem.toggleViewletPinnedAction];
409 410
		if (this.viewlet.extensionId) {
			actions.push(new Separator());
411
			actions.push(ViewletActionItem.manageExtensionAction);
412 413 414 415
		}

		const isPinned = this.activityBarService.isPinned(this.viewlet.id);
		if (isPinned) {
416
			ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Remove from Activity Bar");
417
		} else {
418
			ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar");
419 420 421 422 423 424 425 426 427
		}

		this.contextMenuService.showContextMenu({
			getAnchor: () => container,
			getActionsContext: () => this.viewlet,
			getActions: () => TPromise.as(actions)
		});
	}

E
Erich Gamma 已提交
428
	public focus(): void {
429
		this.$container.domFocus();
E
Erich Gamma 已提交
430 431 432 433 434
	}

	public set keybinding(keybinding: string) {
		this._keybinding = keybinding;

435
		if (!this.$label) {
E
Erich Gamma 已提交
436 437 438 439 440
			return;
		}

		let title: string;
		if (keybinding) {
441
			title = nls.localize('titleKeybinding', "{0} ({1})", this.activity.name, keybinding);
E
Erich Gamma 已提交
442
		} else {
443
			title = this.activity.name;
E
Erich Gamma 已提交
444 445
		}

446
		this.$label.title(title);
E
Erich Gamma 已提交
447 448 449
		this.$badge.title(title);
	}

450
	protected _updateClass(): void {
E
Erich Gamma 已提交
451 452 453 454 455 456 457 458
		if (this.cssClass) {
			this.$badge.removeClass(this.cssClass);
		}

		this.cssClass = this.getAction().class;
		this.$badge.addClass(this.cssClass);
	}

459
	protected _updateChecked(): void {
E
Erich Gamma 已提交
460
		if (this.getAction().checked) {
461
			this.$container.addClass('active');
E
Erich Gamma 已提交
462
		} else {
463
			this.$container.removeClass('active');
E
Erich Gamma 已提交
464 465 466
		}
	}

467
	protected _updateEnabled(): void {
E
Erich Gamma 已提交
468 469 470 471 472 473 474 475 476 477
		if (this.getAction().enabled) {
			this.builder.removeClass('disabled');
		} else {
			this.builder.addClass('disabled');
		}
	}

	public dispose(): void {
		super.dispose();

478
		ViewletActionItem.clearDraggedViewlet();
479

480
		this.$label.destroy();
E
Erich Gamma 已提交
481
	}
482 483
}

484 485 486 487 488
export class ViewletOverflowActivityAction extends ActivityAction {

	constructor(
		private showMenu: () => void
	) {
489 490 491 492 493
		super({
			id: 'activitybar.additionalViewlets.action',
			name: nls.localize('additionalViews', "Additional Views"),
			cssClass: 'toggle-more'
		});
494 495 496 497 498 499 500 501 502
	}

	public run(event): TPromise<any> {
		this.showMenu();

		return TPromise.as(true);
	}
}

503
export class ViewletOverflowActivityActionItem extends ActivityActionItem {
504 505 506 507 508 509 510 511 512 513 514
	private name: string;
	private cssClass: string;
	private actions: OpenViewletAction[];

	constructor(
		action: ActivityAction,
		private getOverflowingViewlets: () => ViewletDescriptor[],
		private getBadge: (viewlet: ViewletDescriptor) => IBadge,
		@IInstantiationService private instantiationService: IInstantiationService,
		@IViewletService private viewletService: IViewletService,
		@IContextMenuService private contextMenuService: IContextMenuService,
515
		@IThemeService themeService: IThemeService
516
	) {
517
		super(action, null, themeService);
518 519 520 521 522

		this.cssClass = action.class;
		this.name = action.label;
	}

523 524 525 526 527 528 529 530 531 532 533
	protected updateStyles(): void {
		const theme = this.themeService.getTheme();

		// Label
		if (this.$label) {
			const background = theme.getColor(ACTIVITY_BAR_FOREGROUND);

			this.$label.style('background-color', background ? background.toString() : null);
		}
	}

534 535 536
	public render(container: HTMLElement): void {
		super.render(container);

537
		this.$label = $('a.action-label').attr({
538 539 540 541 542
			tabIndex: '0',
			role: 'button',
			title: this.name,
			class: this.cssClass
		}).appendTo(this.builder);
543 544

		this.updateStyles();
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
	}

	public showMenu(): void {
		if (this.actions) {
			dispose(this.actions);
		}

		this.actions = this.getActions();

		this.contextMenuService.showContextMenu({
			getAnchor: () => this.builder.getHTMLElement(),
			getActions: () => TPromise.as(this.actions),
			onHide: () => dispose(this.actions)
		});
	}

	private getActions(): OpenViewletAction[] {
		const activeViewlet = this.viewletService.getActiveViewlet();

		return this.getOverflowingViewlets().map(viewlet => {
			const action = this.instantiationService.createInstance(OpenViewletAction, viewlet);
			action.radio = activeViewlet && activeViewlet.getId() === action.id;

			const badge = this.getBadge(action.viewlet);
			let suffix: string | number;
			if (badge instanceof NumberBadge) {
				suffix = badge.number;
			} else if (badge instanceof TextBadge) {
				suffix = badge.text;
			}

			if (suffix) {
				action.label = nls.localize('numberBadge', "{0} ({1})", action.viewlet.name, suffix);
			} else {
				action.label = action.viewlet.name;
			}

			return action;
		});
	}

	public dispose(): void {
		super.dispose();

		this.actions = dispose(this.actions);
	}
}

593 594 595 596 597
class ManageExtensionAction extends Action {

	constructor(
		@ICommandService private commandService: ICommandService
	) {
598
		super('activitybar.manage.extension', nls.localize('manageExtension', "Manage Extension"));
599 600
	}

601 602
	public run(viewlet: ViewletDescriptor): TPromise<any> {
		return this.commandService.executeCommand('_extensions.manage', viewlet.extensionId);
603
	}
604 605 606 607 608
}

class OpenViewletAction extends Action {

	constructor(
B
Benjamin Pasero 已提交
609
		private _viewlet: ViewletDescriptor,
610 611 612
		@IPartService private partService: IPartService,
		@IViewletService private viewletService: IViewletService
	) {
B
Benjamin Pasero 已提交
613 614 615 616 617
		super(_viewlet.id, _viewlet.name);
	}

	public get viewlet(): ViewletDescriptor {
		return this._viewlet;
618 619 620 621 622 623 624 625
	}

	public run(): TPromise<any> {
		const sideBarVisible = this.partService.isVisible(Parts.SIDEBAR_PART);
		const activeViewlet = this.viewletService.getActiveViewlet();

		// Hide sidebar if selected viewlet already visible
		if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
626
			return this.partService.setSideBarHidden(true);
627 628
		}

629
		return this.viewletService.openViewlet(this.viewlet.id, true);
630 631 632 633 634 635 636 637 638
	}
}

export class ToggleViewletPinnedAction extends Action {

	constructor(
		private viewlet: ViewletDescriptor,
		@IActivityBarService private activityBarService: IActivityBarService
	) {
639
		super('activitybar.show.toggleViewletPinned', viewlet ? viewlet.name : nls.localize('toggle', "Toggle View Pinned"));
640 641 642 643 644 645 646 647 648 649 650 651 652

		this.checked = this.viewlet && this.activityBarService.isPinned(this.viewlet.id);
	}

	public run(context?: ViewletDescriptor): TPromise<any> {
		const viewlet = this.viewlet || context;

		if (this.activityBarService.isPinned(viewlet.id)) {
			this.activityBarService.unpin(viewlet.id);
		} else {
			this.activityBarService.pin(viewlet.id);
		}

653 654
		return TPromise.as(true);
	}
655 656
}

B
Benjamin Pasero 已提交
657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673
export class GlobalActivityAction extends ActivityAction {

	constructor(activity: IGlobalActivity) {
		super(activity);
	}
}

export class GlobalActivityActionItem extends ActivityActionItem {

	constructor(
		action: GlobalActivityAction,
		@IThemeService themeService: IThemeService,
		@IContextMenuService protected contextMenuService: IContextMenuService
	) {
		super(action, { draggable: false }, themeService);
	}

674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696
	public render(container: HTMLElement): void {
		super.render(container);

		// Context menus are triggered on mouse down so that an item can be picked
		// and executed with releasing the mouse over it
		this.$container.on(DOM.EventType.MOUSE_DOWN, (e: MouseEvent) => {
			DOM.EventHelper.stop(e, true);

			const event = new StandardMouseEvent(e);
			this.showContextMenu({ x: event.posx, y: event.posy });
		});

		this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
			let event = new StandardKeyboardEvent(e);
			if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
				DOM.EventHelper.stop(e, true);

				this.showContextMenu(this.$container.getHTMLElement());
			}
		});
	}

	private showContextMenu(location: HTMLElement | { x: number, y: number }): void {
B
Benjamin Pasero 已提交
697 698 699 700 701
		const globalAction = this._action as GlobalActivityAction;
		const activity = globalAction.activity as IGlobalActivity;
		const actions = activity.getActions();

		this.contextMenuService.showContextMenu({
702
			getAnchor: () => location,
B
Benjamin Pasero 已提交
703 704 705 706 707 708
			getActions: () => TPromise.as(actions),
			onHide: () => dispose(actions)
		});
	}
}

709 710 711
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

	// Styling with Outline color (e.g. high contrast theme)
712
	const outline = theme.getColor(activeContrastBorder);
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752
	if (outline) {
		collector.addRule(`
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:before {
				content: "";
				position: absolute;
				top: 9px;
				left: 9px;
				height: 32px;
				width: 32px;
				opacity: 0.6;
			}

			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before {
				outline: 1px solid;
			}

			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
				outline: 1px dashed;
			}

			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
				opacity: 1;
			}

			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
				border-left-color: ${outline};
			}

			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
				outline-color: ${outline};
			}
		`);
	}

	// Styling without outline color
	else {
753
		const focusBorderColor = theme.getColor(focusBorder);
754 755 756 757 758 759 760
		if (focusBorderColor) {
			collector.addRule(`
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active .action-label,
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus .action-label,
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover .action-label {
					opacity: 1;
				}
761

762 763 764
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label {
					opacity: 0.6;
				}
765

766 767 768 769 770
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
					border-left-color: ${focusBorderColor};
				}
			`);
		}
771 772
	}
});