Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/microsoft/vscode.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/vs/platform/update/common/updateIpc.ts21
-rw-r--r--src/vs/workbench/browser/activity.ts42
-rw-r--r--src/vs/workbench/browser/parts/activitybar/activitybarActions.ts271
-rw-r--r--src/vs/workbench/browser/parts/activitybar/activitybarPart.ts87
-rw-r--r--src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css3
-rw-r--r--src/vs/workbench/parts/update/electron-browser/media/update.contribution.css9
-rw-r--r--src/vs/workbench/parts/update/electron-browser/media/update.svg1
-rw-r--r--src/vs/workbench/parts/update/electron-browser/update.contribution.ts16
-rw-r--r--src/vs/workbench/parts/update/electron-browser/update.ts100
-rw-r--r--src/vs/workbench/services/activity/common/activityBarService.ts5
10 files changed, 408 insertions, 147 deletions
diff --git a/src/vs/platform/update/common/updateIpc.ts b/src/vs/platform/update/common/updateIpc.ts
index 2076719e98a..0dd76ca51f6 100644
--- a/src/vs/platform/update/common/updateIpc.ts
+++ b/src/vs/platform/update/common/updateIpc.ts
@@ -7,7 +7,8 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { IChannel, eventToCall, eventFromCall } from 'vs/base/parts/ipc/common/ipc';
-import Event from 'vs/base/common/event';
+import Event, { Emitter } from 'vs/base/common/event';
+import { onUnexpectedError } from 'vs/base/common/errors';
import { IUpdateService, IRawUpdate, State, IUpdate } from './update';
export interface IUpdateChannel extends IChannel {
@@ -18,6 +19,7 @@ export interface IUpdateChannel extends IChannel {
call(command: 'event:onStateChange'): TPromise<void>;
call(command: 'checkForUpdates', arg: boolean): TPromise<IUpdate>;
call(command: 'quitAndInstall'): TPromise<void>;
+ call(command: '_getInitialState'): TPromise<State>;
call(command: string, arg?: any): TPromise<any>;
}
@@ -34,6 +36,7 @@ export class UpdateChannel implements IUpdateChannel {
case 'event:onStateChange': return eventToCall(this.service.onStateChange);
case 'checkForUpdates': return this.service.checkForUpdates(arg);
case 'quitAndInstall': return this.service.quitAndInstall();
+ case '_getInitialState': return TPromise.as(this.service.state);
}
return undefined;
}
@@ -55,14 +58,24 @@ export class UpdateChannelClient implements IUpdateService {
private _onUpdateReady = eventFromCall<IRawUpdate>(this.channel, 'event:onUpdateReady');
get onUpdateReady(): Event<IRawUpdate> { return this._onUpdateReady; }
- private _onStateChange = eventFromCall<State>(this.channel, 'event:onStateChange');
- get onStateChange(): Event<State> { return this._onStateChange; }
+ private _onRemoteStateChange = eventFromCall<State>(this.channel, 'event:onStateChange');
+ private _onStateChange = new Emitter<State>();
+ get onStateChange(): Event<State> { return this._onStateChange.event; }
private _state: State = State.Uninitialized;
get state(): State { return this._state; };
- constructor(private channel: IChannel) {
+ constructor(private channel: IUpdateChannel) {
+ // always set this._state as the state changes
this.onStateChange(state => this._state = state);
+
+ channel.call('_getInitialState').done(state => {
+ // fire initial state
+ this._onStateChange.fire(state);
+
+ // fire subsequent states as they come in from remote
+ this._onRemoteStateChange(state => this._onStateChange.fire(state));
+ }, onUnexpectedError);
}
checkForUpdates(explicit: boolean): TPromise<IUpdate> {
diff --git a/src/vs/workbench/browser/activity.ts b/src/vs/workbench/browser/activity.ts
new file mode 100644
index 00000000000..5f23bef3a5e
--- /dev/null
+++ b/src/vs/workbench/browser/activity.ts
@@ -0,0 +1,42 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import { Registry } from 'vs/platform/platform';
+import { IAction } from 'vs/base/common/actions';
+import { IConstructorSignature0 } from 'vs/platform/instantiation/common/instantiation';
+
+export interface IActivity {
+ id: string;
+ name: string;
+ cssClass: string;
+}
+
+export interface IGlobalActivity extends IActivity {
+ getActions(): IAction[];
+}
+
+export const GlobalActivityExtensions = 'workbench.contributions.globalActivities';
+
+export interface IGlobalActivityRegistry {
+ registerActivity(descriptor: IConstructorSignature0<IGlobalActivity>): void;
+ getActivities(): IConstructorSignature0<IGlobalActivity>[];
+}
+
+export class GlobalActivityRegistry implements IGlobalActivityRegistry {
+
+ private activityDescriptors = new Set<IConstructorSignature0<IGlobalActivity>>();
+
+ registerActivity(descriptor: IConstructorSignature0<IGlobalActivity>): void {
+ this.activityDescriptors.add(descriptor);
+ }
+
+ getActivities(): IConstructorSignature0<IGlobalActivity>[] {
+ const result: IConstructorSignature0<IGlobalActivity>[] = [];
+ this.activityDescriptors.forEach(d => result.push(d));
+ return result;
+ }
+}
+
+Registry.add(GlobalActivityExtensions, new GlobalActivityRegistry()); \ No newline at end of file
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
index e1133c4e5ce..cc95a89bdd0 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts
@@ -20,6 +20,7 @@ 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';
+import { IActivity } from 'vs/workbench/browser/activity';
import { dispose } from 'vs/base/common/lifecycle';
import { IViewletService, } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPartService, Parts } from 'vs/workbench/services/part/common/partService';
@@ -31,8 +32,12 @@ export class ActivityAction extends Action {
private badge: IBadge;
private _onDidChangeBadge = new Emitter<this>();
- constructor(id: string, name: string, clazz: string) {
- super(id, name, clazz);
+ get activity(): IActivity {
+ return this._activity;
+ }
+
+ constructor(private _activity: IActivity) {
+ super(_activity.id, _activity.name, _activity.cssClass);
this.badge = null;
}
@@ -74,7 +79,7 @@ export class ViewletActivityAction extends ActivityAction {
@IViewletService private viewletService: IViewletService,
@IPartService private partService: IPartService
) {
- super(viewlet.id, viewlet.name, viewlet.cssClass);
+ super(viewlet);
}
public run(event): TPromise<any> {
@@ -102,7 +107,15 @@ export class ViewletActivityAction extends ActivityAction {
}
}
-export abstract class ThemableActivityActionItem extends BaseActionItem {
+export class ActivityActionItem extends BaseActionItem {
+
+ protected $label: Builder;
+ protected $badge: Builder;
+ private $badgeContent: Builder;
+
+ protected get activity(): IActivity {
+ return (this._action as ActivityAction).activity;
+ }
constructor(
action: ActivityAction,
@@ -112,33 +125,126 @@ export abstract class ThemableActivityActionItem extends BaseActionItem {
super(null, action, options);
this.themeService.onThemeChange(this.onThemeChange, this, this._callOnDispose);
+ action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
+ }
+
+ 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);
+
+ container.title = this.activity.name;
+
+ // Label
+ this.$label = $('a.action-label').appendTo(this.builder);
+ if (this.activity.cssClass) {
+ this.$label.addClass(this.activity.cssClass);
+ }
+
+ this.$badge = this.builder.clone().div({ 'class': 'badge' }, (badge: Builder) => {
+ this.$badgeContent = badge.div({ 'class': 'badge-content' });
+ });
+
+ this.$badge.hide();
+
+ this.updateStyles();
}
private onThemeChange(theme: ITheme): void {
this.updateStyles();
}
- protected abstract updateStyles(): void;
+ 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()}`);
+ }
+ }
+
+ private handleBadgeChangeEvenet(): void {
+ const action = this.getAction();
+ if (action instanceof ActivityAction) {
+ this.updateBadge(action.getBadge());
+ }
+ }
+
+ public dispose(): void {
+ super.dispose();
+ this.$badge.destroy();
+ }
}
-export class ActivityActionItem extends ThemableActivityActionItem {
+export class ViewletActionItem extends ActivityActionItem {
private static manageExtensionAction: ManageExtensionAction;
private static toggleViewletPinnedAction: ToggleViewletPinnedAction;
private static draggedViewlet: ViewletDescriptor;
private $container: Builder;
- private $label: Builder;
- private name: string;
private _keybinding: string;
private cssClass: string;
- private $badge: Builder;
- private $badgeContent: Builder;
private mouseUpTimeout: number;
+ private get viewlet(): ViewletDescriptor {
+ return this.action.activity as ViewletDescriptor;
+ }
+
constructor(
- action: ActivityAction,
- private viewlet: ViewletDescriptor,
+ private action: ViewletActivityAction,
@IContextMenuService private contextMenuService: IContextMenuService,
@IActivityBarService private activityBarService: IActivityBarService,
@IKeybindingService private keybindingService: IKeybindingService,
@@ -148,42 +254,14 @@ export class ActivityActionItem extends ThemableActivityActionItem {
super(action, { draggable: true }, themeService);
this.cssClass = action.class;
- this.name = viewlet.name;
- this._keybinding = this.getKeybindingLabel(viewlet.id);
-
- if (!ActivityActionItem.manageExtensionAction) {
- ActivityActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
- }
-
- if (!ActivityActionItem.toggleViewletPinnedAction) {
- ActivityActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0);
- }
+ this._keybinding = this.getKeybindingLabel(this.viewlet.id);
- action.onDidChangeBadge(this.handleBadgeChangeEvenet, this, this._callOnDispose);
- }
-
- 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);
+ if (!ViewletActionItem.manageExtensionAction) {
+ ViewletActionItem.manageExtensionAction = instantiationService.createInstance(ManageExtensionAction);
}
- // 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);
+ if (!ViewletActionItem.toggleViewletPinnedAction) {
+ ViewletActionItem.toggleViewletPinnedAction = instantiationService.createInstance(ToggleViewletPinnedAction, void 0);
}
}
@@ -240,7 +318,7 @@ export class ActivityActionItem extends ThemableActivityActionItem {
// Drag enter
let counter = 0; // see https://github.com/Microsoft/vscode/issues/14470
this.$container.on(DOM.EventType.DRAG_ENTER, (e: DragEvent) => {
- const draggedViewlet = ActivityActionItem.getDraggedViewlet();
+ const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
counter++;
this.updateFromDragging(container, true);
@@ -249,7 +327,7 @@ export class ActivityActionItem extends ThemableActivityActionItem {
// Drag leave
this.$container.on(DOM.EventType.DRAG_LEAVE, (e: DragEvent) => {
- const draggedViewlet = ActivityActionItem.getDraggedViewlet();
+ const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
counter--;
if (counter === 0) {
@@ -260,12 +338,12 @@ export class ActivityActionItem extends ThemableActivityActionItem {
// Drag end
this.$container.on(DOM.EventType.DRAG_END, (e: DragEvent) => {
- const draggedViewlet = ActivityActionItem.getDraggedViewlet();
+ const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
counter = 0;
this.updateFromDragging(container, false);
- ActivityActionItem.clearDraggedViewlet();
+ ViewletActionItem.clearDraggedViewlet();
}
});
@@ -273,34 +351,21 @@ export class ActivityActionItem extends ThemableActivityActionItem {
this.$container.on(DOM.EventType.DROP, (e: DragEvent) => {
DOM.EventHelper.stop(e, true);
- const draggedViewlet = ActivityActionItem.getDraggedViewlet();
+ const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet && draggedViewlet.id !== this.viewlet.id) {
this.updateFromDragging(container, false);
- ActivityActionItem.clearDraggedViewlet();
+ ViewletActionItem.clearDraggedViewlet();
this.activityBarService.move(draggedViewlet.id, this.viewlet.id);
}
});
- // Label
- this.$label = $('a.action-label').appendTo(this.builder);
- if (this.cssClass) {
- this.$label.addClass(this.cssClass);
- }
-
- // Badge
- this.$badge = this.builder.div({ 'class': 'badge' }, (badge: Builder) => {
- this.$badgeContent = badge.div({ 'class': 'badge-content' });
- });
-
- this.$badge.hide();
-
// Keybinding
this.keybinding = this._keybinding; // force update
// Activate on drag over to reveal targets
[this.$badge, this.$label].forEach(b => new DelayedDragHandler(b.getHTMLElement(), () => {
- if (!ActivityActionItem.getDraggedViewlet() && !this.getAction().checked) {
+ if (!ViewletActionItem.getDraggedViewlet() && !this.getAction().checked) {
this.getAction().run();
}
}));
@@ -316,29 +381,29 @@ export class ActivityActionItem extends ThemableActivityActionItem {
}
public static getDraggedViewlet(): ViewletDescriptor {
- return ActivityActionItem.draggedViewlet;
+ return ViewletActionItem.draggedViewlet;
}
private setDraggedViewlet(viewlet: ViewletDescriptor): void {
- ActivityActionItem.draggedViewlet = viewlet;
+ ViewletActionItem.draggedViewlet = viewlet;
}
public static clearDraggedViewlet(): void {
- ActivityActionItem.draggedViewlet = void 0;
+ ViewletActionItem.draggedViewlet = void 0;
}
private showContextMenu(container: HTMLElement): void {
- const actions: Action[] = [ActivityActionItem.toggleViewletPinnedAction];
+ const actions: Action[] = [ViewletActionItem.toggleViewletPinnedAction];
if (this.viewlet.extensionId) {
actions.push(new Separator());
- actions.push(ActivityActionItem.manageExtensionAction);
+ actions.push(ViewletActionItem.manageExtensionAction);
}
const isPinned = this.activityBarService.isPinned(this.viewlet.id);
if (isPinned) {
- ActivityActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Remove from Activity Bar");
+ ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('removeFromActivityBar', "Remove from Activity Bar");
} else {
- ActivityActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar");
+ ViewletActionItem.toggleViewletPinnedAction.label = nls.localize('keepInActivityBar', "Keep in Activity Bar");
}
this.contextMenuService.showContextMenu({
@@ -352,10 +417,6 @@ export class ActivityActionItem extends ThemableActivityActionItem {
this.$container.domFocus();
}
- public setBadge(badge: IBadge): void {
- this.updateBadge(badge);
- }
-
public set keybinding(keybinding: string) {
this._keybinding = keybinding;
@@ -365,49 +426,15 @@ export class ActivityActionItem extends ThemableActivityActionItem {
let title: string;
if (keybinding) {
- title = nls.localize('titleKeybinding', "{0} ({1})", this.name, keybinding);
+ title = nls.localize('titleKeybinding', "{0} ({1})", this.activity.name, keybinding);
} else {
- title = this.name;
+ title = this.activity.name;
}
this.$label.title(title);
this.$badge.title(title);
}
- private 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.name} - ${badge.getDescription()}`);
- }
- }
-
protected _updateClass(): void {
if (this.cssClass) {
this.$badge.removeClass(this.cssClass);
@@ -425,13 +452,6 @@ export class ActivityActionItem extends ThemableActivityActionItem {
}
}
- private handleBadgeChangeEvenet(): void {
- const action = this.getAction();
- if (action instanceof ActivityAction) {
- this.updateBadge(action.getBadge());
- }
- }
-
protected _updateEnabled(): void {
if (this.getAction().enabled) {
this.builder.removeClass('disabled');
@@ -443,13 +463,12 @@ export class ActivityActionItem extends ThemableActivityActionItem {
public dispose(): void {
super.dispose();
- ActivityActionItem.clearDraggedViewlet();
+ ViewletActionItem.clearDraggedViewlet();
if (this.mouseUpTimeout) {
clearTimeout(this.mouseUpTimeout);
}
- this.$badge.destroy();
this.$label.destroy();
}
}
@@ -459,7 +478,11 @@ export class ViewletOverflowActivityAction extends ActivityAction {
constructor(
private showMenu: () => void
) {
- super('activitybar.additionalViewlets.action', nls.localize('additionalViews', "Additional Views"), 'toggle-more');
+ super({
+ id: 'activitybar.additionalViewlets.action',
+ name: nls.localize('additionalViews', "Additional Views"),
+ cssClass: 'toggle-more'
+ });
}
public run(event): TPromise<any> {
@@ -469,8 +492,8 @@ export class ViewletOverflowActivityAction extends ActivityAction {
}
}
-export class ViewletOverflowActivityActionItem extends ThemableActivityActionItem {
- private $label: Builder;
+export class ViewletOverflowActivityActionItem extends ActivityActionItem {
+
private name: string;
private cssClass: string;
private actions: OpenViewletAction[];
diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
index d824b944e0c..4085747c900 100644
--- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
+++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts
@@ -15,9 +15,11 @@ import { Builder, $, Dimension } from 'vs/base/browser/builder';
import { Action } from 'vs/base/common/actions';
import { ActionsOrientation, ActionBar, IActionItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { ViewletDescriptor } from 'vs/workbench/browser/viewlet';
+import { IGlobalActivity, GlobalActivityExtensions, IGlobalActivityRegistry } from 'vs/workbench/browser/activity';
+import { Registry } from 'vs/platform/platform';
import { Part } from 'vs/workbench/browser/part';
import { IViewlet } from 'vs/workbench/common/viewlet';
-import { ToggleViewletPinnedAction, ViewletActivityAction, ActivityAction, ActivityActionItem, ViewletOverflowActivityAction, ViewletOverflowActivityActionItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
+import { ToggleViewletPinnedAction, ViewletActivityAction, ActivityAction, ActivityActionItem, ViewletActionItem, ViewletOverflowActivityAction, ViewletOverflowActivityActionItem } from 'vs/workbench/browser/parts/activitybar/activitybarActions';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IActivityBarService, IBadge } from 'vs/workbench/services/activity/common/activityBarService';
import { IPartService, Position as SideBarPosition } from 'vs/workbench/services/part/common/partService';
@@ -27,7 +29,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage';
import { Scope as MementoScope } from 'vs/workbench/common/memento';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { StandardMouseEvent } from 'vs/base/browser/mouseEvent';
-import { dispose, IDisposable } from 'vs/base/common/lifecycle';
+import { dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { ToggleActivityBarVisibilityAction } from 'vs/workbench/browser/actions/toggleActivityBarVisibility';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER } from 'vs/workbench/common/theme';
@@ -38,6 +40,40 @@ interface IViewletActivity {
clazz: string;
}
+class GlobalActivityAction extends ActivityAction {
+
+ constructor(activity: IGlobalActivity) {
+ super(activity);
+ }
+}
+
+class GlobalActivityActionItem extends ActivityActionItem {
+
+ constructor(
+ action: GlobalActivityAction,
+ @IThemeService themeService: IThemeService,
+ @IContextMenuService protected contextMenuService: IContextMenuService
+ ) {
+ super(action, { draggable: false }, themeService);
+ }
+
+ onClick(e: MouseEvent): void {
+ const globalAction = this._action as GlobalActivityAction;
+ const activity = globalAction.activity as IGlobalActivity;
+ const actions = activity.getActions();
+
+ const event = new StandardMouseEvent(e);
+ event.stopPropagation();
+ event.preventDefault();
+
+ this.contextMenuService.showContextMenu({
+ getAnchor: () => ({ x: event.posx, y: event.posy }),
+ getActions: () => TPromise.as(actions),
+ onHide: () => dispose(actions)
+ });
+ }
+}
+
export class ActivitybarPart extends Part implements IActivityBarService {
private static readonly ACTIVITY_ACTION_HEIGHT = 50;
@@ -48,9 +84,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private dimension: Dimension;
private viewletSwitcherBar: ActionBar;
+ private activityActionBar: ActionBar;
private viewletOverflowAction: ViewletOverflowActivityAction;
private viewletOverflowActionItem: ViewletOverflowActivityActionItem;
+ private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; };
private viewletIdToActions: { [viewletId: string]: ActivityAction; };
private viewletIdToActionItems: { [viewletId: string]: IActionItem; };
private viewletIdToActivityStack: { [viewletId: string]: IViewletActivity[]; };
@@ -71,6 +109,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
) {
super(id, { hasTitle: false }, themeService);
+ this.globalActivityIdToActions = Object.create(null);
this.viewletIdToActionItems = Object.create(null);
this.viewletIdToActions = Object.create(null);
this.viewletIdToActivityStack = Object.create(null);
@@ -123,6 +162,21 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
}
+ public showGlobalActivity(globalActivityId: string, badge: IBadge): IDisposable {
+ if (!badge) {
+ throw illegalArgument('badge');
+ }
+
+ const action = this.globalActivityIdToActions[globalActivityId];
+
+ if (!action) {
+ throw illegalArgument('globalActivityId');
+ }
+
+ action.setBadge(badge);
+ return toDisposable(() => action.setBadge(undefined));
+ }
+
public showActivity(viewletId: string, badge: IBadge, clazz?: string): IDisposable {
if (!badge) {
throw illegalArgument('badge');
@@ -180,6 +234,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
// Top Actionbar with action items for each viewlet action
this.createViewletSwitcher($result.clone());
+ // Top Actionbar with action items for each viewlet action
+ this.createGlobalActivityActionBar($result.getHTMLElement());
+
// Contextmenu for viewlets
$(parent).on('contextmenu', (e: MouseEvent) => {
DOM.EventHelper.stop(e, true);
@@ -189,11 +246,11 @@ export class ActivitybarPart extends Part implements IActivityBarService {
// Allow to drop at the end to move viewlet to the end
$(parent).on(DOM.EventType.DROP, (e: DragEvent) => {
- const draggedViewlet = ActivityActionItem.getDraggedViewlet();
+ const draggedViewlet = ViewletActionItem.getDraggedViewlet();
if (draggedViewlet) {
DOM.EventHelper.stop(e, true);
- ActivityActionItem.clearDraggedViewlet();
+ ViewletActionItem.clearDraggedViewlet();
const targetId = this.pinnedViewlets[this.pinnedViewlets.length - 1];
if (targetId !== draggedViewlet.id) {
@@ -252,6 +309,26 @@ export class ActivitybarPart extends Part implements IActivityBarService {
this.extensionService.onReady().then(() => this.updateViewletSwitcher());
}
+ private createGlobalActivityActionBar(container: HTMLElement): void {
+ const activityRegistry = Registry.as<IGlobalActivityRegistry>(GlobalActivityExtensions);
+ const descriptors = activityRegistry.getActivities();
+ const actions = descriptors
+ .map(d => this.instantiationService.createInstance(d))
+ .map(a => new GlobalActivityAction(a));
+
+ this.activityActionBar = new ActionBar(container, {
+ actionItemProvider: a => this.instantiationService.createInstance(GlobalActivityActionItem, a),
+ orientation: ActionsOrientation.VERTICAL,
+ ariaLabel: nls.localize('globalActions', "Global Actions"),
+ animated: false
+ });
+
+ actions.forEach(a => {
+ this.globalActivityIdToActions[a.id] = a;
+ this.activityActionBar.push(a);
+ });
+ }
+
private updateViewletSwitcher() {
let viewletsToShow = this.getPinnedViewlets();
@@ -371,7 +448,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
private toAction(viewlet: ViewletDescriptor): ActivityAction {
const action = this.instantiationService.createInstance(ViewletActivityAction, viewlet);
- this.viewletIdToActionItems[action.id] = this.instantiationService.createInstance(ActivityActionItem, action, viewlet);
+ this.viewletIdToActionItems[action.id] = this.instantiationService.createInstance(ViewletActionItem, action);
this.viewletIdToActions[viewlet.id] = action;
return action;
diff --git a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
index 7acd81393fb..50061325e7e 100644
--- a/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
+++ b/src/vs/workbench/browser/parts/activitybar/media/activitybarpart.css
@@ -9,6 +9,9 @@
.monaco-workbench > .activitybar > .content {
height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
}
.monaco-workbench > .activitybar > .content .monaco-action-bar {
diff --git a/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css
new file mode 100644
index 00000000000..a12923fce44
--- /dev/null
+++ b/src/vs/workbench/parts/update/electron-browser/media/update.contribution.css
@@ -0,0 +1,9 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the MIT License. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+.update-activity {
+ -webkit-mask: url('update.svg') no-repeat 50% 50%;
+ -webkit-mask-size: 22px;
+} \ No newline at end of file
diff --git a/src/vs/workbench/parts/update/electron-browser/media/update.svg b/src/vs/workbench/parts/update/electron-browser/media/update.svg
new file mode 100644
index 00000000000..3dec2ba50fd
--- /dev/null
+++ b/src/vs/workbench/parts/update/electron-browser/media/update.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><g fill="#424242"><path d="M12.714 9.603c-.07.207-.15.407-.246.601l1.017 2.139c-.335.424-.718.807-1.142 1.143l-2.14-1.018c-.193.097-.394.176-.601.247l-.795 2.235c-.265.03-.534.05-.807.05-.272 0-.541-.02-.806-.05l-.795-2.235c-.207-.071-.408-.15-.602-.247l-2.14 1.017c-.424-.336-.807-.719-1.143-1.143l1.017-2.139c-.094-.193-.175-.393-.245-.6l-2.236-.796c-.03-.265-.05-.534-.05-.807s.02-.542.05-.807l2.236-.795c.07-.207.15-.407.246-.601l-1.016-2.139c.336-.423.719-.807 1.143-1.142l2.14 1.017c.193-.096.394-.176.602-.247l.793-2.236c.265-.03.534-.05.806-.05.273 0 .542.02.808.05l.795 2.236c.207.07.407.15.601.246l2.14-1.017c.424.335.807.719 1.142 1.142l-1.017 2.139c.096.194.176.394.246.601l2.236.795c.029.266.049.535.049.808s-.02.542-.05.807l-2.236.796zm-4.714-4.603c-1.657 0-3 1.343-3 3s1.343 3 3 3 3-1.343 3-3-1.343-3-3-3z"/><circle cx="8" cy="8" r="1.5"/></g></svg> \ No newline at end of file
diff --git a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts
index 52f121006f5..26cc50dfe8e 100644
--- a/src/vs/workbench/parts/update/electron-browser/update.contribution.ts
+++ b/src/vs/workbench/parts/update/electron-browser/update.contribution.ts
@@ -6,20 +6,31 @@
'use strict';
import * as nls from 'vs/nls';
+import 'vs/css!./media/update.contribution';
import { Registry } from 'vs/platform/platform';
+import product from 'vs/platform/node/product';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
-import { ShowCurrentReleaseNotesAction, UpdateContribution } from 'vs/workbench/parts/update/electron-browser/update';
import { ReleaseNotesEditor } from 'vs/workbench/parts/update/electron-browser/releaseNotesEditor';
import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput';
import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor';
+import { IGlobalActivityRegistry, GlobalActivityExtensions } from 'vs/workbench/browser/activity';
import { IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/common/editor';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actionRegistry';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
+import { ShowCurrentReleaseNotesAction, ProductContribution, UpdateContribution, LightUpdateContribution } from './update';
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
- .registerWorkbenchContribution(UpdateContribution);
+ .registerWorkbenchContribution(ProductContribution);
+
+if (product.quality === 'insider') {
+ Registry.as<IGlobalActivityRegistry>(GlobalActivityExtensions)
+ .registerActivity(LightUpdateContribution);
+} else {
+ Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench)
+ .registerWorkbenchContribution(UpdateContribution);
+}
// Editor
const editorDescriptor = new EditorDescriptor(
@@ -35,7 +46,6 @@ Registry.as<IEditorRegistry>(EditorExtensions.Editors)
Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions)
.registerWorkbenchAction(new SyncActionDescriptor(ShowCurrentReleaseNotesAction, ShowCurrentReleaseNotesAction.ID, ShowCurrentReleaseNotesAction.LABEL), 'Open Release Notes');
-
// Configuration: Update
const configurationRegistry = <IConfigurationRegistry>Registry.as(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
diff --git a/src/vs/workbench/parts/update/electron-browser/update.ts b/src/vs/workbench/parts/update/electron-browser/update.ts
index 644f7bd40df..4b8eff773df 100644
--- a/src/vs/workbench/parts/update/electron-browser/update.ts
+++ b/src/vs/workbench/parts/update/electron-browser/update.ts
@@ -8,24 +8,28 @@
import nls = require('vs/nls');
import severity from 'vs/base/common/severity';
import { TPromise } from 'vs/base/common/winjs.base';
-import { Action } from 'vs/base/common/actions';
+import { IAction, Action } from 'vs/base/common/actions';
+import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { IMessageService, CloseAction, Severity } from 'vs/platform/message/common/message';
import pkg from 'vs/platform/node/package';
import product from 'vs/platform/node/product';
import URI from 'vs/base/common/uri';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
+import { IActivityBarService, TextBadge } from 'vs/workbench/services/activity/common/activityBarService';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ReleaseNotesInput } from 'vs/workbench/parts/update/electron-browser/releaseNotesInput';
+import { IGlobalActivity } from 'vs/workbench/browser/activity';
import { IRequestService } from 'vs/platform/request/node/request';
import { asText } from 'vs/base/node/request';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingIO } from 'vs/workbench/services/keybinding/common/keybindingIO';
import { IOpenerService } from 'vs/platform/opener/common/opener';
+import { ICommandService } from 'vs/platform/commands/common/commands';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
-import { IUpdateService } from 'vs/platform/update/common/update';
+import { IUpdateService, State as UpdateState } from 'vs/platform/update/common/update';
import * as semver from 'semver';
-import { OS } from 'vs/base/common/platform';
+import { OS, isLinux, isWindows } from 'vs/base/common/platform';
class ApplyUpdateAction extends Action {
constructor( @IUpdateService private updateService: IUpdateService) {
@@ -189,24 +193,22 @@ const LinkAction = (id: string, message: string, licenseUrl: string) => new Acti
() => { window.open(licenseUrl); return TPromise.as(null); }
);
-export class UpdateContribution implements IWorkbenchContribution {
+export class ProductContribution implements IWorkbenchContribution {
private static KEY = 'releaseNotes/lastVersion';
- getId() { return 'vs.update'; }
+ getId() { return 'vs.product'; }
constructor(
@IStorageService storageService: IStorageService,
@IInstantiationService instantiationService: IInstantiationService,
@IMessageService messageService: IMessageService,
- @IUpdateService updateService: IUpdateService,
@IWorkbenchEditorService editorService: IWorkbenchEditorService
) {
- const lastVersion = storageService.get(UpdateContribution.KEY, StorageScope.GLOBAL, '');
+ const lastVersion = storageService.get(ProductContribution.KEY, StorageScope.GLOBAL, '');
- // was there an update?
+ // was there an update? if so, open release notes
if (product.releaseNotesUrl && lastVersion && pkg.version !== lastVersion) {
- instantiationService.invokeFunction(loadReleaseNotes, pkg.version)
- .then(
+ instantiationService.invokeFunction(loadReleaseNotes, pkg.version).then(
text => editorService.openEditor(instantiationService.createInstance(ReleaseNotesInput, pkg.version, text), { pinned: true }),
() => {
messageService.show(Severity.Info, {
@@ -230,8 +232,19 @@ export class UpdateContribution implements IWorkbenchContribution {
});
}
- storageService.store(UpdateContribution.KEY, pkg.version, StorageScope.GLOBAL);
+ storageService.store(ProductContribution.KEY, pkg.version, StorageScope.GLOBAL);
+ }
+}
+export class UpdateContribution implements IWorkbenchContribution {
+
+ getId() { return 'vs.update'; }
+
+ constructor(
+ @IInstantiationService instantiationService: IInstantiationService,
+ @IMessageService messageService: IMessageService,
+ @IUpdateService updateService: IUpdateService
+ ) {
updateService.onUpdateReady(update => {
const applyUpdateAction = instantiationService.createInstance(ApplyUpdateAction);
const releaseNotesAction = instantiationService.createInstance(ShowReleaseNotesAction, false, update.version);
@@ -262,4 +275,69 @@ export class UpdateContribution implements IWorkbenchContribution {
updateService.onError(err => messageService.show(severity.Error, err));
}
+}
+
+export class LightUpdateContribution implements IGlobalActivity {
+
+ get id() { return 'vs.update'; }
+ get name() { return ''; }
+ get cssClass() { return 'update-activity'; }
+
+ constructor(
+ @IStorageService storageService: IStorageService,
+ @ICommandService private commandService: ICommandService,
+ @IInstantiationService instantiationService: IInstantiationService,
+ @IMessageService messageService: IMessageService,
+ @IUpdateService private updateService: IUpdateService,
+ @IWorkbenchEditorService editorService: IWorkbenchEditorService,
+ @IActivityBarService activityBarService: IActivityBarService
+ ) {
+ this.updateService.onUpdateReady(() => {
+ const badge = new TextBadge('\u21e9', () => nls.localize('updateIsReady', "New update available."));
+ activityBarService.showGlobalActivity(this.id, badge);
+ });
+
+ this.updateService.onError(err => messageService.show(severity.Error, err));
+ }
+
+ getActions(): IAction[] {
+ return [
+ new Action('showCommandPalette', nls.localize('commandPalette', "Command Palette..."), undefined, true, () => this.commandService.executeCommand('workbench.action.showCommands')),
+ new Separator(),
+ new Action('openKeybindings', nls.localize('settings', "Settings"), null, true, () => this.commandService.executeCommand('workbench.action.openGlobalSettings')),
+ new Action('openSettings', nls.localize('keyboardShortcuts', "Keyboard Shortcuts"), null, true, () => this.commandService.executeCommand('workbench.action.openGlobalKeybindings')),
+ new Separator(),
+ this.getUpdateAction()
+ ];
+ }
+
+ private getUpdateAction(): IAction {
+ switch (this.updateService.state) {
+ case UpdateState.Uninitialized:
+ return new Action('update.notavailable', nls.localize('not available', "Updates Not Available"), undefined, false);
+
+ case UpdateState.CheckingForUpdate:
+ return new Action('update.checking', nls.localize('checkingForUpdates', "Checking For Updates..."), undefined, false);
+
+ case UpdateState.UpdateAvailable:
+ if (isLinux) {
+ return new Action('update.linux.available', nls.localize('DownloadUpdate', "Download Available Update"), undefined, true, () =>
+ this.updateService.quitAndInstall());
+ }
+
+ const updateAvailableLabel = isWindows
+ ? nls.localize('DownloadingUpdate', "Downloading Update...")
+ : nls.localize('InstallingUpdate', "Installing Update...");
+
+ return new Action('update.available', updateAvailableLabel, undefined, false);
+
+ case UpdateState.UpdateDownloaded:
+ return new Action('update.restart', nls.localize('restartToUpdate', "Restart To Update..."), undefined, true, () =>
+ this.updateService.quitAndInstall());
+
+ default:
+ return new Action('update.check', nls.localize('checkForUpdates', "Check For Updates..."), undefined, this.updateService.state === UpdateState.Idle, () =>
+ this.updateService.checkForUpdates(true));
+ }
+ }
} \ No newline at end of file
diff --git a/src/vs/workbench/services/activity/common/activityBarService.ts b/src/vs/workbench/services/activity/common/activityBarService.ts
index 810b062b63f..5c5b18a2105 100644
--- a/src/vs/workbench/services/activity/common/activityBarService.ts
+++ b/src/vs/workbench/services/activity/common/activityBarService.ts
@@ -64,6 +64,11 @@ export interface IActivityBarService {
_serviceBrand: any;
/**
+ * Show activity in the activitybar for the given global activity.
+ */
+ showGlobalActivity(globalActivityId: string, badge: IBadge): IDisposable;
+
+ /**
* Show activity in the activitybar for the given viewlet.
*/
showActivity(viewletId: string, badge: IBadge, clazz?: string): IDisposable;