From 1e42f0b923eb1e3faaac1976dbd360aa6019c30c Mon Sep 17 00:00:00 2001 From: Alan Ren Date: Mon, 26 Apr 2021 14:01:00 -0700 Subject: [PATCH] enable inline actions for connection dialog's browse tab (#15227) * enable inline actions * comments --- .../connection/browser/connectionBrowseTab.ts | 161 +++++++++++------- .../browser/media/connectionBrowseTab.css | 25 ++- 2 files changed, 127 insertions(+), 59 deletions(-) diff --git a/src/sql/workbench/services/connection/browser/connectionBrowseTab.ts b/src/sql/workbench/services/connection/browser/connectionBrowseTab.ts index f40a83a5d0..ea09f65cef 100644 --- a/src/sql/workbench/services/connection/browser/connectionBrowseTab.ts +++ b/src/sql/workbench/services/connection/browser/connectionBrowseTab.ts @@ -20,12 +20,13 @@ import { ServerTreeRenderer } from 'sql/workbench/services/objectExplorer/browse import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import * as DOM from 'vs/base/browser/dom'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { status } from 'vs/base/browser/ui/aria/aria'; import { IIdentityProvider, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IListAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { IAsyncDataSource, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; -import { IAction } from 'vs/base/common/actions'; +import { IAction, IActionViewItemProvider } from 'vs/base/common/actions'; import { debounce } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; @@ -35,8 +36,8 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./media/connectionBrowseTab'; import { localize } from 'vs/nls'; -import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { createAndFillInContextMenuActions, MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; @@ -73,8 +74,8 @@ export class ConnectionBrowserView extends Disposable implements IPanelView { private treeContainer: HTMLElement | undefined; private model: TreeModel | undefined; private treeLabels: ResourceLabels | undefined; + private treeMenus: ConnectionBrowseTreeMenuProvider | undefined; private treeDataSource: DataSource | undefined; - private readonly contextKey = new ContextKey(this.contextKeyService); private filterProgressBar: ProgressBar | undefined; public onDidChangeVisibility = Event.None; @@ -88,8 +89,6 @@ export class ConnectionBrowserView extends Disposable implements IPanelView { @IContextViewService private readonly contextViewService: IContextViewService, @IThemeService private readonly themeService: IThemeService, @ICommandService private readonly commandService: ICommandService, - @IMenuService private readonly menuService: IMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IConnectionManagementService private readonly connectionManagementService: IConnectionManagementService, @ICapabilitiesService private readonly capabilitiesService: ICapabilitiesService @@ -150,9 +149,16 @@ export class ConnectionBrowserView extends Disposable implements IPanelView { renderTree(container: HTMLElement): void { this.treeContainer = container.appendChild(DOM.$('div')); this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); + this.treeMenus = this.instantiationService.createInstance(ConnectionBrowseTreeMenuProvider); + const actionViewItemProvider = (action: IAction) => { + if (action instanceof MenuItemAction) { + return this.instantiationService.createInstance(MenuEntryActionViewItem, action); + } + return undefined; + }; const renderers: ITreeRenderer[] = [ - new ProviderElementRenderer(), - this.instantiationService.createInstance(TreeItemRenderer, this.treeLabels), + new ProviderElementRenderer(this.treeMenus, actionViewItemProvider), + this.instantiationService.createInstance(TreeItemRenderer, this.treeMenus, this.treeLabels, actionViewItemProvider), this.instantiationService.createInstance(ConnectionProfileRenderer, true), this.instantiationService.createInstance(ConnectionProfileGroupRenderer), this.instantiationService.createInstance(TreeNodeRenderer), @@ -176,26 +182,12 @@ export class ConnectionBrowserView extends Disposable implements IPanelView { accessibilityProvider: new ListAccessibilityProvider() }) as WorkbenchAsyncDataTree); this._register(this.tree.onContextMenu(e => { - let context: ITreeItem | ConnectionDialogTreeProviderElement | undefined; - let actionContext: TreeViewItemHandleArg | ConnectionDialogTreeProviderElement | undefined; - if (instanceOfITreeItemFromProvider(e.element)) { - context = e.element.element; - actionContext = { $treeViewId: e.element.treeId, $treeItemHandle: context.handle, $treeItem: context }; - } else if (e.element instanceof ConnectionDialogTreeProviderElement) { - context = e.element; - actionContext = e.element; - } - if (context) { - this.contextKey.set(context); - const menu = this.menuService.createMenu(MenuId.ConnectionDialogBrowseTreeContext, this.contextKeyService); - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result); - + const actionContext = this.treeMenus.getActionContext(e.element); + if (actionContext) { + const actions = this.treeMenus.getActions(e.element); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, - getActions: () => result.primary, + getActions: () => actions, getActionsContext: () => actionContext }); } @@ -334,6 +326,7 @@ class ListDelegate implements IListVirtualDelegate { interface TreeElementTemplate { readonly icon: HTMLElement; readonly name: HTMLElement; + readonly actionBar?: ActionBar; } abstract class BaseTreeItemRender implements ITreeRenderer { @@ -341,19 +334,37 @@ abstract class BaseTreeItemRender implements ITreeRenderer, index: number, templateData: TreeElementTemplate, height: number): void { templateData.name.innerText = this.getText(element.element); templateData.icon.classList.add('codicon', this.getIconClass(element.element)); + if (this.menus) { + templateData.actionBar.clear(); + templateData.actionBar.context = this.menus.getActionContext(element.element); + templateData.actionBar.push(this.menus.getActions(element.element), { icon: true, label: false }); + } } disposeTemplate(templateData: TreeElementTemplate): void { + templateData.actionBar?.dispose(); } } @@ -361,6 +372,10 @@ class ProviderElementRenderer extends BaseTreeItemRender{ $treeViewId: element.treeId, $treeItemHandle: element.element.handle, $treeItem: element.element }; + } else if (element instanceof ConnectionDialogTreeProviderElement) { + actionContext = element; + } + return actionContext; + } + + public getActions(element: any): IAction[] { + if (!this.contextKeyService) { + return []; + } + + let context: ITreeItem | ConnectionDialogTreeProviderElement | undefined; + if (instanceOfITreeItemFromProvider(element)) { + context = element.element; + } else if (element instanceof ConnectionDialogTreeProviderElement) { + context = element; + } + + if (!context) { + return []; + } + + const contextKeyService = this.contextKeyService.createScoped(); + const contextKey = new ContextKey(contextKeyService); + contextKey.set(context); + const menu = this.menuService.createMenu(MenuId.ConnectionDialogBrowseTreeContext, contextKeyService); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result); + + menu.dispose(); + contextKeyService.dispose(); + + return result.primary; + } } class TreeItemRenderer extends Disposable implements ITreeRenderer { static readonly ITEM_HEIGHT = 22; static readonly TREE_TEMPLATE_ID = 'treeExplorer'; - // private _actionRunner: MultipleSelectionActionRunner | undefined; - constructor( - // private treeViewId: string, - // private menus: TreeMenus, + private menus: ConnectionBrowseTreeMenuProvider, private labels: ResourceLabels, - // private actionViewItemProvider: IActionViewItemProvider, - // private aligner: Aligner, + private actionViewItemProvider: IActionViewItemProvider, @IThemeService private readonly themeService: IThemeService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILabelService private readonly labelService: ILabelService @@ -564,22 +624,18 @@ class TreeItemRenderer extends Disposable implements ITreeRenderer, index: number, templateData: ITreeExplorerTemplateData): void { @@ -595,7 +651,7 @@ class TreeItemRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); @@ -622,17 +678,8 @@ class TreeItemRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; - // templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); - // if (this._actionRunner) { - // templateData.actionBar.actionRunner = this._actionRunner; - // } - this.setAlignment(templateData.container, node); - templateData.elementDisposable = (this.themeService.onDidFileIconThemeChange(() => this.setAlignment(templateData.container, node))); - } - - private setAlignment(container: HTMLElement, treeItem: ITreeItem) { - // DOM.toggleClass(container.parentElement!, 'align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); + templateData.actionBar.context = this.menus.getActionContext(element.element); + templateData.actionBar.push(this.menus.getActions(element.element), { icon: true, label: false }); } private isFileKindThemeIcon(icon: ThemeIcon | undefined): boolean { @@ -660,8 +707,8 @@ class TreeItemRenderer extends Disposable implements ITreeRenderer