diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 9ae27329a50..c314a406254 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -293,6 +293,14 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { private readonly _onDidChangeVisibility = this._register(new Emitter()); readonly onDidChangeVisibility = this._onDidChangeVisibility.event; + private readonly _onDidAddViews = this._register(new Emitter()); + readonly onDidAddViews = this._onDidAddViews.event; + + private readonly _onDidRemoveViews = this._register(new Emitter()); + readonly onDidRemoveViews = this._onDidRemoveViews.event; + + private readonly _onDidChangeViewVisibility = this._register(new Emitter()); + readonly onDidChangeViewVisibility = this._onDidChangeViewVisibility.event; get onDidSashChange(): Event { return assertIsDefined(this.paneview).onDidSashChange; @@ -347,15 +355,15 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this._register(addDisposableListener(parent, EventType.CONTEXT_MENU, (e: MouseEvent) => this.showContextMenu(new StandardMouseEvent(e)))); this._register(this.onDidSashChange(() => this.saveViewSizes())); - this.viewsModel.onDidAdd(added => this.onDidAddViews(added)); - this.viewsModel.onDidRemove(removed => this.onDidRemoveViews(removed)); + this.viewsModel.onDidAdd(added => this.onDidAddViewDescriptors(added)); + this.viewsModel.onDidRemove(removed => this.onDidRemoveViewDescriptors(removed)); const addedViews: IAddedViewDescriptorRef[] = this.viewsModel.visibleViewDescriptors.map((viewDescriptor, index) => { const size = this.viewsModel.getSize(viewDescriptor.id); const collapsed = this.viewsModel.isCollapsed(viewDescriptor.id); return ({ viewDescriptor, index, size, collapsed }); }); if (addedViews.length) { - this.onDidAddViews(addedViews); + this.onDidAddViewDescriptors(addedViews); } // Update headers after and title contributed views after available, since we read from cache in the beginning to know if the viewlet has single view or not. Ref #29609 @@ -503,6 +511,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { if (this.isViewMergedWithContainer() !== wasMerged) { this.updateTitleArea(); } + + this._onDidAddViews.fire(panes.map(({ pane }) => pane)); } setVisible(visible: boolean): void { @@ -606,7 +616,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return view; } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { + protected onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] { const panesToAdd: { pane: ViewPane, size: number, index: number }[] = []; for (const { viewDescriptor, collapsed, index, size } of added) { @@ -653,7 +663,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return this.actionRunner; } - private onDidRemoveViews(removed: IViewDescriptorRef[]): void { + private onDidRemoveViewDescriptors(removed: IViewDescriptorRef[]): void { removed = removed.sort((a, b) => b.index - a.index); const panesToRemove: ViewPane[] = []; for (const { index } of removed) { @@ -682,6 +692,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { this.updateTitleArea(); } }); + const onDidChangeVisibility = pane.onDidChangeBodyVisibility(() => this._onDidChangeViewVisibility.fire(pane)); const onDidChange = pane.onDidChange(() => { if (pane === this.lastFocusedPane && !pane.isExpanded()) { this.lastFocusedPane = undefined; @@ -695,7 +706,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { headerBorder: SIDE_BAR_SECTION_HEADER_BORDER, dropBackground: SIDE_BAR_DRAG_AND_DROP_BACKGROUND }, pane); - const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange); + const disposable = combinedDisposable(onDidFocus, onDidChangeTitleArea, paneStyler, onDidChange, onDidChangeVisibility); const paneItem: IViewPaneItem = { pane: pane, disposable }; this.paneItems.splice(index, 0, paneItem); @@ -711,6 +722,8 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { if (wasMerged !== this.isViewMergedWithContainer()) { this.updateTitleArea(); } + + this._onDidRemoveViews.fire(panes); } private removePane(pane: ViewPane): void { diff --git a/src/vs/workbench/browser/parts/views/views.ts b/src/vs/workbench/browser/parts/views/views.ts index 83706144b5a..52ecfe80c29 100644 --- a/src/vs/workbench/browser/parts/views/views.ts +++ b/src/vs/workbench/browser/parts/views/views.ts @@ -12,7 +12,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { Event, Emitter } from 'vs/base/common/event'; import { firstIndex, move } from 'vs/base/common/arrays'; -import { isUndefinedOrNull, isUndefined } from 'vs/base/common/types'; +import { isUndefinedOrNull, isUndefined, isString } from 'vs/base/common/types'; import { MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -22,9 +22,19 @@ import { toggleClass, addClass } from 'vs/base/browser/dom'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import type { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { PaneComposite } from 'vs/workbench/browser/panecomposite'; +import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { VIEW_ID as SEARCH_VIEW_ID } from 'vs/workbench/services/search/common/search'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { PaneCompositePanel, PanelRegistry, PanelDescriptor, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; +import { URI } from 'vs/base/common/uri'; export interface IViewState { visibleGlobal: boolean | undefined; @@ -451,10 +461,14 @@ export class ViewsService extends Disposable implements IViewsService { private readonly viewContainersRegistry: IViewContainersRegistry; private readonly viewDisposable: Map; + private readonly _onDidChangeViewVisibility: Emitter<{ id: string, visible: boolean }> = this._register(new Emitter<{ id: string, visible: boolean }>()); + readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }> = this._onDidChangeViewVisibility.event; + constructor( @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IPanelService private readonly panelService: IPanelService, - @IViewletService private readonly viewletService: IViewletService + @IViewletService private readonly viewletService: IViewletService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); @@ -466,11 +480,11 @@ export class ViewsService extends Disposable implements IViewsService { this.viewDisposable.clear(); })); - this.viewContainersRegistry.all.forEach(viewContainer => this.onViewContainerRegistered(viewContainer)); - this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer }) => this.onViewContainerRegistered(viewContainer))); + this.viewContainersRegistry.all.forEach(viewContainer => this.onDidRegisterViewContainer(viewContainer, this.viewContainersRegistry.getViewContainerLocation(viewContainer))); + this._register(this.viewContainersRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => this.onDidRegisterViewContainer(viewContainer, viewContainerLocation))); } - private onViewContainerRegistered(viewContainer: ViewContainer): void { + private onDidRegisterViewContainer(viewContainer: ViewContainer, location: ViewContainerLocation): void { const viewDescriptorCollection = this.viewDescriptorService.getViewDescriptors(viewContainer); this.onViewsAdded(viewDescriptorCollection.allViewDescriptors, viewContainer); this._register(viewDescriptorCollection.onDidChangeViews(({ added, removed }) => { @@ -577,7 +591,7 @@ export class ViewsService extends Disposable implements IViewsService { return undefined; } - getActiveViewWithId(id: string): IView | null { + getActiveViewWithId(id: string): T | null { const viewContainer = this.viewDescriptorService.getViewContainer(id); if (viewContainer) { const location = this.viewContainersRegistry.getViewContainerLocation(viewContainer); @@ -585,12 +599,12 @@ export class ViewsService extends Disposable implements IViewsService { if (location === ViewContainerLocation.Sidebar) { const activeViewlet = this.viewletService.getActiveViewlet(); if (activeViewlet?.getId() === viewContainer.id) { - return activeViewlet.getViewPaneContainer().getView(id) ?? null; + return activeViewlet.getViewPaneContainer().getView(id) as T ?? null; } } else if (location === ViewContainerLocation.Panel) { const activePanel = this.panelService.getActivePanel(); - if (activePanel?.getId() === viewContainer.id && activePanel instanceof PaneComposite) { - return activePanel.getViewPaneContainer().getView(id) ?? null; + if (activePanel?.getId() === viewContainer.id) { + return (activePanel as IPaneComposite).getViewPaneContainer().getView(id) as T ?? null; } } } @@ -613,6 +627,14 @@ export class ViewsService extends Disposable implements IViewsService { return null; } + + createContainer(container: ViewContainer): ViewPaneContainer { + const viewPaneContainer: ViewPaneContainer = this._register((this.instantiationService as any).createInstance(container.ctorDescriptor!.ctor, ...(container.ctorDescriptor!.staticArguments || []))); + this._register(viewPaneContainer.onDidAddViews(views => views.forEach(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: view.isBodyVisible() })))); + this._register(viewPaneContainer.onDidChangeViewVisibility(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: view.isBodyVisible() }))); + this._register(viewPaneContainer.onDidRemoveViews(views => views.forEach(view => this._onDidChangeViewVisibility.fire({ id: view.id, visible: false })))); + return viewPaneContainer; + } } export function createFileIconThemableTreeContainerScope(container: HTMLElement, themeService: IWorkbenchThemeService): IDisposable { @@ -629,3 +651,74 @@ export function createFileIconThemableTreeContainerScope(container: HTMLElement, } registerSingleton(IViewsService, ViewsService); + +// Viewlets & Panels +(function registerViewletsAndPanels(): void { + const registerPanel = (viewContainer: ViewContainer): void => { + class PaneContainerPanel extends PaneCompositePanel { + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IViewsService viewsService: ViewsService + ) { + super(viewContainer.id, viewsService.createContainer(viewContainer), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); + } + } + Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( + PaneContainerPanel, + viewContainer.id, + viewContainer.name, + isString(viewContainer.icon) ? viewContainer.icon : undefined, + viewContainer.order, + viewContainer.focusCommand?.id, + )); + }; + + const registerViewlet = (viewContainer: ViewContainer): void => { + class PaneContainerViewlet extends Viewlet { + constructor( + @IConfigurationService configurationService: IConfigurationService, + @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, + @ITelemetryService telemetryService: ITelemetryService, + @IWorkspaceContextService contextService: IWorkspaceContextService, + @IStorageService storageService: IStorageService, + @IInstantiationService instantiationService: IInstantiationService, + @IThemeService themeService: IThemeService, + @IContextMenuService contextMenuService: IContextMenuService, + @IExtensionService extensionService: IExtensionService + ) { + super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); + } + } + const viewletDescriptor = ViewletDescriptor.create( + PaneContainerViewlet, + viewContainer.id, + viewContainer.name, + isString(viewContainer.icon) ? viewContainer.icon : undefined, + viewContainer.order, + viewContainer.icon instanceof URI ? viewContainer.icon : undefined + ); + + Registry.as(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); + }; + + const viewContainerRegistry = Registry.as(ViewExtensions.ViewContainersRegistry); + viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer)); + viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => { + switch (viewContainerLocation) { + case ViewContainerLocation.Panel: + registerPanel(viewContainer); + return; + case ViewContainerLocation.Sidebar: + if (viewContainer.ctorDescriptor) { + registerViewlet(viewContainer); + } + return; + } + }); +})(); diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index 3ededdd5bf7..b3a0ef0e024 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -130,8 +130,8 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer { return views; } - onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { - const panes: ViewPane[] = super.onDidAddViews(added); + onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] { + const panes: ViewPane[] = super.onDidAddViewDescriptors(added); for (let i = 0; i < added.length; i++) { if (this.constantViewDescriptors.has(added[i].viewDescriptor.id)) { panes[i].setExpanded(false); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index 1da60908997..83d6b8821a7 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -8,21 +8,6 @@ import * as nls from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; -import { PanelRegistry, Extensions as PanelExtensions, PanelDescriptor, PaneCompositePanel } from 'vs/workbench/browser/panel'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ViewContainer, IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainerLocation } from 'vs/workbench/common/views'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { Viewlet, ViewletDescriptor, ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; -import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { isString } from 'vs/base/common/types'; -import { URI } from 'vs/base/common/uri'; // Configuration (function registerConfiguration(): void { @@ -393,74 +378,3 @@ import { URI } from 'vs/base/common/uri'; } }); })(); - -// Viewlets & Panels -(function registerViewletsAndPanels(): void { - const registerPanel = (viewContainer: ViewContainer): void => { - class PaneContainerPanel extends PaneCompositePanel { - constructor( - @ITelemetryService telemetryService: ITelemetryService, - @IStorageService storageService: IStorageService, - @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, - @IContextMenuService contextMenuService: IContextMenuService, - @IExtensionService extensionService: IExtensionService, - @IWorkspaceContextService contextService: IWorkspaceContextService - ) { - super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService); - } - } - Registry.as(PanelExtensions.Panels).registerPanel(PanelDescriptor.create( - PaneContainerPanel, - viewContainer.id, - viewContainer.name, - isString(viewContainer.icon) ? viewContainer.icon : undefined, - viewContainer.order, - viewContainer.focusCommand?.id, - )); - }; - - const registerViewlet = (viewContainer: ViewContainer): void => { - class PaneContainerViewlet extends Viewlet { - constructor( - @IConfigurationService configurationService: IConfigurationService, - @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, - @ITelemetryService telemetryService: ITelemetryService, - @IWorkspaceContextService contextService: IWorkspaceContextService, - @IStorageService storageService: IStorageService, - @IEditorService editorService: IEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IThemeService themeService: IThemeService, - @IContextMenuService contextMenuService: IContextMenuService, - @IExtensionService extensionService: IExtensionService - ) { - super(viewContainer.id, (instantiationService as any).createInstance(viewContainer.ctorDescriptor!.ctor, ...(viewContainer.ctorDescriptor!.staticArguments || [])), telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, layoutService, configurationService); - } - } - const viewletDescriptor = ViewletDescriptor.create( - PaneContainerViewlet, - viewContainer.id, - viewContainer.name, - isString(viewContainer.icon) ? viewContainer.icon : undefined, - viewContainer.order, - viewContainer.icon instanceof URI ? viewContainer.icon : undefined - ); - - Registry.as(ViewletExtensions.Viewlets).registerViewlet(viewletDescriptor); - }; - - const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); - viewContainerRegistry.getViewContainers(ViewContainerLocation.Panel).forEach(viewContainer => registerPanel(viewContainer)); - viewContainerRegistry.onDidRegister(({ viewContainer, viewContainerLocation }) => { - switch (viewContainerLocation) { - case ViewContainerLocation.Panel: - registerPanel(viewContainer); - return; - case ViewContainerLocation.Sidebar: - if (viewContainer.ctorDescriptor) { - registerViewlet(viewContainer); - } - return; - } - }); -})(); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index 3b43c26044e..fbede54b565 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -341,6 +341,10 @@ export interface IView { readonly id: string; + isVisible(): boolean; + + isBodyVisible(): boolean; + } export interface IViewsViewlet extends IViewlet { @@ -354,9 +358,12 @@ export const IViewsService = createDecorator('viewsService'); export interface IViewsService { _serviceBrand: undefined; - getActiveViewWithId(id: string): IView | null; + readonly onDidChangeViewVisibility: Event<{ id: string, visible: boolean }>; + + getActiveViewWithId(id: string): T | null; openView(id: string, focus?: boolean): Promise; + } export const IViewDescriptorService = createDecorator('viewDescriptorService'); @@ -512,6 +519,10 @@ export interface IEditableData { } export interface IViewPaneContainer { + onDidAddViews: Event; + onDidRemoveViews: Event; + onDidChangeViewVisibility: Event; + setVisible(visible: boolean): void; isVisible(): boolean; focus(): void; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index f0741bcf4f5..97cc84812cb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -536,8 +536,8 @@ export class ExtensionsViewPaneContainer extends ViewPaneContainer implements IE ))).then(() => undefined); } - protected onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { - const addedViews = super.onDidAddViews(added); + protected onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] { + const addedViews = super.onDidAddViewDescriptors(added); this.progress(Promise.all(addedViews.map(addedView => (addedView).show(this.normalizedQuery()) .then(model => this.alertSearchResult(model.length, addedView.id)) diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index a6f5e8b4cae..5feb009f368 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -512,9 +512,9 @@ export class RemoteViewPaneContainer extends FilterViewPaneContainer implements return title; } - onDidAddViews(added: IAddedViewDescriptorRef[]): ViewPane[] { + onDidAddViewDescriptors(added: IAddedViewDescriptorRef[]): ViewPane[] { // Call to super MUST be first, since registering the additional view will cause this to be called again. - const panels: ViewPane[] = super.onDidAddViews(added); + const panels: ViewPane[] = super.onDidAddViewDescriptors(added); // This context key is set to false in the constructor, but is expected to be changed by resolver extensions to enable the forwarded ports view. const viewEnabled: boolean = !!forwardedPortsViewEnabled.getValue(this.contextKeyService); if (this.environmentService.configuration.remoteAuthority && !this.tunnelPanelDescriptor && viewEnabled) {