diff --git a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts index 4e9c8a347cb..bc5a30e6f42 100644 --- a/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsContainersExtensionPoint.ts @@ -80,12 +80,11 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { } private registerTestViewContainer(): void { - const id = 'test'; const title = localize('test', "Test"); - const cssClass = `extensionViewlet-${id}`; + const cssClass = `extensionViewlet-test`; const icon = require.toUrl('./media/test.svg'); - this.registerCustomViewlet({ id, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass); + this.registerCustomViewlet({ id: ViewLocation.TEST.id, title, icon }, TEST_VIEW_CONTAINER_ORDER, cssClass); } private handleAndRegisterCustomViewContainers() { @@ -139,13 +138,13 @@ class ViewsContainersExtensionHandler implements IWorkbenchContribution { const cssClass = `extensionViewlet-${descriptor.id}`; // TODO@extensionLocation const icon = join(extension.extensionLocation.fsPath, descriptor.icon); - this.registerCustomViewlet({ id: descriptor.id, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass); + this.registerCustomViewlet({ id: `workbench.view.extension.${descriptor.id}`, title: descriptor.title, icon }, TEST_VIEW_CONTAINER_ORDER + index + 1, cssClass); }); } private registerCustomViewlet(descriptor: IUserFriendlyViewsContainerDescriptor, order: number, cssClass: string): void { const viewletRegistry = Registry.as(ViewletExtensions.Viewlets); - const id = `workbench.view.extension.${descriptor.id}`; + const id = descriptor.id; if (!viewletRegistry.getViewlet(id)) { diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 338c2a65dee..3bd54b0fd57 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -25,7 +25,6 @@ import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND, import { contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { CompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBar'; import { ToggleCompositePinnedAction, ICompositeBar } from 'vs/workbench/browser/parts/compositebar/compositeBarActions'; -import { ViewLocation, ViewsRegistry } from 'vs/workbench/common/views'; import { ViewletDescriptor } from 'vs/workbench/browser/viewlet'; import { Dimension, createCSSRule } from 'vs/base/browser/dom'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -57,7 +56,6 @@ export class ActivitybarPart extends Part { private globalActivityIdToActions: { [globalActivityId: string]: GlobalActivityAction; }; private placeholderComposites: IPlaceholderComposite[] = []; - private extensionsRegistrationCompleted: boolean = false; private compositeBar: CompositeBar; private compositeActions: { [compositeId: string]: { activityAction: ViewletActivityAction, pinnedAction: ToggleCompositePinnedAction } }; @@ -101,7 +99,6 @@ export class ActivitybarPart extends Part { } private onDidRegisterExtensions(): void { - this.extensionsRegistrationCompleted = true; this.removeNotExistingPlaceholderComposites(); this.updateCompositebar(); } @@ -109,8 +106,6 @@ export class ActivitybarPart extends Part { private registerListeners(): void { this.toUnbind.push(this.viewletService.onDidViewletRegister(() => this.updateCompositebar())); - this.toUnbind.push(ViewsRegistry.onViewsRegistered(() => this.updateCompositebar())); - this.toUnbind.push(ViewsRegistry.onViewsDeregistered(() => this.updateCompositebar())); // Activate viewlet action on opening of a viewlet this.toUnbind.push(this.viewletService.onDidViewletOpen(viewlet => this.compositeBar.activateComposite(viewlet.getId()))); @@ -119,7 +114,7 @@ export class ActivitybarPart extends Part { this.toUnbind.push(this.viewletService.onDidViewletClose(viewlet => this.compositeBar.deactivateComposite(viewlet.getId()))); this.toUnbind.push(this.viewletService.onDidViewletEnablementChange(({ id, enabled }) => { if (enabled) { - this.compositeBar.addComposite(this.viewletService.getViewlet(id), true); + this.compositeBar.addComposite(this.viewletService.getViewlet(id)); } else { this.removeComposite(id); } @@ -226,26 +221,18 @@ export class ActivitybarPart extends Part { private updateCompositebar(): void { const viewlets = this.viewletService.getViewlets(); for (const viewlet of viewlets) { - const hasPlaceholder = this.placeholderComposites.some(c => c.id === viewlet.id); + this.compositeBar.addComposite(viewlet); - // Add the composite if it has views registered or - // If it was existing before and the extensions registration is not yet completed. - if (this.hasRegisteredViews(viewlet) - || (hasPlaceholder && !this.extensionsRegistrationCompleted) - ) { - this.compositeBar.addComposite(viewlet, false); - // Pin it by default if it is new => it does not has a placeholder - if (!hasPlaceholder) { - this.compositeBar.pin(viewlet.id); - } - this.enableCompositeActions(viewlet); - const activeViewlet = this.viewletService.getActiveViewlet(); - if (activeViewlet && activeViewlet.getId() === viewlet.id) { - this.compositeBar.pin(viewlet.id); - this.compositeBar.activateComposite(viewlet.id); - } - } else { - this.removeComposite(viewlet.id); + // Pin it by default if it is new => it does not has a placeholder + if (this.placeholderComposites.every(c => c.id !== viewlet.id)) { + this.compositeBar.pin(viewlet.id); + } + + this.enableCompositeActions(viewlet); + const activeViewlet = this.viewletService.getActiveViewlet(); + if (activeViewlet && activeViewlet.getId() === viewlet.id) { + this.compositeBar.pin(viewlet.id); + this.compositeBar.activateComposite(viewlet.id); } } } @@ -254,7 +241,7 @@ export class ActivitybarPart extends Part { const viewlets = this.viewletService.getViewlets(); for (const { id } of this.placeholderComposites) { if (viewlets.every(viewlet => viewlet.id !== id)) { - this.compositeBar.addComposite({ id, name: id, order: void 0 }, false); + this.compositeBar.addComposite({ id, name: id, order: void 0 }); } } } @@ -288,14 +275,6 @@ export class ActivitybarPart extends Part { } } - private hasRegisteredViews(viewlet: ViewletDescriptor): boolean { - const viewLocation = ViewLocation.get(viewlet.id); - if (viewLocation) { - return ViewsRegistry.getViews(viewLocation).length > 0; - } - return true; - } - public getPinned(): string[] { return this.viewletService.getViewlets().map(v => v.id).filter(id => this.compositeBar.isPinned(id)); } @@ -324,7 +303,7 @@ export class ActivitybarPart extends Part { } public shutdown(): void { - const state = this.viewletService.getViewlets().filter(viewlet => this.hasRegisteredViews(viewlet)).map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl })); + const state = this.viewletService.getViewlets().filter(viewlet => this.compositeBar.isPinned(viewlet.id)).map(viewlet => ({ id: viewlet.id, iconUrl: viewlet.iconUrl })); this.storageService.store(ActivitybarPart.PLACEHOLDER_VIEWLETS, JSON.stringify(state), StorageScope.GLOBAL); super.shutdown(); } diff --git a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts index e9e71d81bf0..54b7a6c74e0 100644 --- a/src/vs/workbench/browser/parts/compositebar/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositebar/compositeBar.ts @@ -118,7 +118,7 @@ export class CompositeBar extends Widget implements ICompositeBar { this.updateCompositeSwitcher(); } - public addComposite({ id, name, order }: { id: string; name: string, order: number }, open: boolean): void { + public addComposite({ id, name, order }: { id: string; name: string, order: number }): void { const state = this.storedState.filter(s => s.id === id)[0]; const pinned = state ? state.pinned : true; let index = order >= 0 ? order : this.model.items.length; @@ -139,8 +139,8 @@ export class CompositeBar extends Widget implements ICompositeBar { // Add to the model if (this.model.add(id, name, order, index)) { this.computeSizes([this.model.findItem(id)]); - if (pinned || open) { - this.pin(id, open); + if (pinned) { + this.pin(id); } else { this.updateCompositeSwitcher(); } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 06578335499..68f224cc687 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -105,7 +105,7 @@ export class PanelPart extends CompositePart implements IPanelService { }); this.toUnbind.push(this.compositeBar); for (const panel of this.getPanels()) { - this.compositeBar.addComposite(panel, false); + this.compositeBar.addComposite(panel); } this.activePanelContextKey = ActivePanelContext.bindTo(contextKeyService); this.onDidPanelOpen(this._onDidPanelOpen, this, this.disposables); @@ -116,7 +116,7 @@ export class PanelPart extends CompositePart implements IPanelService { private registerListeners(): void { - this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor, false))); + this.toUnbind.push(this.registry.onDidRegister(panelDescriptor => this.compositeBar.addComposite(panelDescriptor))); // Activate panel action on opening of a panel this.toUnbind.push(this.onDidPanelOpen(panel => { @@ -198,7 +198,7 @@ export class PanelPart extends CompositePart implements IPanelService { if (descriptor && descriptor.enabled !== enabled) { descriptor.enabled = enabled; if (enabled) { - this.compositeBar.addComposite(descriptor, true); + this.compositeBar.addComposite(descriptor); } else { this.removeComposite(id); } diff --git a/src/vs/workbench/browser/parts/views/contributableViews.ts b/src/vs/workbench/browser/parts/views/contributableViews.ts index 42c9ea86328..d1b0d8a3830 100644 --- a/src/vs/workbench/browser/parts/views/contributableViews.ts +++ b/src/vs/workbench/browser/parts/views/contributableViews.ts @@ -55,7 +55,7 @@ export interface IViewItem { active: boolean; } -class ViewDescriptorCollection { +export class ViewDescriptorCollection { private contextKeys = new CounterSet(); private items: IViewItem[] = []; diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/views.ts similarity index 91% rename from src/vs/workbench/browser/parts/views/customView.ts rename to src/vs/workbench/browser/parts/views/views.ts index 40431d62f5e..2e127abb4b0 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -33,8 +33,13 @@ import { FileIconThemableWorkbenchTree } from 'vs/workbench/browser/parts/views/ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { Emitter, Event } from 'vs/base/common/event'; +import { ViewDescriptorCollection } from './contributableViews'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -export class CustomViewsService extends Disposable implements IViewsService { +export class ViewsService extends Disposable implements IViewsService { _serviceBrand: any; @@ -42,9 +47,16 @@ export class CustomViewsService extends Disposable implements IViewsService { constructor( @IInstantiationService private instantiationService: IInstantiationService, - @IViewletService private viewletService: IViewletService + @ILifecycleService private lifecycleService: ILifecycleService, + @IViewletService private viewletService: IViewletService, + @IStorageService private storageService: IStorageService ) { super(); + + ViewLocation.all.forEach(viewLocation => this.onDidRegisterViewLocation(viewLocation)); + this._register(ViewLocation.onDidRegister(viewLocation => this.onDidRegisterViewLocation(viewLocation))); + this._register(Registry.as(ViewletExtensions.Viewlets).onDidRegister(viewlet => this.viewletService.setViewletEnablement(viewlet.id, this.storageService.getBoolean(`viewservice.${viewlet.id}.enablement`, StorageScope.GLOBAL, viewlet.id !== ViewLocation.TEST.id)))); + this.createViewers(ViewsRegistry.getAllViews()); this._register(ViewsRegistry.onViewsRegistered(viewDescriptors => this.createViewers(viewDescriptors))); this._register(ViewsRegistry.onViewsDeregistered(viewDescriptors => this.removeViewers(viewDescriptors))); @@ -72,6 +84,18 @@ export class CustomViewsService extends Disposable implements IViewsService { return TPromise.as(null); } + private onDidRegisterViewLocation(viewLocation: ViewLocation): void { + const viewDescriptorCollection = this._register(this.instantiationService.createInstance(ViewDescriptorCollection, viewLocation)); + this._register(viewDescriptorCollection.onDidChange(() => this.updateViewletEnablement(viewLocation, viewDescriptorCollection))); + this.lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateViewletEnablement(viewLocation, viewDescriptorCollection)); + } + + private updateViewletEnablement(viewLocation: ViewLocation, viewDescriptorCollection: ViewDescriptorCollection): void { + const enabled = viewDescriptorCollection.viewDescriptors.length > 0; + this.viewletService.setViewletEnablement(viewLocation.id, enabled); + this.storageService.store(`viewservice.${viewLocation.id}.enablement`, enabled, StorageScope.GLOBAL); + } + private createViewers(viewDescriptors: IViewDescriptor[]): void { for (const viewDescriptor of viewDescriptors) { if ((viewDescriptor).treeView) { diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 52355a0e60c..ff8cf746353 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -14,23 +14,34 @@ import { IViewlet } from 'vs/workbench/common/viewlet'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { values } from 'vs/base/common/map'; export class ViewLocation { + private static readonly _onDidRegister: Emitter = new Emitter(); + static readonly onDidRegister: Event = ViewLocation._onDidRegister.event; + private static locations: Map = new Map(); static register(id: string): ViewLocation { - const viewLocation = new ViewLocation(id); - ViewLocation.locations.set(id, viewLocation); - return viewLocation; + if (!ViewLocation.locations.has(id)) { + const viewLocation = new ViewLocation(id); + ViewLocation.locations.set(id, viewLocation); + ViewLocation._onDidRegister.fire(viewLocation); + } + return ViewLocation.get(id); } static get(value: string): ViewLocation { return ViewLocation.locations.get(value); } + static get all(): ViewLocation[] { + return values(ViewLocation.locations); + } static readonly Explorer: ViewLocation = ViewLocation.register('workbench.view.explorer'); static readonly Debug: ViewLocation = ViewLocation.register('workbench.view.debug'); static readonly Extensions: ViewLocation = ViewLocation.register('workbench.view.extensions'); static readonly SCM: ViewLocation = ViewLocation.register('workbench.view.scm.views.contributed'); + static readonly TEST: ViewLocation = ViewLocation.register('workbench.view.extension.test'); private constructor(private _id: string) { } get id(): string { return this._id; } diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index da94b87236e..be7e26968ac 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -98,7 +98,7 @@ import { IListService, ListService } from 'vs/platform/list/browser/listService' import { domEvent } from 'vs/base/browser/event'; import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys'; import { IViewsService } from 'vs/workbench/common/views'; -import { CustomViewsService } from 'vs/workbench/browser/parts/views/customView'; +import { ViewsService } from 'vs/workbench/browser/parts/views/views'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { NotificationService } from 'vs/workbench/services/notification/common/notificationService'; import { NotificationsCenter } from 'vs/workbench/browser/parts/notifications/notificationsCenter'; @@ -387,9 +387,11 @@ export class Workbench implements IPartService { } perf.mark('willRestoreViewlet'); - restorePromises.push(this.viewletService.openViewlet(viewletIdToRestore).then(() => { - perf.mark('didRestoreViewlet'); - })); + restorePromises.push(this.viewletService.openViewlet(viewletIdToRestore) + .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId())) + .then(() => { + perf.mark('didRestoreViewlet'); + })); } // Restore Panel @@ -575,7 +577,7 @@ export class Workbench implements IPartService { serviceCollection.set(IPanelService, this.panelPart); // Custom views service - const customViewsService = this.instantiationService.createInstance(CustomViewsService); + const customViewsService = this.instantiationService.createInstance(ViewsService); serviceCollection.set(IViewsService, customViewsService); // Activity service (activitybar part) @@ -872,7 +874,8 @@ export class Workbench implements IPartService { else if (!hidden && !this.sidebarPart.getActiveViewlet()) { const viewletToOpen = this.sidebarPart.getLastActiveViewletId(); if (viewletToOpen) { - promise = this.sidebarPart.openViewlet(viewletToOpen, true); + promise = this.viewletService.openViewlet(viewletToOpen, true) + .then(viewlet => viewlet || this.viewletService.openViewlet(this.viewletService.getDefaultViewletId(), true)); } } diff --git a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts b/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts index 851e30dc4e5..d5b6bf21e19 100644 --- a/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts +++ b/src/vs/workbench/parts/search/browser/searchViewLocationUpdater.ts @@ -16,23 +16,29 @@ export class SearchViewLocationUpdater implements IWorkbenchContribution { @IPanelService panelService: IPanelService, @IConfigurationService configurationService: IConfigurationService ) { - const updateSearchViewLocation = () => { + const updateSearchViewLocation = (open: boolean) => { const config = configurationService.getValue(); if (config.search.location === 'panel') { viewletService.setViewletEnablement(VIEW_ID, false); panelService.setPanelEnablement(VIEW_ID, true); + if (open) { + panelService.openPanel(VIEW_ID); + } } else { panelService.setPanelEnablement(VIEW_ID, false); viewletService.setViewletEnablement(VIEW_ID, true); + if (open) { + viewletService.openViewlet(VIEW_ID); + } } }; configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration('search.location')) { - updateSearchViewLocation(); + updateSearchViewLocation(true); } }); - updateSearchViewLocation(); + updateSearchViewLocation(false); } } diff --git a/src/vs/workbench/services/viewlet/browser/viewletService.ts b/src/vs/workbench/services/viewlet/browser/viewletService.ts index 0a6b990f23f..354410b5820 100644 --- a/src/vs/workbench/services/viewlet/browser/viewletService.ts +++ b/src/vs/workbench/services/viewlet/browser/viewletService.ts @@ -62,7 +62,7 @@ export class ViewletService implements IViewletService { } public setViewletEnablement(id: string, enabled: boolean): void { - const descriptor = this.getBuiltInViewlets().filter(desc => desc.id === id).pop(); + const descriptor = this.getAllViewlets().filter(desc => desc.id === id).pop(); if (descriptor && descriptor.enabled !== enabled) { descriptor.enabled = enabled; this._onDidViewletEnable.fire({ id, enabled }); @@ -74,7 +74,12 @@ export class ViewletService implements IViewletService { return this.sidebarPart.openViewlet(id, focus); } return this.extensionService.whenInstalledExtensionsRegistered() - .then(() => this.sidebarPart.openViewlet(id, focus)); + .then(() => { + if (this.getViewlet(id)) { + return this.sidebarPart.openViewlet(id, focus); + } + return null; + }); } public getActiveViewlet(): IViewlet { @@ -82,11 +87,11 @@ export class ViewletService implements IViewletService { } public getViewlets(): ViewletDescriptor[] { - return this.getBuiltInViewlets() + return this.getAllViewlets() .filter(v => v.enabled); } - private getBuiltInViewlets(): ViewletDescriptor[] { + private getAllViewlets(): ViewletDescriptor[] { return this.viewletRegistry.getViewlets() .sort((v1, v2) => v1.order - v2.order); }