Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Show "Collapse All" command in tree view toolbar. #12514

Merged
merged 2 commits into from
May 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/core/src/browser/view-container.ts
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,7 @@ export interface DialogsMain {
}

export interface RegisterTreeDataProviderOptions {
showCollapseAll?: boolean
canSelectMany?: boolean
dragMimeTypes?: string[]
dropMimeTypes?: string[]
Expand Down
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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