mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-24 20:26:08 +00:00
Expose the focused element and change event in the TreeView API (#184268)
* Expose the focused element and event in the TreeView API * Exposed active TreeItem through extension proposal * Add proposal to test extension * Merge change selection and focus events * Finish selection+focus change in treeview * Clean up * Clean up * Add checkProposedApiEnabled back in --------- Co-authored-by: Ehab Younes <ehab.younes@sigasi.com> Co-authored-by: Alex Ross <alros@microsoft.com>
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
"timeline",
|
||||
"tokenInformation",
|
||||
"treeItemCheckbox",
|
||||
"treeViewActiveItem",
|
||||
"treeViewReveal",
|
||||
"testInvalidateResults",
|
||||
"workspaceTrust",
|
||||
|
||||
@@ -177,8 +177,7 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
private registerListeners(treeViewId: string, treeView: ITreeView): void {
|
||||
this._register(treeView.onDidExpandItem(item => this._proxy.$setExpanded(treeViewId, item.handle, true)));
|
||||
this._register(treeView.onDidCollapseItem(item => this._proxy.$setExpanded(treeViewId, item.handle, false)));
|
||||
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.onDidChangeSelectionAndFocus(items => this._proxy.$setSelectionAndFocus(treeViewId, items.selection.map(({ handle }) => handle), items.focus.handle)));
|
||||
this._register(treeView.onDidChangeVisibility(isVisible => this._proxy.$setVisible(treeViewId, isVisible)));
|
||||
this._register(treeView.onDidChangeCheckboxState(items => {
|
||||
this._proxy.$changeCheckboxState(treeViewId, <CheckboxUpdate[]>items.map(item => {
|
||||
|
||||
@@ -1563,8 +1563,7 @@ export interface ExtHostTreeViewsShape {
|
||||
$handleDrop(destinationViewId: string, requestId: number, treeDataTransfer: DataTransferDTO, targetHandle: string | undefined, token: CancellationToken, operationUuid?: string, sourceViewId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
|
||||
$handleDrag(sourceViewId: string, sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise<DataTransferDTO | undefined>;
|
||||
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
|
||||
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
|
||||
$setFocus(treeViewId: string, treeItemHandle: string): void;
|
||||
$setSelectionAndFocus(treeViewId: string, selectionHandles: string[], focusHandle: string): void;
|
||||
$setVisible(treeViewId: string, visible: boolean): void;
|
||||
$changeCheckboxState(treeViewId: string, checkboxUpdates: CheckboxUpdate[]): void;
|
||||
$hasResolve(treeViewId: string): Promise<boolean>;
|
||||
|
||||
@@ -24,6 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { ITreeViewsDnDService, TreeViewsDnDService } from 'vs/editor/common/services/treeViewsDnd';
|
||||
import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility';
|
||||
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
||||
type TreeItemHandle = string;
|
||||
|
||||
@@ -99,6 +100,14 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
get onDidExpandElement() { return treeView.onDidExpandElement; },
|
||||
get selection() { return treeView.selectedElements; },
|
||||
get onDidChangeSelection() { return treeView.onDidChangeSelection; },
|
||||
get activeItem() {
|
||||
checkProposedApiEnabled(extension, 'treeViewActiveItem');
|
||||
return treeView.focusedElement;
|
||||
},
|
||||
get onDidChangeActiveItem() {
|
||||
checkProposedApiEnabled(extension, 'treeViewActiveItem');
|
||||
return treeView.onDidChangeActiveItem;
|
||||
},
|
||||
get visible() { return treeView.visible; },
|
||||
get onDidChangeVisibility() { return treeView.onDidChangeVisibility; },
|
||||
get onDidChangeCheckboxState() {
|
||||
@@ -222,20 +231,12 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
treeView.setExpanded(treeItemHandle, expanded);
|
||||
}
|
||||
|
||||
$setSelection(treeViewId: string, treeItemHandles: string[]): void {
|
||||
$setSelectionAndFocus(treeViewId: string, selectedHandles: string[], focusedHandle: string) {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
throw new NoTreeViewError(treeViewId);
|
||||
}
|
||||
treeView.setSelection(treeItemHandles);
|
||||
}
|
||||
|
||||
$setFocus(treeViewId: string, treeItemHandles: string) {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
throw new NoTreeViewError(treeViewId);
|
||||
}
|
||||
treeView.setFocus(treeItemHandles);
|
||||
treeView.setSelectionAndFocus(selectedHandles, focusedHandle);
|
||||
}
|
||||
|
||||
$setVisible(treeViewId: string, isVisible: boolean): void {
|
||||
@@ -313,6 +314,9 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
private _onDidChangeSelection: Emitter<vscode.TreeViewSelectionChangeEvent<T>> = this._register(new Emitter<vscode.TreeViewSelectionChangeEvent<T>>());
|
||||
readonly onDidChangeSelection: Event<vscode.TreeViewSelectionChangeEvent<T>> = this._onDidChangeSelection.event;
|
||||
|
||||
private _onDidChangeActiveItem: Emitter<vscode.TreeViewActiveItemChangeEvent<T>> = this._register(new Emitter<vscode.TreeViewActiveItemChangeEvent<T>>());
|
||||
readonly onDidChangeActiveItem: Event<vscode.TreeViewActiveItemChangeEvent<T>> = this._onDidChangeActiveItem.event;
|
||||
|
||||
private _onDidChangeVisibility: Emitter<vscode.TreeViewVisibilityChangeEvent> = this._register(new Emitter<vscode.TreeViewVisibilityChangeEvent>());
|
||||
readonly onDidChangeVisibility: Event<vscode.TreeViewVisibilityChangeEvent> = this._onDidChangeVisibility.event;
|
||||
|
||||
@@ -479,15 +483,20 @@ class ExtHostTreeView<T> extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
setSelection(treeItemHandles: TreeItemHandle[]): void {
|
||||
if (!equals(this._selectedHandles, treeItemHandles)) {
|
||||
this._selectedHandles = treeItemHandles;
|
||||
setSelectionAndFocus(selectedHandles: TreeItemHandle[], focusedHandle: string): void {
|
||||
const changedSelection = !equals(this._selectedHandles, selectedHandles);
|
||||
this._selectedHandles = selectedHandles;
|
||||
|
||||
const changedFocus = this._focusedHandle !== focusedHandle;
|
||||
this._focusedHandle = focusedHandle;
|
||||
|
||||
if (changedSelection) {
|
||||
this._onDidChangeSelection.fire(Object.freeze({ selection: this.selectedElements }));
|
||||
}
|
||||
}
|
||||
|
||||
setFocus(treeItemHandle: TreeItemHandle) {
|
||||
this._focusedHandle = treeItemHandle;
|
||||
if (changedFocus) {
|
||||
this._onDidChangeActiveItem.fire(Object.freeze({ activeItem: this.focusedElement }));
|
||||
}
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
|
||||
@@ -215,6 +215,8 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
|
||||
private root: ITreeItem;
|
||||
private elementsToRefresh: ITreeItem[] = [];
|
||||
private lastSelection: readonly ITreeItem[] = [];
|
||||
private lastActive: ITreeItem;
|
||||
|
||||
private readonly _onDidExpandItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
readonly onDidExpandItem: Event<ITreeItem> = this._onDidExpandItem.event;
|
||||
@@ -222,11 +224,8 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
private readonly _onDidCollapseItem: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
readonly onDidCollapseItem: Event<ITreeItem> = this._onDidCollapseItem.event;
|
||||
|
||||
private _onDidChangeSelection: Emitter<readonly ITreeItem[]> = this._register(new Emitter<readonly ITreeItem[]>());
|
||||
readonly onDidChangeSelection: Event<readonly ITreeItem[]> = this._onDidChangeSelection.event;
|
||||
|
||||
private _onDidChangeFocus: Emitter<ITreeItem> = this._register(new Emitter<ITreeItem>());
|
||||
readonly onDidChangeFocus: Event<ITreeItem> = this._onDidChangeFocus.event;
|
||||
private _onDidChangeSelectionAndFocus: Emitter<{ selection: readonly ITreeItem[]; focus: ITreeItem }> = this._register(new Emitter<{ selection: readonly ITreeItem[]; focus: ITreeItem }>());
|
||||
readonly onDidChangeSelectionAndFocus: Event<{ selection: readonly ITreeItem[]; focus: ITreeItem }> = this._onDidChangeSelectionAndFocus.event;
|
||||
|
||||
private readonly _onDidChangeVisibility: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onDidChangeVisibility: Event<boolean> = this._onDidChangeVisibility.event;
|
||||
@@ -267,6 +266,7 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
) {
|
||||
super();
|
||||
this.root = new Root();
|
||||
this.lastActive = this.root;
|
||||
// Try not to add anything that could be costly to this constructor. It gets called once per tree view
|
||||
// during startup, and anything added here can affect performance.
|
||||
}
|
||||
@@ -702,10 +702,17 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
const customTreeKey = RawCustomTreeViewContextKey.bindTo(this.tree.contextKeyService);
|
||||
customTreeKey.set(true);
|
||||
this._register(this.tree.onContextMenu(e => this.onContextMenu(treeMenus, e, actionRunner)));
|
||||
this._register(this.tree.onDidChangeSelection(e => this._onDidChangeSelection.fire(e.elements)));
|
||||
|
||||
this._register(this.tree.onDidChangeSelection(e => {
|
||||
this.lastSelection = e.elements;
|
||||
this.lastActive = this.tree?.getFocus()[0] ?? this.lastActive;
|
||||
this._onDidChangeSelectionAndFocus.fire({ selection: this.lastSelection, focus: this.lastActive });
|
||||
}));
|
||||
this._register(this.tree.onDidChangeFocus(e => {
|
||||
if (e.elements.length) {
|
||||
this._onDidChangeFocus.fire(e.elements[0]);
|
||||
if (e.elements.length && (e.elements[0] !== this.lastActive)) {
|
||||
this.lastActive = e.elements[0];
|
||||
this.lastSelection = this.tree?.getSelection() ?? this.lastSelection;
|
||||
this._onDidChangeSelectionAndFocus.fire({ selection: this.lastSelection, focus: this.lastActive });
|
||||
}
|
||||
}));
|
||||
this._register(this.tree.onDidChangeCollapseState(e => {
|
||||
@@ -957,7 +964,8 @@ abstract class AbstractTreeView extends Disposable implements ITreeView {
|
||||
}
|
||||
const newSelection = tree.getSelection();
|
||||
if (oldSelection.length !== newSelection.length || oldSelection.some((value, index) => value.handle !== newSelection[index].handle)) {
|
||||
this._onDidChangeSelection.fire(newSelection);
|
||||
this.lastSelection = newSelection;
|
||||
this._onDidChangeSelectionAndFocus.fire({ selection: this.lastSelection, focus: this.lastActive });
|
||||
}
|
||||
this.refreshing = false;
|
||||
this._onDidCompleteRefresh.fire();
|
||||
|
||||
@@ -667,9 +667,7 @@ export interface ITreeView extends IDisposable {
|
||||
|
||||
readonly onDidCollapseItem: Event<ITreeItem>;
|
||||
|
||||
readonly onDidChangeSelection: Event<readonly ITreeItem[]>;
|
||||
|
||||
readonly onDidChangeFocus: Event<ITreeItem>;
|
||||
readonly onDidChangeSelectionAndFocus: Event<{ selection: readonly ITreeItem[]; focus: ITreeItem }>;
|
||||
|
||||
readonly onDidChangeVisibility: Event<boolean>;
|
||||
|
||||
|
||||
@@ -91,6 +91,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',
|
||||
treeViewActiveItem: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.treeViewActiveItem.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',
|
||||
windowActivity: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.windowActivity.d.ts',
|
||||
|
||||
30
src/vscode-dts/vscode.proposed.treeViewActiveItem.d.ts
vendored
Normal file
30
src/vscode-dts/vscode.proposed.treeViewActiveItem.d.ts
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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' {
|
||||
|
||||
// https://github.com/microsoft/vscode/issues/170248
|
||||
|
||||
export interface TreeView<T> extends Disposable {
|
||||
/**
|
||||
* Currently active item.
|
||||
*/
|
||||
readonly activeItem: T | undefined;
|
||||
/**
|
||||
* Event that is fired when the {@link TreeView.activeItem active item} has changed
|
||||
*/
|
||||
readonly onDidChangeActiveItem: Event<TreeViewActiveItemChangeEvent<T>>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event that is fired when there is a change in {@link TreeView.activeItem tree view's active item}
|
||||
*/
|
||||
export interface TreeViewActiveItemChangeEvent<T> {
|
||||
/**
|
||||
* Active item.
|
||||
*/
|
||||
readonly activeItem: T | undefined;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user