Skip to content

Commit

Permalink
Show "Collapse All" command in tree view toolbar. (#12514)
Browse files Browse the repository at this point in the history
Contributed on behalf of STMicroelectronics

Signed-off-by: Thomas Mäder <t.s.maeder@gmail.com>
  • Loading branch information
tsmaeder committed May 23, 2023
1 parent e94536a commit 700799e
Show file tree
Hide file tree
Showing 8 changed files with 77 additions and 5 deletions.
21 changes: 21 additions & 0 deletions packages/core/src/browser/view-container.ts
Expand Up @@ -76,6 +76,20 @@ export namespace BadgeWidget {
}
}

/**
* A widget that may change it's internal structure dynamically. Current use is for
* updating the toolbar when a contributed view is contructed "lazily"
*/
export interface DynamicToolbarWidget {
onDidChangeToolbarItems: CommonEvent<void>;
}

export namespace DynamicToolbarWidget {
export function is(arg: unknown): arg is DynamicToolbarWidget {
return isObject(arg) && 'onDidChangeToolbarItems' in arg;
}
}

/**
* A view container holds an arbitrary number of widgets inside a split panel.
* Each widget is wrapped in a _part_ that displays the widget title and toolbar
Expand Down Expand Up @@ -970,6 +984,13 @@ export class ViewContainerPart extends BaseWidget {
this.wrapped.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire(), undefined, this.toDispose);
}

if (DynamicToolbarWidget.is(this.wrapped)) {
this.wrapped.onDidChangeToolbarItems(() => {
this.toolbar.updateTarget(this.wrapped);
this.viewContainer?.update();
});
}

const { header, body, disposable } = this.createContent();
this.header = header;
this.body = body;
Expand Down
1 change: 1 addition & 0 deletions packages/plugin-ext/src/common/plugin-api-rpc.ts
Expand Up @@ -728,6 +728,7 @@ export interface DialogsMain {
}

export interface RegisterTreeDataProviderOptions {
showCollapseAll?: boolean
canSelectMany?: boolean
dragMimeTypes?: string[]
dropMimeTypes?: string[]
Expand Down
Expand Up @@ -84,6 +84,7 @@ import { PluginTerminalRegistry } from './plugin-terminal-registry';
import { DnDFileContentStore } from './view/dnd-file-content-store';
import { WebviewContextKeys } from './webview/webview-context-keys';
import { LanguagePackService, languagePackServicePath } from '../../common/language-pack-service';
import { TabBarToolbarContribution } from '@theia/core/lib/browser/shell/tab-bar-toolbar';

export default new ContainerModule((bind, unbind, isBound, rebind) => {

Expand All @@ -110,6 +111,7 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
bind(OpenUriCommandHandler).toSelf().inSingletonScope();
bind(PluginApiFrontendContribution).toSelf().inSingletonScope();
bind(CommandContribution).toService(PluginApiFrontendContribution);
bind(TabBarToolbarContribution).toService(PluginApiFrontendContribution);

bind(EditorModelService).toSelf().inSingletonScope();

Expand Down
Expand Up @@ -15,21 +15,56 @@
// *****************************************************************************

import { injectable, inject } from '@theia/core/shared/inversify';
import { CommandRegistry, CommandContribution } from '@theia/core/lib/common';
import { CommandRegistry, CommandContribution, Command } from '@theia/core/lib/common';
import { OpenUriCommandHandler } from './commands';
import URI from '@theia/core/lib/common/uri';
import { TreeViewWidget } from './view/tree-view-widget';
import { CompositeTreeNode, Widget, codicon } from '@theia/core/lib/browser';
import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { PluginViewWidget } from './view/plugin-view-widget';

@injectable()
export class PluginApiFrontendContribution implements CommandContribution {
export class PluginApiFrontendContribution implements CommandContribution, TabBarToolbarContribution {

@inject(OpenUriCommandHandler)
protected readonly openUriCommandHandler: OpenUriCommandHandler;

static readonly COLLAPSE_ALL_COMMAND = Command.toDefaultLocalizedCommand({
id: 'treeviews.collapseAll',
iconClass: codicon('collapse-all'),
label: 'Collapse All'
});

registerCommands(commands: CommandRegistry): void {
commands.registerCommand(OpenUriCommandHandler.COMMAND_METADATA, {
execute: (arg: URI) => this.openUriCommandHandler.execute(arg),
isVisible: () => false
});
commands.registerCommand(PluginApiFrontendContribution.COLLAPSE_ALL_COMMAND, {
execute: (widget: Widget) => {
if (widget instanceof PluginViewWidget && widget.widgets[0] instanceof TreeViewWidget) {
const model = widget.widgets[0].model;
if (CompositeTreeNode.is(model.root)) {
for (const child of model.root.children) {
if (CompositeTreeNode.is(child)) {
model.collapseAll(child);
}
}
}
}
},
isVisible: (widget: Widget) => widget instanceof PluginViewWidget && widget.widgets[0] instanceof TreeViewWidget && widget.widgets[0].showCollapseAll
});

}

registerToolbarItems(registry: TabBarToolbarRegistry): void {
registry.registerItem({
id: PluginApiFrontendContribution.COLLAPSE_ALL_COMMAND.id,
command: PluginApiFrontendContribution.COLLAPSE_ALL_COMMAND.id,
tooltip: PluginApiFrontendContribution.COLLAPSE_ALL_COMMAND.label,
icon: PluginApiFrontendContribution.COLLAPSE_ALL_COMMAND.iconClass,
priority: 1000
});
}
}
11 changes: 9 additions & 2 deletions packages/plugin-ext/src/main/browser/view/plugin-view-widget.ts
Expand Up @@ -21,7 +21,7 @@ import { CommandRegistry } from '@theia/core/lib/common/command';
import { StatefulWidget } from '@theia/core/lib/browser/shell/shell-layout-restorer';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { TreeViewWidget } from './tree-view-widget';
import { BadgeWidget, DescriptionWidget } from '@theia/core/lib/browser/view-container';
import { BadgeWidget, DescriptionWidget, DynamicToolbarWidget } from '@theia/core/lib/browser/view-container';
import { DisposableCollection, Emitter, Event } from '@theia/core/lib/common';
import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';

Expand All @@ -32,7 +32,7 @@ export class PluginViewWidgetIdentifier {
}

@injectable()
export class PluginViewWidget extends Panel implements StatefulWidget, DescriptionWidget, BadgeWidget {
export class PluginViewWidget extends Panel implements StatefulWidget, DescriptionWidget, BadgeWidget, DynamicToolbarWidget {

currentViewContainerId?: string;

Expand All @@ -46,6 +46,11 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti
protected onDidChangeBadgeEmitter = new Emitter<void>();
protected onDidChangeBadgeTooltipEmitter = new Emitter<void>();
protected toDispose = new DisposableCollection(this.onDidChangeDescriptionEmitter, this.onDidChangeBadgeEmitter, this.onDidChangeBadgeTooltipEmitter);
protected readonly onDidChangeToolbarItemsEmitter = new Emitter<void>();

get onDidChangeToolbarItems(): Event<void> {
return this.onDidChangeToolbarItemsEmitter.event;
}

@inject(MenuModelRegistry)
protected readonly menus: MenuModelRegistry;
Expand Down Expand Up @@ -192,11 +197,13 @@ export class PluginViewWidget extends Panel implements StatefulWidget, Descripti
widget.onDidChangeBadgeTooltip(() => this.onDidChangeBadgeTooltipEmitter.fire());
}
this.updateWidgetMessage();
this.onDidChangeToolbarItemsEmitter.fire();
}

override insertWidget(index: number, widget: Widget): void {
super.insertWidget(index, widget);
this.updateWidgetMessage();
this.onDidChangeToolbarItemsEmitter.fire();
}

override dispose(): void {
Expand Down
Expand Up @@ -164,6 +164,7 @@ export namespace CompositeTreeViewNode {
@injectable()
export class TreeViewWidgetOptions {
id: string;
showCollapseAll: boolean | undefined;
multiSelect: boolean | undefined;
dragMimeTypes: string[] | undefined;
dropMimeTypes: string[] | undefined;
Expand Down Expand Up @@ -443,6 +444,10 @@ export class TreeViewWidget extends TreeViewWelcomeWidget {
this.treeDragType = `application/vnd.code.tree.${this.id.toLowerCase()}`;
}

get showCollapseAll(): boolean {
return this.options.showCollapseAll || false;
}

protected override renderIcon(node: TreeNode, props: NodeProps): React.ReactNode {
const icon = this.toNodeIcon(node);
if (icon) {
Expand Down
Expand Up @@ -63,6 +63,7 @@ export class TreeViewsMainImpl implements TreeViewsMain, Disposable {
this.treeViewProviders.set(treeViewId, this.viewRegistry.registerViewDataProvider(treeViewId, async ({ state, viewInfo }) => {
const options: TreeViewWidgetOptions = {
id: treeViewId,
showCollapseAll: $options.showCollapseAll,
multiSelect: $options.canSelectMany,
dragMimeTypes: $options.dragMimeTypes,
dropMimeTypes: $options.dropMimeTypes
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-ext/src/plugin/tree/tree-views.ts
Expand Up @@ -234,7 +234,7 @@ class TreeViewExtImpl<T> implements Disposable {
// make copies of optionally provided MIME types:
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes?.slice();
const dropMimeTypes = options.dragAndDropController?.dropMimeTypes?.slice();
proxy.$registerTreeDataProvider(treeViewId, { canSelectMany: options.canSelectMany, dragMimeTypes, dropMimeTypes });
proxy.$registerTreeDataProvider(treeViewId, { showCollapseAll: options.showCollapseAll, canSelectMany: options.canSelectMany, dragMimeTypes, dropMimeTypes });
this.toDispose.push(Disposable.create(() => this.proxy.$unregisterTreeDataProvider(treeViewId)));
options.treeDataProvider.onDidChangeTreeData?.(() => {
this.pendingRefresh = proxy.$refresh(treeViewId);
Expand Down

0 comments on commit 700799e

Please sign in to comment.