|
6 | 6 | import 'vs/css!./media/tree'; |
7 | 7 | import { IDisposable, dispose } from 'vs/base/common/lifecycle'; |
8 | 8 | import { IListOptions, List, IListStyles } from 'vs/base/browser/ui/list/listWidget'; |
9 | | -import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent } from 'vs/base/browser/ui/list/list'; |
| 9 | +import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction } from 'vs/base/browser/ui/list/list'; |
10 | 10 | import { append, $, toggleClass } from 'vs/base/browser/dom'; |
11 | 11 | import { Event, Relay } from 'vs/base/common/event'; |
12 | 12 | import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; |
13 | 13 | import { KeyCode } from 'vs/base/common/keyCodes'; |
14 | | -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop } from 'vs/base/browser/ui/tree/tree'; |
| 14 | +import { ITreeModel, ITreeNode, ITreeRenderer, ITreeEvent, ITreeMouseEvent, ITreeContextMenuEvent, ITreeFilter, ITreeNavigator, ICollapseStateChangeEvent, ITreeDragAndDrop, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree'; |
15 | 15 | import { ISpliceable } from 'vs/base/common/sequence'; |
| 16 | +import { IDragAndDropData } from 'vs/base/browser/dnd'; |
| 17 | +import { range } from 'vs/base/common/arrays'; |
16 | 18 |
|
17 | | -function asListOptions<T, TFilterData>(options?: IAbstractTreeOptions<T, TFilterData>): IListOptions<ITreeNode<T, TFilterData>> | undefined { |
| 19 | +class TreeNodeListDragAndDrop<T, TFilterData, TRef> implements IListDragAndDrop<ITreeNode<T, TFilterData>> { |
| 20 | + |
| 21 | + constructor(private modelProvider: () => ITreeModel<T, TFilterData, TRef>, private dnd: ITreeDragAndDrop<T>) { } |
| 22 | + |
| 23 | + getDragURI(node: ITreeNode<T, TFilterData>): string | null { |
| 24 | + return this.dnd.getDragURI(node.element); |
| 25 | + } |
| 26 | + |
| 27 | + getDragLabel(nodes: ITreeNode<T, TFilterData>[]): string | undefined { |
| 28 | + return this.dnd.getDragLabel && this.dnd.getDragLabel(nodes.map(node => node.element)); |
| 29 | + } |
| 30 | + |
| 31 | + onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { |
| 32 | + this.dnd.onDragStart(data, originalEvent); |
| 33 | + } |
| 34 | + |
| 35 | + onDragOver(data: IDragAndDropData, targetNode: ITreeNode<T, TFilterData> | undefined, targetIndex: number | undefined, originalEvent: DragEvent, force = false): boolean | IListDragOverReaction { |
| 36 | + const result = this.dnd.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); |
| 37 | + |
| 38 | + if (typeof targetNode === 'undefined') { |
| 39 | + return result; |
| 40 | + } |
| 41 | + |
| 42 | + if (typeof result === 'boolean' || !result.accept || typeof result.bubble === 'undefined') { |
| 43 | + if (force) { |
| 44 | + const accept = typeof result === 'boolean' ? result : result.accept; |
| 45 | + const effect = typeof result === 'boolean' ? undefined : result.effect; |
| 46 | + return { accept, effect, feedback: [targetIndex!] }; |
| 47 | + } |
| 48 | + |
| 49 | + return result; |
| 50 | + } |
| 51 | + |
| 52 | + if (result.bubble === TreeDragOverBubble.Up) { |
| 53 | + const parentNode = targetNode.parent; |
| 54 | + const model = this.modelProvider(); |
| 55 | + const parentIndex = parentNode && model.getListIndex(model.getNodeLocation(parentNode)); |
| 56 | + |
| 57 | + return this.onDragOver(data, parentNode, parentIndex, originalEvent, true); |
| 58 | + } |
| 59 | + |
| 60 | + const model = this.modelProvider(); |
| 61 | + const ref = model.getNodeLocation(targetNode); |
| 62 | + const start = model.getListIndex(ref); |
| 63 | + const length = model.getListRenderCount(ref); |
| 64 | + |
| 65 | + return { ...result, feedback: range(start, start + length) }; |
| 66 | + } |
| 67 | + |
| 68 | + drop(data: IDragAndDropData, targetNode: ITreeNode<T, TFilterData> | undefined, targetIndex: number | undefined, originalEvent: DragEvent): void { |
| 69 | + this.dnd.drop(data, targetNode && targetNode.element, targetIndex, originalEvent); |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +function asListOptions<T, TFilterData, TRef>(modelProvider: () => ITreeModel<T, TFilterData, TRef>, options?: IAbstractTreeOptions<T, TFilterData>): IListOptions<ITreeNode<T, TFilterData>> | undefined { |
18 | 74 | return options && { |
19 | 75 | ...options, |
20 | 76 | identityProvider: options.identityProvider && { |
21 | 77 | getId(el) { |
22 | 78 | return options.identityProvider!.getId(el.element); |
23 | 79 | } |
24 | 80 | }, |
25 | | - dnd: options.dnd && { |
26 | | - getDragURI(node) { |
27 | | - return options.dnd!.getDragURI(node.element); |
28 | | - }, |
29 | | - getDragLabel: options.dnd!.getDragLabel && ((nodes) => { |
30 | | - return options.dnd!.getDragLabel!(nodes.map(node => node.element)); |
31 | | - }), |
32 | | - onDragStart(data, originalEvent) { |
33 | | - return options.dnd!.onDragStart(data, originalEvent); |
34 | | - }, |
35 | | - onDragOver(data, targetNode, targetIndex, originalEvent) { |
36 | | - return options.dnd!.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent); |
37 | | - }, |
38 | | - drop(data, targetNode, targetIndex, originalEvent) { |
39 | | - return options.dnd!.drop(data, targetNode && targetNode.element, targetIndex, originalEvent); |
40 | | - } |
41 | | - }, |
| 81 | + dnd: options.dnd && new TreeNodeListDragAndDrop(modelProvider, options.dnd), |
42 | 82 | multipleSelectionController: options.multipleSelectionController && { |
43 | 83 | isSelectionSingleChangeEvent(e) { |
44 | 84 | return options.multipleSelectionController!.isSelectionSingleChangeEvent({ ...e, element: e.element } as any); |
@@ -239,7 +279,7 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable |
239 | 279 | const treeRenderers = renderers.map(r => new TreeRenderer<T, TFilterData, any>(r, onDidChangeCollapseStateRelay.event)); |
240 | 280 | this.disposables.push(...treeRenderers); |
241 | 281 |
|
242 | | - this.view = new List(container, treeDelegate, treeRenderers, asListOptions(options)); |
| 282 | + this.view = new List(container, treeDelegate, treeRenderers, asListOptions(() => this.model, options)); |
243 | 283 |
|
244 | 284 | this.model = this.createModel(this.view, options); |
245 | 285 | onDidChangeCollapseStateRelay.input = this.model.onDidChangeCollapseState; |
|
0 commit comments