diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 54c7463d9a0..8d4619fa70e 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -42,6 +42,7 @@ "textSearchProvider", "timeline", "tokenInformation", + "treeItemCheckbox", "treeViewReveal", "workspaceTrust", "telemetry" diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index ad6142a9143..79b436079c6 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from 'vs/base/common/lifecycle'; -import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, CheckboxUpdate } from 'vs/workbench/api/common/extHost.protocol'; import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem, ITreeViewDragAndDropController, IViewBadge } from 'vs/workbench/common/views'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; import { distinct } from 'vs/base/common/arrays'; @@ -170,6 +170,11 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie this._register(treeView.onDidChangeSelection(items => this._proxy.$setSelection(treeViewId, items.map(({ handle }) => handle)))); this._register(treeView.onDidChangeFocus(item => this._proxy.$setFocus(treeViewId, item.handle))); this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible))); + this._register(treeView.onDidChangeCheckboxState(items => { + this._proxy.$changeCheckboxState(treeViewId, items.map(item => { + return { treeItemHandle: item.handle, newState: item.checkboxChecked ?? false }; + })); + })); } private getTreeView(treeViewId: string): ITreeView | null { diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 708b4db0c46..6729f902156 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1295,6 +1295,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ThemeColor: extHostTypes.ThemeColor, ThemeIcon: extHostTypes.ThemeIcon, TreeItem: extHostTypes.TreeItem, + TreeItem2: extHostTypes.TreeItem, + TreeItemCheckboxState: extHostTypes.TreeItemCheckboxState, TreeItemCollapsibleState: extHostTypes.TreeItemCollapsibleState, TypeHierarchyItem: extHostTypes.TypeHierarchyItem, UIKind: UIKind, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 928250b003f..38759f95fb0 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1399,6 +1399,11 @@ export interface DataTransferDTO { readonly items: Array<[/* type */string, DataTransferItemDTO]>; } +export interface CheckboxUpdate { + treeItemHandle: string; + newState: boolean; +} + export interface ExtHostTreeViewsShape { $getChildren(treeViewId: string, treeItemHandle?: string): Promise; $handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise; @@ -1407,6 +1412,7 @@ export interface ExtHostTreeViewsShape { $setSelection(treeViewId: string, treeItemHandles: string[]): void; $setFocus(treeViewId: string, treeItemHandle: string): void; $setVisible(treeViewId: string, visible: boolean): void; + $changeCheckboxState(treeViewId: string, checkboxUpdates: CheckboxUpdate[]): void; $hasResolve(treeViewId: string): Promise; $resolve(treeViewId: string, treeItemHandle: string, token: CancellationToken): Promise; } diff --git a/src/vs/workbench/api/common/extHostTreeViews.ts b/src/vs/workbench/api/common/extHostTreeViews.ts index 12f0b616797..9a74d786aae 100644 --- a/src/vs/workbench/api/common/extHostTreeViews.ts +++ b/src/vs/workbench/api/common/extHostTreeViews.ts @@ -10,11 +10,11 @@ import { basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; -import { DataTransferDTO, ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; +import { CheckboxUpdate, DataTransferDTO, ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol'; import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions, TreeCommand, TreeViewPaneHandleArg } from 'vs/workbench/common/views'; import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands'; import { asPromise } from 'vs/base/common/async'; -import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType, TreeItem } from 'vs/workbench/api/common/extHostTypes'; +import { TreeItemCollapsibleState, TreeItemCheckboxState, ThemeIcon, MarkdownString as MarkdownStringType, TreeItem } from 'vs/workbench/api/common/extHostTypes'; import { isUndefinedOrNull, isString } from 'vs/base/common/types'; import { equals, coalesce } from 'vs/base/common/arrays'; import { ILogService } from 'vs/platform/log/common/log'; @@ -23,6 +23,7 @@ import { MarkdownString, ViewBadge, DataTransfer } from 'vs/workbench/api/common import { IMarkdownString } from 'vs/base/common/htmlContent'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; type TreeItemHandle = string; @@ -99,6 +100,7 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { get onDidChangeSelection() { return treeView.onDidChangeSelection; }, get visible() { return treeView.visible; }, get onDidChangeVisibility() { return treeView.onDidChangeVisibility; }, + get onDidChangeTreeCheckbox() { checkProposedApiEnabled(extension, 'treeItemCheckbox'); return treeView.onDidChangeTreeCheckbox; }, get message() { return treeView.message; }, set message(message: string) { treeView.message = message; @@ -234,6 +236,14 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape { treeView.setVisible(isVisible); } + $changeCheckboxState(treeViewId: string, checkboxUpdate: CheckboxUpdate[]): void { + const treeView = this.treeViews.get(treeViewId); + if (!treeView) { + throw new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)); + } + treeView.setCheckboxState(checkboxUpdate); + } + private createExtHostTreeView(id: string, options: vscode.TreeViewOptions, extension: IExtensionDescription): ExtHostTreeView { const treeView = new ExtHostTreeView(id, options, this._proxy, this.commands.converter, this.logService, extension); this.treeViews.set(id, treeView); @@ -296,6 +306,9 @@ class ExtHostTreeView extends Disposable { private _onDidChangeVisibility: Emitter = this._register(new Emitter()); readonly onDidChangeVisibility: Event = this._onDidChangeVisibility.event; + private _onDidChangeTreeCheckbox = this._register(new Emitter>()); + readonly onDidChangeTreeCheckbox: Event> = this._onDidChangeTreeCheckbox.event; + private _onDidChangeData: Emitter> = this._register(new Emitter>()); private refreshPromise: Promise = Promise.resolve(); @@ -474,6 +487,26 @@ class ExtHostTreeView extends Disposable { } } + async setCheckboxState(checkboxUpdates: CheckboxUpdate[]) { + const items = (await Promise.all(checkboxUpdates.map(async checkboxUpdate => { + const extensionItem = this.getExtensionElement(checkboxUpdate.treeItemHandle); + if (extensionItem) { + return { + extensionItem: extensionItem, + treeItem: await this.dataProvider.getTreeItem(extensionItem), + newState: checkboxUpdate.newState ? TreeItemCheckboxState.Checked : TreeItemCheckboxState.Unchecked + }; + } + return Promise.resolve(undefined); + }))).filter((item) => item !== undefined) as { extensionItem: T; treeItem: vscode.TreeItem2; newState: TreeItemCheckboxState }[]; + + items.forEach(item => { + item.treeItem.checkboxState = item.newState ? TreeItemCheckboxState.Checked : TreeItemCheckboxState.Unchecked; + }); + + this._onDidChangeTreeCheckbox.fire({ items: items.map(item => [item.extensionItem, item.newState]) }); + } + async handleDrag(sourceTreeItemHandles: TreeItemHandle[], treeDataTransfer: vscode.DataTransfer, token: CancellationToken): Promise { const extensionTreeItems: T[] = []; for (const sourceHandle of sourceTreeItemHandles) { @@ -717,8 +750,13 @@ class ExtHostTreeView extends Disposable { return command ? { ...this.commands.toInternal(command, disposable), originalId: command.command } : undefined; } + private getCheckbox(extensionTreeItem: vscode.TreeItem2): boolean | undefined { + return (extensionTreeItem.checkboxState !== undefined) ? + extensionTreeItem.checkboxState === TreeItemCheckboxState.Checked : undefined; + } + private validateTreeItem(extensionTreeItem: vscode.TreeItem) { - if (!TreeItem.isTreeItem(extensionTreeItem)) { + if (!TreeItem.isTreeItem(extensionTreeItem, this.extension)) { throw new Error(`Extension ${this.extension.identifier.value} has provided an invalid tree item.`); } } @@ -741,7 +779,8 @@ class ExtHostTreeView extends Disposable { iconDark: this.getDarkIconPath(extensionTreeItem) || icon, themeIcon: this.getThemeIcon(extensionTreeItem), collapsibleState: isUndefinedOrNull(extensionTreeItem.collapsibleState) ? TreeItemCollapsibleState.None : extensionTreeItem.collapsibleState, - accessibilityInformation: extensionTreeItem.accessibilityInformation + accessibilityInformation: extensionTreeItem.accessibilityInformation, + checkboxChecked: this.getCheckbox(extensionTreeItem) }; return { diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 2ccc9dbde2a..238a0be8e0b 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -13,10 +13,12 @@ import { nextCharLength } from 'vs/base/common/strings'; import { isString, isStringArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRelativePatternDto } from 'vs/workbench/api/common/extHost.protocol'; import { CellEditType, ICellPartialMetadataEdit, IDocumentMetadataEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; import type * as vscode from 'vscode'; /** @@ -2413,12 +2415,13 @@ export class TreeItem { command?: vscode.Command; contextValue?: string; tooltip?: string | vscode.MarkdownString; + checkboxState?: vscode.TreeItemCheckboxState; - static isTreeItem(thing: any): thing is TreeItem { + static isTreeItem(thing: any, extension: IExtensionDescription): thing is TreeItem { if (thing instanceof TreeItem) { return true; } - const treeItemThing = thing as vscode.TreeItem; + const treeItemThing = thing as vscode.TreeItem2; if (treeItemThing.label !== undefined && !isString(treeItemThing.label) && !(treeItemThing.label?.label)) { console.log('INVALID tree item, invalid label', treeItemThing.label); return false; @@ -2462,6 +2465,13 @@ export class TreeItem { console.log('INVALID tree item, invalid accessibilityInformation', treeItemThing.accessibilityInformation); return false; } + if (treeItemThing.checkboxState !== undefined) { + checkProposedApiEnabled(extension, 'treeItemCheckbox'); + if (treeItemThing.checkboxState !== TreeItemCheckboxState.Checked && treeItemThing.checkboxState !== TreeItemCheckboxState.Unchecked) { + console.log('INVALID tree item, invalid checkboxState', treeItemThing.checkboxState); + return false; + } + } return true; } @@ -2484,6 +2494,11 @@ export enum TreeItemCollapsibleState { Expanded = 2 } +export enum TreeItemCheckboxState { + Unchecked = 0, + Checked = 1 +} + @es5ClassCompat export class DataTransferItem { diff --git a/src/vs/workbench/browser/checkbox.ts b/src/vs/workbench/browser/checkbox.ts new file mode 100644 index 00000000000..dcea67bf6b1 --- /dev/null +++ b/src/vs/workbench/browser/checkbox.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as DOM from 'vs/base/browser/dom'; +import { Toggle } from 'vs/base/browser/ui/toggle/toggle'; +import { Codicon } from 'vs/base/common/codicons'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { localize } from 'vs/nls'; +import { attachToggleStyler } from 'vs/platform/theme/common/styler'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ITreeItem } from 'vs/workbench/common/views'; + +export class CheckboxStateHandler extends Disposable { + private readonly _onDidChangeCheckboxState = this._register(new Emitter()); + readonly onDidChangeCheckboxState: Event = this._onDidChangeCheckboxState.event; + + public setCheckboxState(node: ITreeItem) { + this._onDidChangeCheckboxState.fire([node]); + } +} + +export class TreeItemCheckbox extends Disposable { + public toggle: Toggle | undefined; + private checkboxContainer: HTMLDivElement; + public isDisposed = false; + + public static readonly checkboxClass = 'custom-view-tree-node-item-checkbox'; + + private readonly _onDidChangeState = new Emitter(); + readonly onDidChangeState: Event = this._onDidChangeState.event; + + constructor(container: HTMLElement, private checkboxStateHandler: CheckboxStateHandler, private themeService: IThemeService) { + super(); + this.checkboxContainer = container; + } + + public render(node: ITreeItem) { + if (node.checkboxChecked !== undefined) { + if (!this.toggle) { + this.createCheckbox(node); + } + else { + this.toggle.checked = node.checkboxChecked; + this.toggle.setIcon(this.toggle.checked ? Codicon.check : undefined); + } + } + } + + private createCheckbox(node: ITreeItem) { + if (node.checkboxChecked !== undefined) { + this.toggle = new Toggle({ + isChecked: node.checkboxChecked, + title: localize('check', "Check"), + icon: node.checkboxChecked ? Codicon.check : undefined + }); + + this.toggle.domNode.classList.add(TreeItemCheckbox.checkboxClass); + DOM.append(this.checkboxContainer, this.toggle.domNode); + this.registerListener(node); + } + } + + private registerListener(node: ITreeItem) { + if (this.toggle) { + this._register({ dispose: () => this.removeCheckbox() }); + this._register(this.toggle); + this._register(this.toggle.onChange(() => { + this.setCheckbox(node); + })); + this._register(attachToggleStyler(this.toggle, this.themeService)); + } + } + + private setCheckbox(node: ITreeItem) { + if (this.toggle && node.checkboxChecked !== undefined) { + node.checkboxChecked = this.toggle.checked; + this.toggle.setIcon(this.toggle.checked ? Codicon.check : undefined); + this.toggle.checked = this.toggle.checked; + this.checkboxStateHandler.setCheckboxState(node); + } + } + + private removeCheckbox() { + const children = this.checkboxContainer.children; + for (const child of children) { + this.checkboxContainer.removeChild(child); + } + } +} diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index 7d0ea329115..65101080870 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -120,6 +120,20 @@ padding-left: 3px; } +.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-checkbox { + width: 16px; + height: 16px; + margin: 3px 6px 3px 0px; + padding: 0px; + border: 1px solid var(--vscode-checkbox-border); + opacity: 1; + background-color: var(--vscode-checkbox-background); +} + +.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-checkbox.codicon { + font-size: 13px; + line-height: 15px; +} .customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .monaco-inputbox { line-height: normal; flex: 1; diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 59ab3d041ca..0d142b16ec8 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -66,6 +66,7 @@ import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService'; import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd'; import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd'; +import { CheckboxStateHandler, TreeItemCheckbox } from 'vs/workbench/browser/checkbox'; export class TreeViewPane extends ViewPane { @@ -236,6 +237,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { private readonly _onDidChangeDescription: Emitter = this._register(new Emitter()); readonly onDidChangeDescription: Event = this._onDidChangeDescription.event; + private readonly _onDidChangeCheckboxState: Emitter = this._register(new Emitter()); + readonly onDidChangeCheckboxState: Event = this._onDidChangeCheckboxState.event; + private readonly _onDidCompleteRefresh: Emitter = this._register(new Emitter()); constructor( @@ -598,7 +602,12 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { this.treeLabels = this._register(this.instantiationService.createInstance(ResourceLabels, this)); const dataSource = this.instantiationService.createInstance(TreeDataSource, this, (task: Promise) => this.progressService.withProgress({ location: this.id }, () => task)); const aligner = new Aligner(this.themeService); - const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner); + const checkboxStateHandler = this._register(new CheckboxStateHandler()); + this._register(checkboxStateHandler.onDidChangeCheckboxState(items => { + items.forEach(item => this.tree?.rerender(item)); + this._onDidChangeCheckboxState.fire(items); + })); + const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, treeMenus, this.treeLabels, actionViewItemProvider, aligner, checkboxStateHandler); const widgetAriaLabel = this._title; this.tree = this._register(this.instantiationService.createInstance(Tree, this.id, this.treeContainer!, new TreeViewDelegate(), [renderer], @@ -641,7 +650,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { } }, expandOnlyOnTwistieClick: (e: ITreeItem) => { - return !!e.command || this.configurationService.getValue<'singleClick' | 'doubleClick'>('workbench.tree.expandMode') === 'doubleClick'; + return !!e.command || e.checkboxChecked !== undefined || this.configurationService.getValue<'singleClick' | 'doubleClick'>('workbench.tree.expandMode') === 'doubleClick'; }, collapseByDefault: (e: ITreeItem): boolean => { return e.collapsibleState !== TreeItemCollapsibleState.Expanded; @@ -685,6 +694,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { if (!e.browserEvent) { return; } + if ((e.browserEvent.target as HTMLElement).classList.contains(TreeItemCheckbox.checkboxClass)) { + return; + } const selection = this.tree!.getSelection(); const command = await this.resolveCommand(selection.length === 1 ? selection[0] : undefined); @@ -1009,6 +1021,8 @@ interface ITreeExplorerTemplateData { container: HTMLElement; resourceLabel: IResourceLabel; icon: HTMLElement; + checkboxContainer: HTMLElement; + checkbox?: TreeItemCheckbox; actionBar: ActionBar; } @@ -1025,6 +1039,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer('explorer.decorations'); const labelResource = resource ? resource : URI.parse('missing:_icon_resource'); @@ -1171,8 +1192,6 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; - const disposableStore = new DisposableStore(); - templateData.elementDisposable = disposableStore; const menuActions = this.menus.getResourceActions(node); if (menuActions.menu) { disposableStore.add(menuActions.menu); @@ -1187,6 +1206,21 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.treeViewsService.removeRenderedTreeItemElement(node))); } + private renderCheckbox(node: ITreeItem, templateData: ITreeExplorerTemplateData, disposableStore: DisposableStore) { + if (node.checkboxChecked !== undefined) { + if (!templateData.checkbox) { + const checkbox = new TreeItemCheckbox(templateData.checkboxContainer, this.checkboxStateHandler, this.themeService); + templateData.checkbox = checkbox; + disposableStore.add({ dispose: () => { checkbox.dispose(); templateData.checkbox = undefined; } }); + } + templateData.checkbox.render(node); + } + else if (templateData.checkbox) { + templateData.checkbox.dispose(); + templateData.checkbox = undefined; + } + } + private setAlignment(container: HTMLElement, treeItem: ITreeItem) { container.parentElement!.classList.toggle('align-icon-with-twisty', this.aligner.alignIconWithTwisty(treeItem)); } @@ -1244,9 +1278,11 @@ class TreeRenderer extends Disposable implements ITreeRenderer | undefined; + private hasCheckboxes: boolean; constructor(private themeService: IThemeService) { super(); + this.hasCheckboxes = false; } set tree(tree: WorkbenchAsyncDataTree) { @@ -1254,6 +1290,9 @@ class Aligner extends Disposable { } public alignIconWithTwisty(treeItem: ITreeItem): boolean { + if (this.hasCheckboxes) { + return false; + } if (treeItem.collapsibleState !== TreeItemCollapsibleState.None) { return false; } @@ -1262,6 +1301,12 @@ class Aligner extends Disposable { } if (this._tree) { + if (!this.hasCheckboxes && treeItem.checkboxChecked !== undefined) { + this.hasCheckboxes = true; + // TODO: rerender already rendered elements + this._tree.rerender(); + return false; + } const parent: ITreeItem = this._tree.getParentElement(treeItem) || this._tree.getInput(); if (this.hasIcon(parent)) { return !!parent.children && parent.children.some(c => c.collapsibleState !== TreeItemCollapsibleState.None && !this.hasIcon(c)); diff --git a/src/vs/workbench/common/views.ts b/src/vs/workbench/common/views.ts index b84662e5a6d..acdb3fc1c9e 100644 --- a/src/vs/workbench/common/views.ts +++ b/src/vs/workbench/common/views.ts @@ -675,6 +675,8 @@ export interface ITreeView extends IDisposable { readonly onDidChangeWelcomeState: Event; + readonly onDidChangeCheckboxState: Event; + readonly container: any | undefined; refresh(treeItems?: ITreeItem[]): Promise; @@ -772,6 +774,8 @@ export interface ITreeItem { children?: ITreeItem[]; accessibilityInformation?: IAccessibilityInformation; + + checkboxChecked?: boolean; } export class ResolvableTreeItem implements ITreeItem { diff --git a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts index 6bedcd03a63..3106f775a7c 100644 --- a/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts +++ b/src/vs/workbench/services/extensions/common/extensionsApiProposals.ts @@ -65,6 +65,7 @@ export const allApiProposals = Object.freeze({ textSearchProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.textSearchProvider.d.ts', timeline: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.timeline.d.ts', tokenInformation: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tokenInformation.d.ts', + treeItemCheckbox: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeItemCheckbox.d.ts', treeViewReveal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewReveal.d.ts', tunnels: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.tunnels.d.ts', workspaceTrust: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.workspaceTrust.d.ts' diff --git a/src/vscode-dts/vscode.proposed.treeItemCheckbox.d.ts b/src/vscode-dts/vscode.proposed.treeItemCheckbox.d.ts new file mode 100644 index 00000000000..a1c5cb1df12 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.treeItemCheckbox.d.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + + export class TreeItem2 extends TreeItem { + /** + * [TreeItemCheckboxState](#TreeItemCheckboxState) of the tree item. + */ + checkboxState?: TreeItemCheckboxState; + } + + /** + * Checkbox state of the tree item + */ + export enum TreeItemCheckboxState { + /** + * Determines an item is unchecked + */ + Unchecked = 0, + /** + * Determines an item is checked + */ + Checked = 1 + } + + /** + * A data provider that provides tree data + */ + export interface TreeView { + /** + * An event to signal that an element or root has either been checked or unchecked. + */ + onDidChangeTreeCheckbox: Event>; + } + + export interface TreeCheckboxChangeEvent { + /** + * The item that was checked or unchecked. + */ + readonly items: [T, TreeItemCheckboxState][]; + } +}