diff --git a/src/vs/workbench/browser/parts/views/viewsViewlet.ts b/src/vs/workbench/browser/parts/views/viewsViewlet.ts index c53f2171cbe65..dc1175d6c2f14 100644 --- a/src/vs/workbench/browser/parts/views/viewsViewlet.ts +++ b/src/vs/workbench/browser/parts/views/viewsViewlet.ts @@ -5,7 +5,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { IViewDescriptor, IViewDescriptorService, IAddedViewDescriptorRef } from 'vs/workbench/common/views'; +import { IViewDescriptor, IViewDescriptorService, IAddedViewDescriptorRef, IView } from 'vs/workbench/common/views'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -48,13 +48,6 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer { this.onFilterChanged(newFilterValue); })); - this._register(this.onDidChangeViewVisibility(view => { - const descriptorMap = Array.from(this.allViews.entries()).find(entry => entry[1].has(view.id)); - if (descriptorMap && !this.filterValue?.includes(descriptorMap[0])) { - this.setFilter(descriptorMap[1].get(view.id)!); - } - })); - this._register(this.viewContainerModel.onDidChangeActiveViewDescriptors(() => { this.updateAllViews(this.viewContainerModel.activeViewDescriptors); })); @@ -137,6 +130,17 @@ export abstract class FilterViewPaneContainer extends ViewPaneContainer { return panes; } + override openView(id: string, focus?: boolean): IView | undefined { + const result = super.openView(id, focus); + if (result) { + const descriptorMap = Array.from(this.allViews.entries()).find(entry => entry[1].has(id)); + if (descriptorMap && !this.filterValue?.includes(descriptorMap[0])) { + this.setFilter(descriptorMap[1].get(id)!); + } + } + return result; + } + abstract override getTitle(): string; } diff --git a/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts b/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts index a1da7375df7be..cda65c122881d 100644 --- a/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts +++ b/src/vs/workbench/contrib/remote/browser/explorerViewItems.ts @@ -4,46 +4,60 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IAction } from 'vs/base/common/actions'; -import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IRemoteExplorerService, REMOTE_EXPLORER_TYPE_KEY } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox'; import { IViewDescriptor } from 'vs/workbench/common/views'; import { isStringArray } from 'vs/base/common/types'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { Action2, MenuId } from 'vs/platform/actions/common/actions'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { VIEWLET_ID } from 'vs/workbench/contrib/remote/browser/remoteExplorer'; -import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { Disposable, DisposableMap } from 'vs/base/common/lifecycle'; interface IRemoteSelectItem extends ISelectOptionItem { authority: string[]; virtualWorkspace?: string; + dispose(): void; } -export class SwitchRemoteViewItem extends SelectActionViewItem { +export const SELECTED_REMOTE_IN_EXPLORER = new RawContextKey('selectedRemoteInExplorer', ''); + +export class SwitchRemoteViewItem extends Disposable { + private switchRemoteMenu: MenuId; + private completedRemotes: DisposableMap = this._register(new DisposableMap()); + private readonly selectedRemoteContext: IContextKey; constructor( - action: IAction, - private readonly optionsItems: IRemoteSelectItem[], - @IContextViewService contextViewService: IContextViewService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IRemoteExplorerService private remoteExplorerService: IRemoteExplorerService, @IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService, @IStorageService private readonly storageService: IStorageService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { - super(null, action, optionsItems, 0, contextViewService, defaultSelectBoxStyles, { ariaLabel: nls.localize('remotes', 'Switch Remote') }); + super(); + this.selectedRemoteContext = SELECTED_REMOTE_IN_EXPLORER.bindTo(contextKeyService); + + this.switchRemoteMenu = new MenuId('workbench.remote.menu.switchRemoteMenu'); + this._register(MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { + submenu: this.switchRemoteMenu, + title: nls.localize('switchRemote.label', "Switch Remote"), + group: 'navigation', + when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), + order: 1, + isSelection: true + })); + this._register(remoteExplorerService.onDidChangeTargetType(e => { + this.select(e); + })); } public setSelectionForConnection(): boolean { let isSetForConnection = false; - if (this.optionsItems.length > 0) { - let index = 0; + if (this.completedRemotes.size > 0) { + let authority: string[] | undefined; const remoteAuthority = this.environmentService.remoteAuthority; let virtualWorkspace: string | undefined; if (!remoteAuthority) { @@ -54,78 +68,77 @@ export class SwitchRemoteViewItem extends SelectActionViewItem 1) { - super.render(container); - container.classList.add('switch-remote'); + public removeOptionItems(views: IViewDescriptor[]) { + for (const view of views) { + if (view.group && view.group.startsWith('targets') && view.remoteAuthority && (!view.when || this.contextKeyService.contextMatchesRules(view.when))) { + const authority = isStringArray(view.remoteAuthority) ? view.remoteAuthority : [view.remoteAuthority]; + this.completedRemotes.deleteAndDispose(authority[0]); + } } } - protected override getActionContext(_: string, index: number): IRemoteSelectItem { - return this.optionsItems[index]; - } - - static createOptionItems(views: IViewDescriptor[], contextKeyService: IContextKeyService): IRemoteSelectItem[] { - const options: IRemoteSelectItem[] = []; - views.forEach(view => { - if (view.group && view.group.startsWith('targets') && view.remoteAuthority && (!view.when || contextKeyService.contextMatchesRules(view.when))) { - options.push({ text: view.name, authority: isStringArray(view.remoteAuthority) ? view.remoteAuthority : [view.remoteAuthority], virtualWorkspace: view.virtualWorkspace }); + public createOptionItems(views: IViewDescriptor[]) { + const startingCount = this.completedRemotes.size; + for (const view of views) { + if (view.group && view.group.startsWith('targets') && view.remoteAuthority && (!view.when || this.contextKeyService.contextMatchesRules(view.when))) { + const text = view.name; + const authority = isStringArray(view.remoteAuthority) ? view.remoteAuthority : [view.remoteAuthority]; + if (this.completedRemotes.has(authority[0])) { + continue; + } + const thisCapture = this; + const action = registerAction2(class extends Action2 { + constructor() { + super({ + id: `workbench.action.remoteExplorer.show.${authority[0]}`, + title: text, + toggled: SELECTED_REMOTE_IN_EXPLORER.isEqualTo(authority[0]), + menu: { + id: thisCapture.switchRemoteMenu + } + }); + } + async run(): Promise { + thisCapture.select(authority); + } + }); + this.completedRemotes.set(authority[0], { text, authority, virtualWorkspace: view.virtualWorkspace, dispose: () => action.dispose() }); } - }); - return options; - } -} - -export class SwitchRemoteAction extends Action2 { - - public static readonly ID = 'remote.explorer.switch'; - public static readonly LABEL = nls.localize('remote.explorer.switch', "Switch Remote"); - - constructor() { - super({ - id: SwitchRemoteAction.ID, - title: SwitchRemoteAction.LABEL, - menu: [{ - id: MenuId.ViewContainerTitle, - when: ContextKeyExpr.equals('viewContainer', VIEWLET_ID), - group: 'navigation', - order: 1 - }], - }); - } - - public async run(accessor: ServicesAccessor, args: IRemoteSelectItem): Promise { - accessor.get(IRemoteExplorerService).targetType = args.authority; + } + if (this.completedRemotes.size > startingCount) { + this.setSelectionForConnection(); + } } } diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index c9cd0c348a2cd..f6a630e3f4cd0 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -26,7 +26,6 @@ import { IExtensionDescription, IRelaxedExtensionDescription } from 'vs/platform import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { registerAction2 } from 'vs/platform/actions/common/actions'; import { IProgress, IProgressStep, IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -35,8 +34,7 @@ import { ReconnectionWaitEvent, PersistentConnectionEventType } from 'vs/platfor import Severity from 'vs/base/common/severity'; import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { SwitchRemoteViewItem, SwitchRemoteAction } from 'vs/workbench/contrib/remote/browser/explorerViewItems'; -import { Action } from 'vs/base/common/actions'; +import { SwitchRemoteViewItem } from 'vs/workbench/contrib/remote/browser/explorerViewItems'; import { isStringArray } from 'vs/base/common/types'; import { IRemoteExplorerService } from 'vs/workbench/services/remote/common/remoteExplorerService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -52,7 +50,6 @@ import * as icons from 'vs/workbench/contrib/remote/browser/remoteIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { ITimerService } from 'vs/workbench/services/timer/browser/timerService'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; -import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { IWalkthroughsService } from 'vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedService'; @@ -582,8 +579,8 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo helpInformation: HelpInformation[] = []; private _onDidChangeHelpInformation = new Emitter(); public onDidChangeHelpInformation: Event = this._onDidChangeHelpInformation.event; - private hasSetSwitchForConnection: boolean = false; private hasRegisteredHelpView: boolean = false; + private remoteSwitcher: SwitchRemoteViewItem | undefined; constructor( @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @@ -596,11 +593,12 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo @IContextMenuService contextMenuService: IContextMenuService, @IExtensionService extensionService: IExtensionService, @IRemoteExplorerService private readonly remoteExplorerService: IRemoteExplorerService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IViewDescriptorService viewDescriptorService: IViewDescriptorService ) { super(VIEWLET_ID, remoteExplorerService.onDidChangeTargetType, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService, viewDescriptorService); this.addConstantViewDescriptors([this.helpPanelDescriptor]); + this._register(this.remoteSwitcher = this.instantiationService.createInstance(SwitchRemoteViewItem)); + const viewsRegistry = Registry.as(Extensions.ViewsRegistry); remoteHelpExtPoint.setHandler((extensions) => { const helpInformation: HelpInformation[] = []; for (const extension of extensions) { @@ -610,7 +608,6 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo this.helpInformation = helpInformation; this._onDidChangeHelpInformation.fire(); - const viewsRegistry = Registry.as(Extensions.ViewsRegistry); if (this.helpInformation.length && !this.hasRegisteredHelpView) { viewsRegistry.registerViews([this.helpPanelDescriptor], this.viewContainer); this.hasRegisteredHelpView = true; @@ -619,6 +616,23 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo this.hasRegisteredHelpView = false; } }); + this.remoteSwitcher.createOptionItems(viewsRegistry.getViews(this.viewContainer)); + this._register(viewsRegistry.onViewsRegistered(e => { + const remoteViews: IViewDescriptor[] = []; + for (const view of e) { + if (view.viewContainer.id === VIEWLET_ID) { + remoteViews.push(...view.views); + } + } + if (remoteViews.length > 0) { + this.remoteSwitcher!.createOptionItems(remoteViews); + } + })); + this._register(viewsRegistry.onViewsDeregistered(e => { + if (e.viewContainer.id === VIEWLET_ID) { + this.remoteSwitcher!.removeOptionItems(e.views); + } + })); } private _handleRemoteInfoExtensionPoint(extension: IExtensionPointUser, helpInformation: HelpInformation[]) { @@ -649,29 +663,12 @@ class RemoteViewPaneContainer extends FilterViewPaneContainer implements IViewMo this.remoteExplorerService.targetType = isStringArray(viewDescriptor.remoteAuthority) ? viewDescriptor.remoteAuthority : [viewDescriptor.remoteAuthority!]; } - public override getActionViewItem(action: Action): IActionViewItem | undefined { - if (action.id === SwitchRemoteAction.ID) { - const optionItems = SwitchRemoteViewItem.createOptionItems(Registry.as(Extensions.ViewsRegistry).getViews(this.viewContainer), this.contextKeyService); - const item = this.instantiationService.createInstance(SwitchRemoteViewItem, action, optionItems); - if (!this.hasSetSwitchForConnection) { - this.hasSetSwitchForConnection = item.setSelectionForConnection(); - } else { - item.setSelection(); - } - return item; - } - - return super.getActionViewItem(action); - } - getTitle(): string { const title = nls.localize('remote.explorer', "Remote Explorer"); return title; } } -registerAction2(SwitchRemoteAction); - Registry.as(Extensions.ViewContainersRegistry).registerViewContainer( { id: VIEWLET_ID,