activitybarActions.ts 22.9 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/common/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 31 32
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
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
export class ViewletActivityAction extends ActivityAction {

	private static preventDoubleClickDelay = 300;

	private lastRun: number = 0;

	constructor(
B
Benjamin Pasero 已提交
86
		private viewlet: ViewletDescriptor,
87 88 89
		@IViewletService private viewletService: IViewletService,
		@IPartService private partService: IPartService
	) {
B
Benjamin Pasero 已提交
90
		super(viewlet);
91 92 93
	}

	public get descriptor(): ViewletDescriptor {
B
Benjamin Pasero 已提交
94
		return this.viewlet;
95 96
	}

97
	public run(event: any): TPromise<any> {
98 99 100 101 102 103
		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();
104
		if (now > this.lastRun /* https://github.com/Microsoft/vscode/issues/25830 */ && now - this.lastRun < ViewletActivityAction.preventDoubleClickDelay) {
105 106 107 108 109 110 111 112
			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
B
Benjamin Pasero 已提交
113
		if (sideBarVisible && activeViewlet && activeViewlet.getId() === this.viewlet.id) {
114
			return this.partService.setSideBarHidden(true);
115 116
		}

B
Benjamin Pasero 已提交
117
		return this.viewletService.openViewlet(this.viewlet.id, true).then(() => this.activate());
118 119 120
	}
}

121
export class ActivityActionItem extends BaseActionItem {
B
Benjamin Pasero 已提交
122
	protected $container: Builder;
123 124 125
	protected $label: Builder;
	protected $badge: Builder;

B
Benjamin Pasero 已提交
126 127
	private $badgeContent: Builder;
	private mouseUpTimeout: number;
128 129 130 131 132 133 134 135 136

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

		this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose);
J
Joao Moreno 已提交
137
		action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
138 139
	}

B
Benjamin Pasero 已提交
140 141 142 143
	protected get activity(): IActivity {
		return (this._action as ActivityAction).activity;
	}

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 169 170 171
	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 已提交
172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
		// 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 已提交
193

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

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

		this.$badge.hide();

		this.updateStyles();
	}

209 210 211 212
	private onThemeChange(theme: ITheme): void {
		this.updateStyles();
	}

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
	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();
			}
246
		}
247

248 249 250
		// Title
		let title: string;
		if (badge && badge.getDescription()) {
B
Benjamin Pasero 已提交
251 252 253 254 255
			if (this.activity.name) {
				title = nls.localize('badgeTitle', "{0} - {1}", this.activity.name, badge.getDescription());
			} else {
				title = badge.getDescription();
			}
256 257
		} else {
			title = this.activity.name;
258
		}
259 260 261 262 263 264 265

		[this.$label, this.$badge, this.$container].forEach(b => {
			if (b) {
				b.attr('aria-label', title);
				b.title(title);
			}
		});
266 267
	}

J
Joao Moreno 已提交
268 269 270 271 272 273 274
	private handleBadgeChangeEvenet(): void {
		const action = this.getAction();
		if (action instanceof ActivityAction) {
			this.updateBadge(action.getBadge());
		}
	}

275 276
	public dispose(): void {
		super.dispose();
B
Benjamin Pasero 已提交
277 278 279 280 281

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

282 283
		this.$badge.destroy();
	}
284 285
}

286
export class ViewletActionItem extends ActivityActionItem {
287 288 289

	private static manageExtensionAction: ManageExtensionAction;
	private static toggleViewletPinnedAction: ToggleViewletPinnedAction;
290
	private static draggedViewlet: ViewletDescriptor;
291

292
	private viewletActivity: IActivity;
E
Erich Gamma 已提交
293
	private cssClass: string;
294

295
	constructor(
296
		private action: ViewletActivityAction,
297
		@IContextMenuService private contextMenuService: IContextMenuService,
298
		@IActivityBarService private activityBarService: IActivityBarService,
299
		@IKeybindingService private keybindingService: IKeybindingService,
B
Benjamin Pasero 已提交
300
		@IInstantiationService instantiationService: IInstantiationService,
301
		@IThemeService themeService: IThemeService
302
	) {
303
		super(action, { draggable: true }, themeService);
E
Erich Gamma 已提交
304 305

		this.cssClass = action.class;
306

307 308
		if (!ViewletActionItem.manageExtensionAction) {
			ViewletActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
309 310
		}

311 312
		if (!ViewletActionItem.toggleViewletPinnedAction) {
			ViewletActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0);
313
		}
B
Benjamin Pasero 已提交
314 315
	}

316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
	protected get activity(): IActivity {
		if (!this.viewletActivity) {
			let activityName: string;

			const keybinding = this.getKeybindingLabel(this.viewlet.id);
			if (keybinding) {
				activityName = nls.localize('titleKeybinding', "{0} ({1})", this.viewlet.name, keybinding);
			} else {
				activityName = this.viewlet.name;
			}

			this.viewletActivity = {
				id: this.viewlet.id,
				cssClass: this.cssClass,
				name: activityName
			};
		}

		return this.viewletActivity;
	}

B
Benjamin Pasero 已提交
337
	private get viewlet(): ViewletDescriptor {
338
		return this.action.descriptor;
B
Benjamin Pasero 已提交
339 340
	}

341
	private getKeybindingLabel(id: string): string {
342
		const kb = this.keybindingService.lookupKeybinding(id);
B
Benjamin Pasero 已提交
343
		if (kb) {
344
			return kb.getLabel();
345 346 347
		}

		return null;
E
Erich Gamma 已提交
348 349 350 351 352
	}

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

353
		this.$container.on('contextmenu', e => {
354 355 356
			DOM.EventHelper.stop(e, true);

			this.showContextMenu(container);
357
		});
358

359
		// Allow to drag
360
		this.$container.on(DOM.EventType.DRAG_START, (e: DragEvent) => {
361 362 363 364 365 366 367 368 369 370 371
			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
372
		this.$container.on(DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
373
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
374 375
			if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
				counter++;
376
				this.updateFromDragging(container, true);
377 378 379 380
			}
		});

		// Drag leave
381
		this.$container.on(DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
382
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
383 384 385
			if (draggedViewlet) {
				counter--;
				if (counter === 0) {
386
					this.updateFromDragging(container, false);
387 388 389 390 391
				}
			}
		});

		// Drag end
392
		this.$container.on(DOM.EventType.DRAG_END, (e: DragEvent) => {
393
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
394 395
			if (draggedViewlet) {
				counter = 0;
396
				this.updateFromDragging(container, false);
397

398
				ViewletActionItem.clearDraggedViewlet();
399 400 401 402
			}
		});

		// Drop
403
		this.$container.on(DOM.EventType.DROP, (e: DragEvent) => {
404 405
			DOM.EventHelper.stop(e, true);

406
			const draggedViewlet = ViewletActionItem.getDraggedViewlet();
407
			if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
408
				this.updateFromDragging(container, false);
409
				ViewletActionItem.clearDraggedViewlet();
410 411 412 413

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

415 416
		// Activate on drag over to reveal targets
		[this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => {
417
			if (!ViewletActionItem.getDraggedViewlet() && !this.getAction().checked) {
418 419 420 421
				this.getAction().run();
			}
		}));

B
Benjamin Pasero 已提交
422
		this.updateStyles();
423 424
	}

425 426 427 428 429 430 431
	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 已提交
432
	public static getDraggedViewlet(): ViewletDescriptor {
433
		return ViewletActionItem.draggedViewlet;
434 435 436
	}

	private setDraggedViewlet(viewlet: ViewletDescriptor): void {
437
		ViewletActionItem.draggedViewlet = viewlet;
438 439
	}

B
Benjamin Pasero 已提交
440
	public static clearDraggedViewlet(): void {
441
		ViewletActionItem.draggedViewlet = void 0;
E
Erich Gamma 已提交
442 443
	}

444
	private showContextMenu(container: HTMLElement): void {
445
		const actions: Action[] = [ViewletActionItem.toggleViewletPinnedAction];
446 447
		if (this.viewlet.extensionId) {
			actions.push(new Separator());
448
			actions.push(ViewletActionItem.manageExtensionAction);
449 450 451 452
		}

		const isPinned = this.activityBarService.isPinned(this.viewlet.id);
		if (isPinned) {
B
Benjamin Pasero 已提交
453
			ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Hide from Activity Bar");
454
		} else {
455
			ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar");
456 457 458 459 460 461 462 463 464
		}

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

E
Erich Gamma 已提交
465
	public focus(): void {
466
		this.$container.domFocus();
E
Erich Gamma 已提交
467 468
	}

469
	protected _updateClass(): void {
E
Erich Gamma 已提交
470 471 472 473 474 475 476 477
		if (this.cssClass) {
			this.$badge.removeClass(this.cssClass);
		}

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

478
	protected _updateChecked(): void {
E
Erich Gamma 已提交
479
		if (this.getAction().checked) {
480
			this.$container.addClass('checked');
E
Erich Gamma 已提交
481
		} else {
482
			this.$container.removeClass('checked');
E
Erich Gamma 已提交
483 484 485
		}
	}

486
	protected _updateEnabled(): void {
E
Erich Gamma 已提交
487 488 489 490 491 492 493 494 495 496
		if (this.getAction().enabled) {
			this.builder.removeClass('disabled');
		} else {
			this.builder.addClass('disabled');
		}
	}

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

497
		ViewletActionItem.clearDraggedViewlet();
498

499
		this.$label.destroy();
E
Erich Gamma 已提交
500
	}
501 502
}

503 504 505 506 507
export class ViewletOverflowActivityAction extends ActivityAction {

	constructor(
		private showMenu: () => void
	) {
508 509 510 511 512
		super({
			id: 'activitybar.additionalViewlets.action',
			name: nls.localize('additionalViews', "Additional Views"),
			cssClass: 'toggle-more'
		});
513 514
	}

515
	public run(event: any): TPromise<any> {
516 517 518 519 520 521
		this.showMenu();

		return TPromise.as(true);
	}
}

522
export class ViewletOverflowActivityActionItem extends ActivityActionItem {
523 524 525 526 527 528 529 530 531 532 533
	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,
534
		@IThemeService themeService: IThemeService
535
	) {
536
		super(action, null, themeService);
537 538 539 540 541 542 543 544 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

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

	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);
	}
}

588 589 590 591 592
class ManageExtensionAction extends Action {

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

596 597
	public run(viewlet: ViewletDescriptor): TPromise<any> {
		return this.commandService.executeCommand('_extensions.manage', viewlet.extensionId);
598
	}
599 600 601 602 603
}

class OpenViewletAction extends Action {

	constructor(
B
Benjamin Pasero 已提交
604
		private _viewlet: ViewletDescriptor,
605 606 607
		@IPartService private partService: IPartService,
		@IViewletService private viewletService: IViewletService
	) {
B
Benjamin Pasero 已提交
608 609 610 611 612
		super(_viewlet.id, _viewlet.name);
	}

	public get viewlet(): ViewletDescriptor {
		return this._viewlet;
613 614 615 616 617 618 619 620
	}

	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) {
621
			return this.partService.setSideBarHidden(true);
622 623
		}

624
		return this.viewletService.openViewlet(this.viewlet.id, true);
625 626 627 628 629 630 631 632 633
	}
}

export class ToggleViewletPinnedAction extends Action {

	constructor(
		private viewlet: ViewletDescriptor,
		@IActivityBarService private activityBarService: IActivityBarService
	) {
634
		super('activitybar.show.toggleViewletPinned', viewlet ? viewlet.name : nls.localize('toggle', "Toggle View Pinned"));
635 636 637 638 639 640 641 642 643 644 645 646 647

		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);
		}

648 649
		return TPromise.as(true);
	}
650 651
}

B
Benjamin Pasero 已提交
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668
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);
	}

669 670 671 672 673 674
	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) => {
675
			this.onClick(e);
676 677
		});

678
		// Extra listener for keyboard interaction
679 680 681
		this.$container.on(DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
			let event = new StandardKeyboardEvent(e);
			if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) {
682
				this.onClick(e);
683 684 685 686
			}
		});
	}

687 688 689 690 691 692 693 694 695 696 697 698 699 700
	public onClick(event?: MouseEvent | KeyboardEvent): void {
		DOM.EventHelper.stop(event, true);

		let location: HTMLElement | { x: number, y: number };
		if (event instanceof MouseEvent) {
			const mouseEvent = new StandardMouseEvent(event);
			location = { x: mouseEvent.posx, y: mouseEvent.posy };
		} else {
			location = this.$container.getHTMLElement();
		}

		this.showContextMenu(location);
	}

701
	private showContextMenu(location: HTMLElement | { x: number, y: number }): void {
B
Benjamin Pasero 已提交
702 703 704 705 706
		const globalAction = this._action as GlobalActivityAction;
		const activity = globalAction.activity as IGlobalActivity;
		const actions = activity.getActions();

		this.contextMenuService.showContextMenu({
707
			getAnchor: () => location,
B
Benjamin Pasero 已提交
708 709 710 711 712 713
			getActions: () => TPromise.as(actions),
			onHide: () => dispose(actions)
		});
	}
}

714 715 716
registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {

	// Styling with Outline color (e.g. high contrast theme)
717
	const outline = theme.getColor(activeContrastBorder);
718 719 720 721 722 723 724 725 726 727 728 729 730
	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,
731 732 733
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active:hover:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before {
734 735 736 737 738 739 740 741
				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,
742
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
743 744 745 746 747 748 749 750 751 752
			.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,
753 754
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:before,
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked:hover:before,
755 756 757 758 759 760 761 762
			.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:hover:before {
				outline-color: ${outline};
			}
		`);
	}

	// Styling without outline color
	else {
763
		const focusBorderColor = theme.getColor(focusBorder);
764 765 766
		if (focusBorderColor) {
			collector.addRule(`
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.active .action-label,
767
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item.checked .action-label,
768 769 770 771
				.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;
				}
772

773 774 775
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item .action-label {
					opacity: 0.6;
				}
776

777 778 779 780 781
				.monaco-workbench > .activitybar > .content .monaco-action-bar .action-item:focus:before {
					border-left-color: ${focusBorderColor};
				}
			`);
		}
782 783
	}
});