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

tree: refactor click handling for expansion

上级 cf72cc41
......@@ -502,33 +502,29 @@ const DefaultOpenController: IOpenController = {
}
};
class MouseController<T> implements IDisposable {
export class MouseController<T> implements IDisposable {
private multipleSelectionSupport: boolean;
readonly multipleSelectionController: IMultipleSelectionController<T>;
private openController: IOpenController;
private disposables: IDisposable[] = [];
constructor(
private list: List<T>,
private view: ListView<T>,
options: IListOptions<T> = {}
) {
this.multipleSelectionSupport = !(options.multipleSelectionSupport === false);
constructor(protected list: List<T>) {
this.multipleSelectionSupport = !(list.options.multipleSelectionSupport === false);
if (this.multipleSelectionSupport) {
this.multipleSelectionController = options.multipleSelectionController || DefaultMultipleSelectionContoller;
this.multipleSelectionController = list.options.multipleSelectionController || DefaultMultipleSelectionContoller;
}
this.openController = options.openController || DefaultOpenController;
this.openController = list.options.openController || DefaultOpenController;
view.onMouseDown(this.onMouseDown, this, this.disposables);
view.onContextMenu(this.onContextMenu, this, this.disposables);
view.onMouseClick(this.onPointer, this, this.disposables);
view.onMouseDblClick(this.onDoubleClick, this, this.disposables);
view.onTouchStart(this.onMouseDown, this, this.disposables);
view.onTap(this.onPointer, this, this.disposables);
Gesture.addTarget(view.domNode);
list.onMouseDown(this.onMouseDown, this, this.disposables);
list.onContextMenu(this.onContextMenu, this, this.disposables);
list.onMouseClick(this.onPointer, this, this.disposables);
list.onMouseDblClick(this.onDoubleClick, this, this.disposables);
list.onTouchStart(this.onMouseDown, this, this.disposables);
list.onTap(this.onPointer, this, this.disposables);
Gesture.addTarget(list.getHTMLElement());
}
private isSelectionSingleChangeEvent(event: IListMouseEvent<any> | IListTouchEvent<any>): boolean {
......@@ -553,16 +549,16 @@ class MouseController<T> implements IDisposable {
private onMouseDown(e: IListMouseEvent<T> | IListTouchEvent<T>): void {
if (document.activeElement !== e.browserEvent.target) {
this.view.domNode.focus();
this.list.domFocus();
}
}
private onContextMenu(e: IListMouseEvent<T> | IListTouchEvent<T>): void {
private onContextMenu(e: IListContextMenuEvent<T>): void {
const focus = typeof e.index === 'undefined' ? [] : [e.index];
this.list.setFocus(focus, e.browserEvent);
}
private onPointer(e: IListMouseEvent<T>): void {
protected onPointer(e: IListMouseEvent<T>): void {
let reference = this.list.getFocus()[0];
const selection = this.list.getSelection();
reference = reference === undefined ? selection[0] : reference;
......@@ -1213,7 +1209,7 @@ export class List<T> implements ISpliceable<T>, IDisposable {
}
if (typeof _options.mouseSupport === 'boolean' ? _options.mouseSupport : true) {
this.mouseController = new MouseController(this, this.view, _options);
this.mouseController = this.createMouseController(_options);
this.disposables.push(this.mouseController);
}
......@@ -1227,6 +1223,10 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.style(_options);
}
protected createMouseController(options: IListOptions<T>): MouseController<T> {
return new MouseController(this);
}
updateOptions(optionsUpdate: IListOptionsUpdate = {}): void {
this._options = { ...this._options, ...optionsUpdate };
this._onDidUpdateOptions.fire(this._options);
......
......@@ -5,7 +5,7 @@
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, mightProducePrintableCharacter } from 'vs/base/browser/ui/list/listWidget';
import { IListOptions, List, IListStyles, mightProducePrintableCharacter, MouseController } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
......@@ -761,11 +761,47 @@ class Trait<T> {
}
}
export class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<ITreeNode<T, TFilterData>> {
constructor(list: TreeNodeList<T, TFilterData, TRef>, private tree: AbstractTree<T, TFilterData, TRef>) {
super(list);
}
protected onPointer(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const node = e.element;
if (!node) {
super.onPointer(e);
return;
}
if (this.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.multipleSelectionController.isSelectionSingleChangeEvent(e)) {
super.onPointer(e);
return;
}
if (!this.tree.options.openOnSingleClick && e.browserEvent.detail !== 2) {
super.onPointer(e);
return;
}
const model = ((this.tree as any).model as ITreeModel<T, TFilterData, TRef>); // internal
const location = model.getNodeLocation(node);
const recursive = e.browserEvent.altKey;
model.setCollapsed(location, undefined, recursive);
super.onPointer(e);
}
}
interface ITreeNodeListOptions<T, TFilterData, TRef> extends IListOptions<ITreeNode<T, TFilterData>> {
readonly tree: AbstractTree<T, TFilterData, TRef>;
}
/**
* We use this List subclass to restore selection and focus as nodes
* get rendered in the list, possibly due to a node expand() call.
*/
class TreeNodeList<T, TFilterData> extends List<ITreeNode<T, TFilterData>> {
class TreeNodeList<T, TFilterData, TRef> extends List<ITreeNode<T, TFilterData>> {
constructor(
container: HTMLElement,
......@@ -773,11 +809,15 @@ class TreeNodeList<T, TFilterData> extends List<ITreeNode<T, TFilterData>> {
renderers: IListRenderer<any /* TODO@joao */, any>[],
private focusTrait: Trait<T>,
private selectionTrait: Trait<T>,
options?: IListOptions<ITreeNode<T, TFilterData>>
options: ITreeNodeListOptions<T, TFilterData, TRef>
) {
super(container, virtualDelegate, renderers, options);
}
protected createMouseController(options: ITreeNodeListOptions<T, TFilterData, TRef>): MouseController<ITreeNode<T, TFilterData>> {
return new TreeNodeListMouseController(this, options.tree);
}
splice(start: number, deleteCount: number, elements: ITreeNode<T, TFilterData>[] = []): void {
super.splice(start, deleteCount, elements);
......@@ -826,7 +866,7 @@ class TreeNodeList<T, TFilterData> extends List<ITreeNode<T, TFilterData>> {
export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable {
private view: TreeNodeList<T, TFilterData>;
private view: TreeNodeList<T, TFilterData, TRef>;
private renderers: TreeRenderer<T, TFilterData, any>[];
private focusNavigationFilter: ((node: ITreeNode<T, TFilterData>) => boolean) | undefined;
protected model: ITreeModel<T, TFilterData, TRef>;
......@@ -884,7 +924,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.disposables.push(filter);
}
this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, asListOptions(() => this.model, _options));
this.view = new TreeNodeList(container, treeDelegate, this.renderers, this.focus, this.selection, { ...asListOptions(() => this.model, _options), tree: this });
this.model = this.createModel(this.view, _options);
onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState;
......@@ -920,9 +960,6 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
}, null, this.disposables);
}
this.view.onTap(this.reactOnMouseClick, this, this.disposables);
this.view.onMouseClick(this.reactOnMouseClick, this, this.disposables);
if (_options.keyboardSupport !== false) {
const onKeyDown = Event.chain(this.view.onKeyDown)
.filter(e => !isInputElement(e.target as HTMLElement))
......@@ -1164,27 +1201,6 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
return this.view.length;
}
private reactOnMouseClick(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
const node = e.element;
if (!node) {
return;
}
if (this.view.multipleSelectionController.isSelectionRangeChangeEvent(e) || this.view.multipleSelectionController.isSelectionSingleChangeEvent(e)) {
return;
}
if (!this.openOnSingleClick && e.browserEvent.detail !== 2) {
return;
}
const location = this.model.getNodeLocation(node);
const recursive = e.browserEvent.altKey;
this.model.setCollapsed(location, undefined, recursive);
}
private onLeftArrow(e: StandardKeyboardEvent): void {
e.preventDefault();
e.stopPropagation();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册