diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index eed797a7705c8ed6e5f469157d422397d5234d49..ca65d7cec4efa1e2ba6ad24b41d9bc4ae3aa5f5f 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -10,7 +10,10 @@ import { TreeModel, ITreeNode, ITreeElement } from 'vs/base/browser/ui/tree/tree import { IIterator, empty } from 'vs/base/common/iterator'; import { IDelegate, IRenderer, IListMouseEvent } from 'vs/base/browser/ui/list/list'; import { append, $ } from 'vs/base/browser/dom'; -import { Event, Relay } from 'vs/base/common/event'; +import { Event, Relay, chain } from 'vs/base/common/event'; +import { domEvent } from 'vs/base/browser/event'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; function toTreeListOptions(options?: IListOptions): IListOptions> { if (!options) { @@ -127,6 +130,10 @@ function getLocation(node: ITreeNode): number[] { return location.reverse(); } +function isInputElement(e: HTMLElement): boolean { + return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA'; +} + export class Tree implements IDisposable { private view: List>; @@ -152,6 +159,14 @@ export class Tree implements IDisposable { onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; this.view.onMouseClick(this.onMouseClick, this, this.disposables); + + const onKeyDown = chain(this.view.onKeyDown) + .filter(e => !isInputElement(e.target as HTMLElement)) + .map(e => new StandardKeyboardEvent(e)); + + onKeyDown.filter(e => e.keyCode === KeyCode.LeftArrow).on(this.onLeftArrow, this, this.disposables); + onKeyDown.filter(e => e.keyCode === KeyCode.RightArrow).on(this.onRightArrow, this, this.disposables); + onKeyDown.filter(e => e.keyCode === KeyCode.Space).on(this.onSpace, this, this.disposables); } splice(location: number[], deleteCount: number, toInsert: IIterator> = empty()): IIterator> { @@ -161,6 +176,57 @@ export class Tree implements IDisposable { private onMouseClick(e: IListMouseEvent>): void { const node = e.element; const location = getLocation(node); + + this.model.toggleCollapsed(location); + } + + private onLeftArrow(e: StandardKeyboardEvent): void { + e.preventDefault(); + e.stopPropagation(); + + const nodes = this.view.getFocusedElements(); + + if (nodes.length === 0) { + return; + } + + const node = nodes[0]; + const location = getLocation(node); + const didCollapse = this.model.setCollapsed(location, true); + + if (!didCollapse) { + console.log('should focus parent'); + // this.view.setFocus([]); + } + } + + private onRightArrow(e: StandardKeyboardEvent): void { + e.preventDefault(); + e.stopPropagation(); + + const nodes = this.view.getFocusedElements(); + + if (nodes.length === 0) { + return; + } + + const node = nodes[0]; + const location = getLocation(node); + this.model.setCollapsed(location, false); + } + + private onSpace(e: StandardKeyboardEvent): void { + e.preventDefault(); + e.stopPropagation(); + + const nodes = this.view.getFocusedElements(); + + if (nodes.length === 0) { + return; + } + + const node = nodes[0]; + const location = getLocation(node); this.model.toggleCollapsed(location); } diff --git a/src/vs/base/browser/ui/tree/treeModel.ts b/src/vs/base/browser/ui/tree/treeModel.ts index 9e9f103e4ea99cb5178b6b84efc467ce7a211887..7acf8a90a4bba288afc53c2e91476871bfc2b958 100644 --- a/src/vs/base/browser/ui/tree/treeModel.ts +++ b/src/vs/base/browser/ui/tree/treeModel.ts @@ -120,15 +120,15 @@ export class TreeModel { return map(iter(deletedNodes), treeNodeToElement); } - setCollapsed(location: number[], collapsed: boolean): void { - this._setCollapsed(location, collapsed); + setCollapsed(location: number[], collapsed: boolean): boolean { + return this._setCollapsed(location, collapsed); } toggleCollapsed(location: number[]): void { this._setCollapsed(location); } - private _setCollapsed(location: number[], collapsed?: boolean | undefined): void { + private _setCollapsed(location: number[], collapsed?: boolean | undefined): boolean { const { node, listIndex, visible } = this.findNode(location); if (typeof collapsed === 'undefined') { @@ -136,7 +136,7 @@ export class TreeModel { } if (node.collapsed === collapsed) { - return; + return false; } node.collapsed = collapsed; @@ -165,6 +165,8 @@ export class TreeModel { this._onDidChangeCollapseState.fire(node); } + + return true; } isCollapsed(location: number[]): boolean {