提交 bb0242c7 编写于 作者: J Joao Moreno

WorkbenchDataTree

上级 b134ef4a
......@@ -295,11 +295,11 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}
getFirstElementChild(location: TRef | null = null): T | null {
return this.model.getFirstChildElement(location);
return this.model.getFirstElementChild(location);
}
getLastElementAncestor(location: TRef | null = null): T | null {
return this.model.getLastAncestorElement(location);
return this.model.getLastElementAncestor(location);
}
// Tree
......
......@@ -11,6 +11,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Emitter, Event, mapEvent } from 'vs/base/common/event';
import { timeout } from 'vs/base/common/async';
import { ISequence } from 'vs/base/common/iterator';
import { IListStyles } from 'vs/base/browser/ui/list/listWidget';
export interface IDataSource<T extends NonNullable<any>> {
hasChildren(element: T | null): boolean;
......@@ -130,7 +131,7 @@ export class DataTree<T extends NonNullable<any>, TFilterData = void> implements
private _onDidChangeNodeState = new Emitter<IDataTreeNode<T>>();
private disposables: IDisposable[] = [];
protected disposables: IDisposable[] = [];
get onDidChangeFocus(): Event<ITreeEvent<T>> { return mapEvent(this.tree.onDidChangeFocus, asTreeEvent); }
get onDidChangeSelection(): Event<ITreeEvent<T>> { return mapEvent(this.tree.onDidChangeSelection, asTreeEvent); }
......@@ -142,8 +143,8 @@ export class DataTree<T extends NonNullable<any>, TFilterData = void> implements
get onMouseClick(): Event<ITreeMouseEvent<T>> { return mapEvent(this.tree.onMouseClick, asTreeMouseEvent); }
get onMouseDblClick(): Event<ITreeMouseEvent<T>> { return mapEvent(this.tree.onMouseDblClick, asTreeMouseEvent); }
get onContextMenu(): Event<ITreeContextMenuEvent<T>> { return mapEvent(this.tree.onContextMenu, asTreeContextMenuEvent); }
get onDidDOMFocus(): Event<void> { return this.tree.onDidFocus; }
get onDidDOMBlur(): Event<void> { return this.tree.onDidBlur; }
get onDidFocus(): Event<void> { return this.tree.onDidFocus; }
get onDidBlur(): Event<void> { return this.tree.onDidBlur; }
get onDidDispose(): Event<void> { return this.tree.onDidDispose; }
......@@ -170,6 +171,12 @@ export class DataTree<T extends NonNullable<any>, TFilterData = void> implements
this.tree.onDidChangeCollapseState(this._onDidChangeCollapseState, this, this.disposables);
}
// Widget
getHTMLElement(): HTMLElement {
return this.tree.getHTMLElement();
}
domFocus(): void {
this.tree.domFocus();
}
......@@ -178,99 +185,14 @@ export class DataTree<T extends NonNullable<any>, TFilterData = void> implements
this.tree.layout(height);
}
refresh(element: T | null): Thenable<void> {
return this.refreshNode(this.getNode(element), ChildrenResolutionReason.Refresh);
}
private getNode(element: T | null): IDataTreeNode<T> {
const node: IDataTreeNode<T> = this.nodes.get(element);
if (typeof node === 'undefined') {
throw new Error(`Data tree node not found: ${element}`);
}
return node;
}
private refreshNode(node: IDataTreeNode<T>, reason: ChildrenResolutionReason): Thenable<void> {
const hasChildren = this.dataSource.hasChildren(node.element);
if (!hasChildren) {
this.setChildren(node === this.root ? null : node);
return Promise.resolve();
} else {
node.state = DataTreeNodeState.Loading;
this._onDidChangeNodeState.fire(node);
const slowTimeout = timeout(800);
slowTimeout.then(() => {
node.state = DataTreeNodeState.Slow;
this._onDidChangeNodeState.fire(node);
}, _ => null);
return this.dataSource.getChildren(node.element)
.then(children => {
slowTimeout.cancel();
node.state = DataTreeNodeState.Loaded;
this._onDidChangeNodeState.fire(node);
const createTreeElement = (element: T): ITreeElement<IDataTreeNode<T>> => {
const collapsible = this.dataSource.hasChildren(element);
return {
element: {
element: element,
state: DataTreeNodeState.Uninitialized,
parent: node
},
collapsible,
collapsed: true
};
};
const nodeChildren = children.map<ITreeElement<IDataTreeNode<T>>>(createTreeElement);
this.setChildren(node === this.root ? null : node, nodeChildren);
this._onDidResolveChildren.fire({ element: node.element, reason });
}, err => {
slowTimeout.cancel();
node.state = DataTreeNodeState.Uninitialized;
this._onDidChangeNodeState.fire(node);
if (node !== this.root) {
this.tree.collapse(node);
}
return Promise.reject(err);
});
}
}
private _onDidChangeCollapseState(treeNode: ITreeNode<IDataTreeNode<T>, any>): void {
if (!treeNode.collapsed && treeNode.element.state === DataTreeNodeState.Uninitialized) {
this.refreshNode(treeNode.element, ChildrenResolutionReason.Expand);
}
style(styles: IListStyles): void {
this.tree.style(styles);
}
private setChildren(element: IDataTreeNode<T> | null, children?: ISequence<ITreeElement<IDataTreeNode<T>>>): void {
const insertedElements = new Set<T>();
const onDidCreateNode = (node: ITreeNode<IDataTreeNode<T>, TFilterData>) => {
if (node.element.element) {
insertedElements.add(node.element.element);
this.nodes.set(node.element.element, node.element);
}
};
const onDidDeleteNode = (node: ITreeNode<IDataTreeNode<T>, TFilterData>) => {
if (node.element.element) {
if (!insertedElements.has(node.element.element)) {
this.nodes.delete(node.element.element);
}
}
};
// Data Tree
this.tree.setChildren(element, children, onDidCreateNode, onDidDeleteNode);
refresh(element: T | null): Thenable<void> {
return this.refreshNode(this.getNode(element), ChildrenResolutionReason.Refresh);
}
// Tree
......@@ -364,6 +286,114 @@ export class DataTree<T extends NonNullable<any>, TFilterData = void> implements
return this.tree.getRelativeTop(this.getNode(element));
}
// Tree navigation
getParentElement(element: T): T | null {
return this.tree.getParentElement(this.getNode(element)).element;
}
getFirstElementChild(element: T | null = null): T | null {
return this.tree.getFirstElementChild(this.getNode(element)).element;
}
getLastElementAncestor(element: T | null = null): T | null {
return this.tree.getLastElementAncestor(this.getNode(element)).element;
}
// Implementation
private getNode(element: T | null): IDataTreeNode<T> {
const node: IDataTreeNode<T> = this.nodes.get(element);
if (typeof node === 'undefined') {
throw new Error(`Data tree node not found: ${element}`);
}
return node;
}
private refreshNode(node: IDataTreeNode<T>, reason: ChildrenResolutionReason): Thenable<void> {
const hasChildren = this.dataSource.hasChildren(node.element);
if (!hasChildren) {
this.setChildren(node === this.root ? null : node);
return Promise.resolve();
} else {
node.state = DataTreeNodeState.Loading;
this._onDidChangeNodeState.fire(node);
const slowTimeout = timeout(800);
slowTimeout.then(() => {
node.state = DataTreeNodeState.Slow;
this._onDidChangeNodeState.fire(node);
}, _ => null);
return this.dataSource.getChildren(node.element)
.then(children => {
slowTimeout.cancel();
node.state = DataTreeNodeState.Loaded;
this._onDidChangeNodeState.fire(node);
const createTreeElement = (element: T): ITreeElement<IDataTreeNode<T>> => {
const collapsible = this.dataSource.hasChildren(element);
return {
element: {
element: element,
state: DataTreeNodeState.Uninitialized,
parent: node
},
collapsible,
collapsed: true
};
};
const nodeChildren = children.map<ITreeElement<IDataTreeNode<T>>>(createTreeElement);
this.setChildren(node === this.root ? null : node, nodeChildren);
this._onDidResolveChildren.fire({ element: node.element, reason });
}, err => {
slowTimeout.cancel();
node.state = DataTreeNodeState.Uninitialized;
this._onDidChangeNodeState.fire(node);
if (node !== this.root) {
this.tree.collapse(node);
}
return Promise.reject(err);
});
}
}
private _onDidChangeCollapseState(treeNode: ITreeNode<IDataTreeNode<T>, any>): void {
if (!treeNode.collapsed && treeNode.element.state === DataTreeNodeState.Uninitialized) {
this.refreshNode(treeNode.element, ChildrenResolutionReason.Expand);
}
}
private setChildren(element: IDataTreeNode<T> | null, children?: ISequence<ITreeElement<IDataTreeNode<T>>>): void {
const insertedElements = new Set<T>();
const onDidCreateNode = (node: ITreeNode<IDataTreeNode<T>, TFilterData>) => {
if (node.element.element) {
insertedElements.add(node.element.element);
this.nodes.set(node.element.element, node.element);
}
};
const onDidDeleteNode = (node: ITreeNode<IDataTreeNode<T>, TFilterData>) => {
if (node.element.element) {
if (!insertedElements.has(node.element.element)) {
this.nodes.delete(node.element.element);
}
}
};
this.tree.setChildren(element, children, onDidCreateNode, onDidDeleteNode);
}
dispose(): void {
this.disposables = dispose(this.disposables);
}
......
......@@ -425,7 +425,7 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
return node === this.root ? null : node.element;
}
getFirstChildElement(location: number[]): T | null {
getFirstElementChild(location: number[]): T | null {
const node = this.getTreeNode(location);
if (node.children.length === 0) {
......@@ -435,7 +435,7 @@ export class IndexTreeModel<T, TFilterData = void> implements ITreeModel<T, TFil
return node.children[0].element;
}
getLastAncestorElement(location: number[]): T | null {
getLastElementAncestor(location: number[]): T | null {
const node = this.getTreeNode(location);
if (node.children.length === 0) {
......
......@@ -61,14 +61,14 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData = void> imp
return this.model.getParentElement(location);
}
getFirstChildElement(ref: T | null = null): T | null {
getFirstElementChild(ref: T | null = null): T | null {
const location = this.getElementLocation(ref);
return this.model.getFirstChildElement(location);
return this.model.getFirstElementChild(location);
}
getLastAncestorElement(ref: T | null = null): T | null {
getLastElementAncestor(ref: T | null = null): T | null {
const location = this.getElementLocation(ref);
return this.model.getLastAncestorElement(location);
return this.model.getLastElementAncestor(location);
}
getListIndex(element: T): number {
......
......@@ -95,8 +95,8 @@ export interface ITreeModel<T, TFilterData, TRef> {
getParentNodeLocation(location: TRef): TRef | null;
getParentElement(location: TRef | null): T | null;
getFirstChildElement(location: TRef | null): T | null;
getLastAncestorElement(location: TRef | null): T | null;
getFirstElementChild(location: TRef | null): T | null;
getLastElementAncestor(location: TRef | null): T | null;
isCollapsed(location: TRef): boolean;
setCollapsed(location: TRef, collapsed: boolean): boolean;
......
......@@ -35,8 +35,9 @@ import { InputFocusedContextKey } from 'vs/platform/workbench/common/contextkeys
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { ITreeOptions as ITreeOptions2, ITreeEvent } from 'vs/base/browser/ui/tree/abstractTree';
import { ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { DataTree, IDataSource } from 'vs/base/browser/ui/tree/dataTree';
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any>;
export type ListWidget = List<any> | PagedList<any> | ITree | ObjectTree<any, any> | DataTree<any, any>;
export const IListService = createDecorator<IListService>('listService');
......@@ -931,6 +932,69 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
}
}
export class WorkbenchDataTree<T extends NonNullable<any>, TFilterData = void> extends DataTree<T, TFilterData> {
readonly contextKeyService: IContextKeyService;
private hasSelectionOrFocus: IContextKey<boolean>;
private hasDoubleSelection: IContextKey<boolean>;
private hasMultiSelection: IContextKey<boolean>;
constructor(
container: HTMLElement,
delegate: IListVirtualDelegate<T>,
renderers: ITreeRenderer<T, TFilterData, any>[],
dataSource: IDataSource<T>,
options: ITreeOptions2<T, TFilterData>,
@IContextKeyService contextKeyService: IContextKeyService,
@IListService listService: IListService,
@IThemeService themeService: IThemeService,
@IConfigurationService configurationService: IConfigurationService
) {
super(container, delegate, renderers, dataSource, {
keyboardSupport: false,
selectOnMouseDown: true,
styleController: new DefaultStyleController(getSharedListStyleSheet()),
...computeStyles(themeService.getTheme(), defaultListStyles),
...handleListControllers(options, configurationService)
});
this.contextKeyService = createScopedContextKeyService(contextKeyService, this);
const listSupportsMultiSelect = WorkbenchListSupportsMultiSelectContextKey.bindTo(this.contextKeyService);
listSupportsMultiSelect.set(!(options.multipleSelectionSupport === false));
this.hasSelectionOrFocus = WorkbenchListHasSelectionOrFocus.bindTo(this.contextKeyService);
this.hasDoubleSelection = WorkbenchListDoubleSelection.bindTo(this.contextKeyService);
this.hasMultiSelection = WorkbenchListMultiSelection.bindTo(this.contextKeyService);
this.disposables.push(
this.contextKeyService,
(listService as ListService).register(this),
attachListStyler(this, themeService),
this.onDidChangeSelection(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
this.hasMultiSelection.set(selection.length > 1);
this.hasDoubleSelection.set(selection.length === 2);
}),
this.onDidChangeFocus(() => {
const selection = this.getSelection();
const focus = this.getFocus();
this.hasSelectionOrFocus.set(selection.length > 0 || focus.length > 0);
})
);
}
dispose(): void {
super.dispose();
this.disposables = dispose(this.disposables);
}
}
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
configurationRegistry.registerConfiguration({
......
......@@ -23,6 +23,7 @@ import { URI } from 'vs/base/common/uri';
import { IDownloadService } from 'vs/platform/download/common/download';
import { generateUuid } from 'vs/base/common/uuid';
import { ObjectTree } from 'vs/base/browser/ui/tree/objectTree';
import { DataTree } from 'vs/base/browser/ui/tree/dataTree';
// --- List Commands
......@@ -58,7 +59,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
......@@ -91,7 +92,7 @@ export function registerCommands(): void {
handler: (accessor, arg2) => focusDown(accessor, arg2)
});
function expandMultiSelection(focused: List<any> | PagedList<any> | ITree | ObjectTree<any, any>, previousFocus: any): void {
function expandMultiSelection(focused: List<any> | PagedList<any> | ITree | ObjectTree<any, any> | DataTree<any, any>, previousFocus: any): void {
// List
if (focused instanceof List || focused instanceof PagedList) {
......@@ -107,7 +108,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const focus = list.getFocus() ? list.getFocus()[0] : void 0;
......@@ -144,7 +145,7 @@ export function registerCommands(): void {
const focused = accessor.get(IListService).lastFocusedList;
// List
if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree) {
if (focused instanceof List || focused instanceof PagedList || focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
// Focus down first
......@@ -188,7 +189,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
......@@ -269,7 +270,7 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
if (focused instanceof ObjectTree) {
if (focused instanceof ObjectTree || focused instanceof DataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
......@@ -316,7 +317,7 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
if (focused instanceof ObjectTree) {
if (focused instanceof ObjectTree || focused instanceof DataTree) {
const tree = focused;
const focusedElements = tree.getFocus();
......@@ -373,7 +374,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
......@@ -411,7 +412,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
......@@ -460,7 +461,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const first = list.getFirstElementChild(null);
......@@ -513,7 +514,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const last = list.getLastElementAncestor();
......@@ -555,7 +556,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
list.setSelection(list.getFocus(), fakeKeyboardEvent);
......@@ -600,7 +601,7 @@ export function registerCommands(): void {
// Tree only
if (focused && !(focused instanceof List || focused instanceof PagedList)) {
if (focused instanceof ObjectTree) {
if (focused instanceof ObjectTree || focused instanceof DataTree) {
const tree = focused;
const focus = tree.getFocus();
......@@ -641,7 +642,7 @@ export function registerCommands(): void {
}
// ObjectTree
else if (focused instanceof ObjectTree) {
else if (focused instanceof ObjectTree || focused instanceof DataTree) {
const list = focused;
const fakeKeyboardEvent = new KeyboardEvent('keydown');
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册