Skip to content

Commit 32f393a

Browse files
authored
Hide empty plugin view containers from user (#13581)
* Hide empty plugin view containers from user * Fix playwright tests
1 parent 3da5b6b commit 32f393a

File tree

3 files changed

+57
-31
lines changed

3 files changed

+57
-31
lines changed

examples/playwright/src/tests/theia-quick-command.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ test.describe('Theia Quick Command', () => {
6161
});
6262

6363
test('should trigger \'Toggle Explorer View\' command after typing', async () => {
64-
await quickCommand.type('Toggle Explorer');
65-
await quickCommand.trigger('Toggle Explorer View');
64+
await quickCommand.type('Toggle Exp');
65+
await quickCommand.trigger('View: Toggle Explorer');
6666
expect(await quickCommand.isOpen()).toBe(false);
6767
const explorerView = new TheiaExplorerView(app);
6868
expect(await explorerView.isDisplayed()).toBe(true);

packages/core/src/browser/shell/view-contribution.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { injectable, inject, interfaces, optional } from 'inversify';
1818
import { Widget } from '@phosphor/widgets';
1919
import {
2020
MenuModelRegistry, Command, CommandContribution,
21-
MenuContribution, CommandRegistry
21+
MenuContribution, CommandRegistry, nls
2222
} from '../../common';
2323
import { KeybindingContribution, KeybindingRegistry } from '../keybinding';
2424
import { WidgetManager } from '../widget-manager';
@@ -69,7 +69,8 @@ export abstract class AbstractViewContribution<T extends Widget> implements Comm
6969
if (options.toggleCommandId) {
7070
this.toggleCommand = {
7171
id: options.toggleCommandId,
72-
label: 'Toggle ' + this.viewLabel + ' View'
72+
category: nls.localizeByDefault('View'),
73+
label: nls.localizeByDefault('Toggle {0}', this.viewLabel)
7374
};
7475
}
7576
}

packages/plugin-ext/src/main/browser/view/plugin-view-registry.ts

+52-27
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ export const PLUGIN_VIEW_DATA_FACTORY_ID = 'plugin-view-data';
5555

5656
export type ViewDataProvider = (params: { state?: object, viewInfo: View }) => Promise<TreeViewWidget>;
5757

58+
export interface ViewContainerInfo {
59+
id: string
60+
location: string
61+
options: ViewContainerTitleOptions
62+
onViewAdded: () => void
63+
}
64+
5865
@injectable()
5966
export class PluginViewRegistry implements FrontendApplicationContribution {
6067

@@ -96,7 +103,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
96103

97104
private readonly views = new Map<string, [string, View]>();
98105
private readonly viewsWelcome = new Map<string, ViewWelcome[]>();
99-
private readonly viewContainers = new Map<string, [string, ViewContainerTitleOptions]>();
106+
private readonly viewContainers = new Map<string, ViewContainerInfo>();
100107
private readonly containerViews = new Map<string, string[]>();
101108
private readonly viewClauseContexts = new Map<string, Set<string> | undefined>();
102109

@@ -324,34 +331,47 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
324331

325332
protected doRegisterViewContainer(id: string, location: string, options: ViewContainerTitleOptions): Disposable {
326333
const toDispose = new DisposableCollection();
327-
this.viewContainers.set(id, [location, options]);
328334
toDispose.push(Disposable.create(() => this.viewContainers.delete(id)));
329335
const toggleCommandId = `plugin.view-container.${id}.toggle`;
330-
toDispose.push(this.commands.registerCommand({
331-
id: toggleCommandId,
332-
label: 'Toggle ' + options.label + ' View'
333-
}, {
334-
execute: () => this.toggleViewContainer(id)
335-
}));
336-
toDispose.push(this.menus.registerMenuAction(CommonMenus.VIEW_VIEWS, {
337-
commandId: toggleCommandId,
338-
label: options.label
339-
}));
340-
toDispose.push(this.quickView?.registerItem({
341-
label: options.label,
342-
open: async () => {
343-
const widget = await this.openViewContainer(id);
336+
// Some plugins may register empty view containers.
337+
// We should not register commands for them immediately, as that leads to bad UX.
338+
// Instead, we register commands the first time we add a view to them.
339+
let activate = () => {
340+
toDispose.push(this.commands.registerCommand({
341+
id: toggleCommandId,
342+
category: nls.localizeByDefault('View'),
343+
label: nls.localizeByDefault('Toggle {0}', options.label)
344+
}, {
345+
execute: () => this.toggleViewContainer(id)
346+
}));
347+
toDispose.push(this.menus.registerMenuAction(CommonMenus.VIEW_VIEWS, {
348+
commandId: toggleCommandId,
349+
label: options.label
350+
}));
351+
toDispose.push(this.quickView?.registerItem({
352+
label: options.label,
353+
open: async () => {
354+
const widget = await this.openViewContainer(id);
355+
if (widget) {
356+
this.shell.activateWidget(widget.id);
357+
}
358+
}
359+
}));
360+
toDispose.push(Disposable.create(async () => {
361+
const widget = await this.getPluginViewContainer(id);
344362
if (widget) {
345-
this.shell.activateWidget(widget.id);
363+
widget.dispose();
346364
}
347-
}
348-
}));
349-
toDispose.push(Disposable.create(async () => {
350-
const widget = await this.getPluginViewContainer(id);
351-
if (widget) {
352-
widget.dispose();
353-
}
354-
}));
365+
}));
366+
// Ignore every subsequent activation call
367+
activate = () => { };
368+
};
369+
this.viewContainers.set(id, {
370+
id,
371+
location,
372+
options,
373+
onViewAdded: () => activate()
374+
});
355375
return toDispose;
356376
}
357377

@@ -374,6 +394,11 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
374394
this.views.set(view.id, [viewContainerId, view]);
375395
toDispose.push(Disposable.create(() => this.views.delete(view.id)));
376396

397+
const containerInfo = this.viewContainers.get(viewContainerId);
398+
if (containerInfo) {
399+
containerInfo.onViewAdded();
400+
}
401+
377402
const containerViews = this.getContainerViews(viewContainerId);
378403
containerViews.push(view.id);
379404
this.containerViews.set(viewContainerId, containerViews);
@@ -634,7 +659,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
634659
if (!data) {
635660
return undefined;
636661
}
637-
const [location] = data;
662+
const { location } = data;
638663
const containerWidget = await this.getOrCreateViewContainerWidget(containerId);
639664
if (!containerWidget.isAttached) {
640665
await this.shell.addWidget(containerWidget, {
@@ -648,7 +673,7 @@ export class PluginViewRegistry implements FrontendApplicationContribution {
648673
protected async prepareViewContainer(viewContainerId: string, containerWidget: ViewContainerWidget): Promise<void> {
649674
const data = this.viewContainers.get(viewContainerId);
650675
if (data) {
651-
const [, options] = data;
676+
const { options } = data;
652677
containerWidget.setTitleOptions(options);
653678
}
654679
for (const viewId of this.getContainerViews(viewContainerId)) {

0 commit comments

Comments
 (0)