提交 2b2cdae1 编写于 作者: J Joao Moreno

list: drag and drop almost working

上级 e87692c6
......@@ -7,7 +7,6 @@ import { GestureEvent } from 'vs/base/browser/touch';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
export interface IListVirtualDelegate<T> {
getHeight(element: T): number;
......@@ -82,6 +81,7 @@ export const enum DragOverEffect {
export interface IDragOverReaction {
accept: boolean;
effect?: DragOverEffect;
feedback?: number[]; // use -1 for entire list
// bubble?: DragOverBubble;
// autoExpand?: boolean;
}
......@@ -96,16 +96,16 @@ export const DragOverReactions = {
};
export interface IDragAndDropData {
update(event: DragMouseEvent): void;
update(dataTransfer: DataTransfer): void;
getData(): any;
}
export interface IDragAndDrop<T> {
getDragURI(element: T): string | null;
getDragLabel?(elements: T[]): string;
onDragStart(data: IDragAndDropData, originalEvent: DragMouseEvent): void;
onDragOver(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): boolean | IDragOverReaction;
drop(data: IDragAndDropData, targetElement: T, originalEvent: DragMouseEvent): void;
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void;
onDragOver(data: IDragAndDropData, targetElement: T | undefined, targetIndex: number | undefined, originalEvent: DragEvent): boolean | IDragOverReaction;
drop(data: IDragAndDropData, targetElement: T | undefined, originalEvent: DragEvent): void;
}
/**
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { getOrDefault } from 'vs/base/common/objects';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose, Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Gesture, EventType as TouchEventType, GestureEvent } from 'vs/base/browser/touch';
import * as DOM from 'vs/base/browser/dom';
import { Event, Emitter } from 'vs/base/common/event';
......@@ -12,14 +12,15 @@ import { domEvent } from 'vs/base/browser/event';
import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement';
import { ScrollEvent, ScrollbarVisibility, INewScrollDimensions } from 'vs/base/common/scrollable';
import { RangeMap, shift } from './rangeMap';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IDragAndDrop, IListDragEvent } from './list';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListTouchEvent, IListGestureEvent, IListDragEvent, IDragAndDrop, IDragAndDropData } from './list';
import { RowCache, IRow } from './rowCache';
import { isWindows } from 'vs/base/common/platform';
import * as browser from 'vs/base/browser/browser';
import { ISpliceable } from 'vs/base/common/sequence';
import { memoize } from 'vs/base/common/decorators';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { Range, IRange } from 'vs/base/common/range';
import { equals, distinct } from 'vs/base/common/arrays';
import { DataTransfers } from 'vs/base/browser/dnd';
function canUseTranslate3d(): boolean {
if (browser.isFirefox) {
......@@ -42,11 +43,16 @@ interface IItem<T> {
hasDynamicHeight: boolean;
renderWidth: number | undefined;
uri: string | undefined;
dropTarget: boolean;
dragStartDisposable: IDisposable;
}
export interface IListViewDragAndDrop<T> extends IDragAndDrop<T> {
getDragElements(element: T): T[];
}
export interface IListViewOptions<T> {
readonly dnd?: IDragAndDrop<T>;
readonly dnd?: IListViewDragAndDrop<T>;
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
readonly setRowLineHeight?: boolean;
......@@ -60,6 +66,7 @@ const DefaultOptions = {
setRowLineHeight: true,
supportDynamicHeights: false,
dnd: {
getDragElements(e) { return [e]; },
getDragURI() { return null; },
onDragStart(): void { },
onDragOver() { return false; },
......@@ -67,8 +74,88 @@ const DefaultOptions = {
}
};
export class ElementsDragAndDropData<T> implements IDragAndDropData {
private elements: T[];
constructor(elements: T[]) {
this.elements = elements;
}
public update(dataTransfer: DataTransfer): void {
// no-op
}
public getData(): any {
return this.elements;
}
}
export class ExternalElementsDragAndDropData<T> implements IDragAndDropData {
private elements: T[];
constructor(elements: T[]) {
this.elements = elements;
}
public update(dataTransfer: DataTransfer): void {
// no-op
}
public getData(): any {
return this.elements;
}
}
export class DesktopDragAndDropData implements IDragAndDropData {
private types: any[];
private files: any[];
constructor() {
this.types = [];
this.files = [];
}
public update(dataTransfer: DataTransfer): void {
if (dataTransfer.types) {
this.types = [...dataTransfer.types];
}
if (dataTransfer.files) {
this.files = [];
for (let i = 0; i < dataTransfer.files.length; i++) {
const file = dataTransfer.files.item(i);
if (file && (file.size || file.type)) {
this.files.push(file);
}
}
}
}
public getData(): any {
return {
types: this.types,
files: this.files
};
}
}
function equalsDragFeedback(f1: number[] | undefined, f2: number[] | undefined): boolean {
if (Array.isArray(f1) && Array.isArray(f2)) {
return equals(f1, f2!);
}
return f1 === f2;
}
export class ListView<T> implements ISpliceable<T>, IDisposable {
private static currentExternalDragAndDropData: IDragAndDropData | undefined;
readonly domNode: HTMLElement;
private items: IItem<T>[];
......@@ -90,18 +177,26 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
private dragOverMouseY: number;
private setRowLineHeight: boolean;
private supportDynamicHeights: boolean;
private readonly dnd: IDragAndDrop<T>;
private dnd: IListViewDragAndDrop<T>;
private currentDragData: IDragAndDropData | undefined;
private currentDragFeedback: number[] | undefined;
private currentDragFeedbackDisposable: IDisposable = Disposable.None;
private onDragLeaveTimeout: IDisposable = Disposable.None;
private disposables: IDisposable[];
private _onDidChangeContentHeight = new Emitter<number>();
readonly onDidChangeContentHeight: Event<number> = Event.latch(this._onDidChangeContentHeight.event);
get contentHeight(): number { return this.rangeMap.size; }
private _onDragStart = new Emitter<{ element: T, uri: string, event: DragMouseEvent }>();
readonly onDragStart = this._onDragStart.event;
// private _onDragStart = new Emitter<{ element: T, uri: string, event: DragEvent }>();
// readonly onDragStart = this._onDragStart.event;
readonly onDragOver: Event<IListDragEvent<T>>;
readonly onDragLeave: Event<void>;
// readonly onDragOver: Event<IListDragEvent<T>>;
// readonly onDragLeave: Event<void>;
// readonly onDrop: Event<IListDragEvent<T>>;
// readonly onDragEnd: Event<void>;
constructor(
container: HTMLElement,
......@@ -150,18 +245,14 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
domEvent(this.scrollableElement.getDomNode(), 'scroll')
(e => (e.target as HTMLElement).scrollTop = 0, null, this.disposables);
const onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => new DragMouseEvent(e));
onDragOver(this.onDidDragOver, this, this.disposables);
this.onDragOver = Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e));
this.onDragLeave = Event.signal(domEvent(this.domNode, 'dragleave'));
const onDragEnd = Event.map(domEvent(document, 'dragend'), e => new DragMouseEvent(e));
onDragEnd(this._onDragEnd, this, this.disposables);
Event.map(domEvent(this.domNode, 'dragover'), e => this.toDragEvent(e))(this.onDragOver, this, this.disposables);
Event.map(domEvent(this.domNode, 'drop'), e => this.toDragEvent(e))(this.onDrop, this, this.disposables);
domEvent(this.domNode, 'dragleave')(this.onDragLeave, this, this.disposables);
domEvent(window, 'dragend')(this.onDragEnd, this, this.disposables);
this.setRowLineHeight = getOrDefault(options, o => o.setRowLineHeight, DefaultOptions.setRowLineHeight);
this.supportDynamicHeights = getOrDefault(options, o => o.supportDynamicHeights, DefaultOptions.supportDynamicHeights);
this.dnd = getOrDefault<IListViewOptions<T>, IDragAndDrop<T>>(options, o => o.dnd, DefaultOptions.dnd);
this.dnd = getOrDefault<IListViewOptions<T>, IListViewDragAndDrop<T>>(options, o => o.dnd, DefaultOptions.dnd);
this.layout();
}
......@@ -203,6 +294,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
renderWidth: undefined,
row: null,
uri: undefined,
dropTarget: false,
dragStartDisposable: Disposable.None
}));
......@@ -385,8 +477,8 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
if (uri) {
item.row.domNode!.draggable = true;
const onDragStart = Event.map(domEvent(item.row.domNode!, 'dragstart'), e => new DragMouseEvent(e));
item.dragStartDisposable = onDragStart(event => this._onDragStart.fire({ element: item.element, uri, event }), this);
const onDragStart = domEvent(item.row.domNode!, 'dragstart');
item.dragStartDisposable = onDragStart(event => this.onDragStart(item.element, uri, event));
}
}
......@@ -402,6 +494,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
item.row!.domNode!.setAttribute('data-last-element', index === this.length - 1 ? 'true' : 'false');
item.row!.domNode!.setAttribute('aria-setsize', `${this.length}`);
item.row!.domNode!.setAttribute('aria-posinset', `${index + 1}`);
DOM.toggleClass(item.row!.domNode!, 'drop-target', item.dropTarget);
}
private removeItemFromDOM(index: number): void {
......@@ -509,7 +602,154 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
// DND
private onDidDragOver(event: DragMouseEvent): void {
private onDragStart(element: T, uri: string, event: DragEvent): void {
if (!event.dataTransfer) {
return;
}
const elements = this.dnd.getDragElements(element);
event.dataTransfer.effectAllowed = 'copyMove';
event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri]));
if (event.dataTransfer.setDragImage) {
let label: string;
if (this.dnd.getDragLabel) {
label = this.dnd.getDragLabel(elements);
} else {
label = String(elements.length);
}
const dragImage = DOM.$('.monaco-list-drag-image');
dragImage.textContent = label;
document.body.appendChild(dragImage);
event.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
}
this.currentDragData = new ElementsDragAndDropData(elements);
ListView.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements);
this.dnd.onDragStart(this.currentDragData, event);
}
private onDragOver(event: IListDragEvent<T>): boolean {
console.log('DRAG OVER');
this.onDragLeaveTimeout.dispose();
this.setupDragAndDropScrollTopAnimation(event.browserEvent);
if (!event.browserEvent.dataTransfer) {
return false;
}
// Drag over from outside
if (!this.currentDragData) {
if (ListView.currentExternalDragAndDropData) {
// Drag over from another list
this.currentDragData = ListView.currentExternalDragAndDropData;
} else {
// Drag over from the desktop
if (!event.browserEvent.dataTransfer.types) {
return false;
}
this.currentDragData = new DesktopDragAndDropData();
}
}
const result = this.dnd.onDragOver(this.currentDragData, event.element, event.index, event.browserEvent);
const canDrop = typeof result === 'boolean' ? result : result.accept;
// const reaction = (typeof result !== 'boolean' && result.effect === DragOverEffect.Copy) ? 'copy' : 'move';
if (!canDrop) {
return false;
}
let feedback: number[];
if (typeof result !== 'boolean' && result.feedback) {
feedback = result.feedback;
} else {
if (typeof event.index === 'undefined') {
feedback = [-1];
} else {
feedback = [event.index];
}
}
// sanitize feedback list
feedback = distinct(feedback).filter(i => i >= -1 && i < this.length).sort();
feedback = feedback[0] === -1 ? [-1] : feedback;
if (feedback.length === 0) {
throw new Error('Invalid empty feedback list');
}
if (equalsDragFeedback(this.currentDragFeedback, feedback)) {
return true;
}
console.log(this.currentDragFeedback, feedback);
this.currentDragFeedback = feedback;
this.currentDragFeedbackDisposable.dispose();
if (feedback[0] === -1) { // entire list feedback
DOM.addClass(this.domNode, 'drop-target');
this.currentDragFeedbackDisposable = toDisposable(() => DOM.removeClass(this.domNode, 'drop-target'));
} else {
for (const index of feedback) {
const item = this.items[index]!;
item.dropTarget = true;
if (item.row && item.row.domNode) {
DOM.addClass(item.row.domNode, 'drop-target');
}
}
this.currentDragFeedbackDisposable = toDisposable(() => {
for (const index of feedback) {
const item = this.items[index]!;
item.dropTarget = false;
if (item.row && item.row.domNode) {
DOM.removeClass(item.row.domNode, 'drop-target');
}
}
});
}
return true;
}
private onDragLeave(event: DragEvent): void {
console.log('LEAVE');
this.onDragLeaveTimeout = DOM.timeout(() => this.clearDragOverFeedback(), 100);
}
private onDrop(e: IListDragEvent<T>): void {
console.log('DROP');
this.teardownDragAndDropScrollTopAnimation();
this.clearDragOverFeedback();
}
private onDragEnd(): void {
console.log('DRAG END');
this.teardownDragAndDropScrollTopAnimation();
this.clearDragOverFeedback();
}
private clearDragOverFeedback(): void {
this.currentDragFeedback = undefined;
this.currentDragFeedbackDisposable.dispose();
}
// DND scroll top animation
private setupDragAndDropScrollTopAnimation(event: DragEvent): void {
if (!this.dragOverAnimationDisposable) {
const viewTop = DOM.getTopLeftOffset(this.domNode).top;
this.dragOverAnimationDisposable = DOM.animate(this.animateDragAndDropScrollTop.bind(this, viewTop));
......@@ -517,11 +757,13 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.dragOverAnimationStopDisposable.dispose();
this.dragOverAnimationStopDisposable = DOM.timeout(() => {
this.dragOverAnimationDisposable.dispose();
this.dragOverAnimationDisposable = undefined;
if (this.dragOverAnimationDisposable) {
this.dragOverAnimationDisposable.dispose();
this.dragOverAnimationDisposable = undefined;
}
}, 1000);
this.dragOverMouseY = event.posy;
this.dragOverMouseY = event.pageY;
}
private animateDragAndDropScrollTop(viewTop: number): void {
......@@ -539,7 +781,7 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
}
private _onDragEnd(): void {
private teardownDragAndDropScrollTopAnimation(): void {
this.dragOverAnimationStopDisposable.dispose();
if (this.dragOverAnimationDisposable) {
......
......@@ -16,17 +16,15 @@ import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent, IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Event, Emitter, EventBufferer } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IDragAndDrop, IDragAndDropData, IListDragEvent } from './list';
import { IListVirtualDelegate, IListRenderer, IListEvent, IListContextMenuEvent, IListMouseEvent, IListTouchEvent, IListGestureEvent, IIdentityProvider, IKeyboardNavigationLabelProvider, IDragAndDrop } from './list';
import { ListView, IListViewOptions } from './listView';
import { Color } from 'vs/base/common/color';
import { mixin, getOrDefault } from 'vs/base/common/objects';
import { mixin } from 'vs/base/common/objects';
import { ScrollbarVisibility } from 'vs/base/common/scrollable';
import { ISpliceable } from 'vs/base/common/sequence';
import { CombinedSpliceable } from 'vs/base/browser/ui/list/splice';
import { clamp } from 'vs/base/common/numbers';
import { matchesPrefix } from 'vs/base/common/filters';
import { DragMouseEvent } from 'vs/base/browser/mouseEvent';
import { DataTransfers } from 'vs/base/browser/dnd';
interface ITraitChangeEvent {
indexes: number[];
......@@ -214,7 +212,7 @@ class TraitSpliceable<T> implements ISpliceable<T> {
splice(start: number, deleteCount: number, elements: T[]): void {
if (!this.identityProvider) {
return this.trait.splice(start, deleteCount, elements.map(e => false));
return this.trait.splice(start, deleteCount, elements.map(() => false));
}
const pastElementsWithTrait = this.trait.get().map(i => this.identityProvider!.getId(this.view.element(i)).toString());
......@@ -714,13 +712,12 @@ export class DefaultStyleController implements IStyleController {
content.push(`.monaco-list${suffix} .monaco-list-row:hover { outline: 1px dashed ${styles.listHoverOutline}; outline-offset: -1px; }`);
}
// TODO
// if (styles.listDropBackground) {
// content.push(`
// .monaco-tree${suffix} .monaco-tree-wrapper.drop-target,
// .monaco-tree${suffix} .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; }
// `);
// }
if (styles.listDropBackground) {
content.push(`
.monaco-list${suffix}.drop-target,
.monaco-list${suffix} .monaco-list-row.drop-target { background-color: ${styles.listDropBackground} !important; color: inherit !important; }
`);
}
const newStyles = content.join('\n');
if (newStyles !== this.styleElement.innerHTML) {
......@@ -729,19 +726,24 @@ export class DefaultStyleController implements IStyleController {
}
}
export interface IListOptions<T> extends IListViewOptions<T>, IListStyles {
identityProvider?: IIdentityProvider<T>;
dnd?: IDragAndDrop<T>;
keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
ariaLabel?: string;
mouseSupport?: boolean;
keyboardSupport?: boolean;
verticalScrollMode?: ScrollbarVisibility;
multipleSelectionSupport?: boolean;
multipleSelectionController?: IMultipleSelectionController<T>;
openController?: IOpenController;
styleController?: IStyleController;
accessibilityProvider?: IAccessibilityProvider<T>;
export interface IListOptions<T> extends IListStyles {
readonly identityProvider?: IIdentityProvider<T>;
readonly dnd?: IDragAndDrop<T>;
readonly keyboardNavigationLabelProvider?: IKeyboardNavigationLabelProvider<T>;
readonly ariaLabel?: string;
readonly keyboardSupport?: boolean;
readonly multipleSelectionSupport?: boolean;
readonly multipleSelectionController?: IMultipleSelectionController<T>;
readonly openController?: IOpenController;
readonly styleController?: IStyleController;
readonly accessibilityProvider?: IAccessibilityProvider<T>;
// list view options
readonly useShadows?: boolean;
readonly verticalScrollMode?: ScrollbarVisibility;
readonly setRowLineHeight?: boolean;
readonly supportDynamicHeights?: boolean;
readonly mouseSupport?: boolean;
}
export interface IListStyles {
......@@ -774,7 +776,7 @@ const defaultStyles: IListStyles = {
listDropBackground: Color.fromHex('#383B3D')
};
const DefaultOptions: IListOptions<any> = {
const DefaultOptions = {
keyboardSupport: true,
mouseSupport: true,
multipleSelectionSupport: true,
......@@ -938,73 +940,8 @@ class AccessibiltyRenderer<T> implements IListRenderer<T, HTMLElement> {
}
}
export class ElementsDragAndDropData<T> implements IDragAndDropData {
private elements: T[];
constructor(elements: T[]) {
this.elements = elements;
}
public update(event: DragMouseEvent): void {
// no-op
}
public getData(): any {
return this.elements;
}
}
export class ExternalElementsDragAndDropData<T> implements IDragAndDropData {
private elements: T[];
constructor(elements: T[]) {
this.elements = elements;
}
public update(event: DragMouseEvent): void {
// no-op
}
public getData(): any {
return this.elements;
}
}
export class DesktopDragAndDropData implements IDragAndDropData {
private types: any[];
private files: any[];
constructor() {
this.types = [];
this.files = [];
}
public update(event: DragMouseEvent): void {
if (event.dataTransfer.types) {
this.types = [...event.dataTransfer.types];
}
if (event.dataTransfer.files) {
this.files = [...event.dataTransfer.files];
this.files = this.files.filter(f => f.size || f.type);
}
}
public getData(): any {
return {
types: this.types,
files: this.files
};
}
}
export class List<T> implements ISpliceable<T>, IDisposable {
private static currentExternalDragAndDropData: IDragAndDropData | undefined;
private static InstanceCount = 0;
private idPrefix = `list_id_${++List.InstanceCount}`;
......@@ -1016,9 +953,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
private styleElement: HTMLStyleElement;
private styleController: IStyleController;
private readonly dnd: IDragAndDrop<T>;
private currentDragAndDropData: IDragAndDropData | undefined;
protected disposables: IDisposable[];
@memoize get onFocusChange(): Event<IListEvent<T>> {
......@@ -1104,7 +1038,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
) {
this.focus = new FocusTrait(i => this.getElementDomId(i));
this.selection = new Trait('selected');
this.dnd = getOrDefault<IListOptions<T>, IDragAndDrop<T>>(options, o => o.dnd, DefaultOptions.dnd);
mixin(options, defaultStyles, false);
......@@ -1116,7 +1049,20 @@ export class List<T> implements ISpliceable<T>, IDisposable {
renderers = renderers.map(r => new PipelineRenderer(r.templateId, [...baseRenderers, r]));
this.view = new ListView(container, virtualDelegate, renderers, options);
const that = this;
const viewOptions: IListViewOptions<T> = {
...options,
dnd: options.dnd && {
...options.dnd,
getDragElements(element) {
const selection = that.getSelectedElements();
const elements = selection.indexOf(element) > -1 ? selection : [element];
return elements;
}
}
};
this.view = new ListView(container, virtualDelegate, renderers, viewOptions);
this.view.domNode.setAttribute('role', 'tree');
DOM.addClass(this.view.domNode, this.idPrefix);
this.view.domNode.tabIndex = 0;
......@@ -1152,11 +1098,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.disposables.push(new MouseController(this, this.view, options));
}
this.view.onDragStart(this.onDragStart, this, this.disposables);
this.view.onDragOver(this.onDragOver, this, this.disposables);
this.view.onDragLeave(this.onDragLeave, this, this.disposables);
this.onFocusChange(this._onFocusChange, this, this.disposables);
this.onSelectionChange(this._onSelectionChange, this, this.disposables);
......@@ -1443,45 +1384,6 @@ export class List<T> implements ISpliceable<T>, IDisposable {
DOM.toggleClass(this.view.domNode, 'selection-multiple', selection.length > 1);
}
// DND
private onDragStart({ element, uri, event }: { element: T, uri: string, event: DragMouseEvent }): void {
const selection = this.getSelectedElements();
const elements = selection.indexOf(element) > -1 ? selection : [element];
event.dataTransfer.effectAllowed = 'copyMove';
event.dataTransfer.setData(DataTransfers.RESOURCES, JSON.stringify([uri]));
if (event.dataTransfer.setDragImage) {
let label: string;
if (this.dnd.getDragLabel) {
label = this.dnd.getDragLabel(elements);
} else {
label = String(elements.length);
}
const dragImage = DOM.$('.monaco-list-drag-image');
dragImage.textContent = label;
document.body.appendChild(dragImage);
event.dataTransfer.setDragImage(dragImage, -10, -10);
setTimeout(() => document.body.removeChild(dragImage), 0);
}
this.currentDragAndDropData = new ElementsDragAndDropData(elements);
List.currentExternalDragAndDropData = new ExternalElementsDragAndDropData(elements);
this.dnd.onDragStart(this.currentDragAndDropData, event);
}
private onDragOver(event: IListDragEvent<T>): void {
console.log(event);
}
private onDragLeave(): void {
console.log('LEAVE');
}
dispose(): void {
this._onDidDispose.fire();
this.disposables = dispose(this.disposables);
......
......@@ -32,11 +32,11 @@ function asListOptions<T, TFilterData>(options?: IAbstractTreeOptions<T, TFilter
onDragStart(data, originalEvent) {
return options.dnd!.onDragStart(data, originalEvent);
},
onDragOver(data, targetNode, originalEvent) {
return options.dnd!.onDragOver(data, targetNode.element, originalEvent);
onDragOver(data, targetNode, targetIndex, originalEvent) {
return options.dnd!.onDragOver(data, targetNode && targetNode.element, targetIndex, originalEvent);
},
drop(data, targetNode, originalEvent) {
return options.dnd!.drop(data, targetNode.element, originalEvent);
return options.dnd!.drop(data, targetNode && targetNode.element, originalEvent);
}
},
multipleSelectionController: options.multipleSelectionController && {
......
......@@ -140,11 +140,11 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
onDragStart(data, originalEvent) {
return options.dnd!.onDragStart(data, originalEvent);
},
onDragOver(data, targetNode, originalEvent) {
return options.dnd!.onDragOver(data, targetNode.element as T, originalEvent);
onDragOver(data, targetNode, targetIndex, originalEvent) {
return options.dnd!.onDragOver(data, targetNode && targetNode.element as T, targetIndex, originalEvent);
},
drop(data, targetNode, originalEvent) {
return options.dnd!.drop(data, targetNode.element as T, originalEvent);
return options.dnd!.drop(data, targetNode && targetNode.element as T, originalEvent);
}
},
multipleSelectionController: options.multipleSelectionController && {
......
......@@ -162,22 +162,24 @@ class WorkbenchOpenController implements IOpenController {
}
function toWorkbenchListOptions<T>(options: IListOptions<T>, configurationService: IConfigurationService, keybindingService: IKeybindingService): IListOptions<T> {
const result = { ...options };
if (options.multipleSelectionSupport !== false && !options.multipleSelectionController) {
options.multipleSelectionController = new MultipleSelectionController(configurationService);
result.multipleSelectionController = new MultipleSelectionController(configurationService);
}
options.openController = new WorkbenchOpenController(configurationService, options.openController);
result.openController = new WorkbenchOpenController(configurationService, options.openController);
if (options.keyboardNavigationLabelProvider) {
const tlp = options.keyboardNavigationLabelProvider;
options.keyboardNavigationLabelProvider = {
result.keyboardNavigationLabelProvider = {
getKeyboardNavigationLabel(e) { return tlp.getKeyboardNavigationLabel(e); },
mightProducePrintableCharacter(e) { return keybindingService.mightProducePrintableCharacter(e); }
};
}
return options;
return result;
}
let sharedListStyleSheet: HTMLStyleElement;
......
......@@ -878,6 +878,7 @@ export class RepositoryPanel extends ViewletPanel {
new ResourceRenderer(this.listLabels, actionItemProvider, () => this.getSelectedResources(), this.themeService, this.menus)
];
const that = this;
this.list = this.instantiationService.createInstance(WorkbenchList, this.listContainer, delegate, renderers, {
identityProvider: scmResourceIdentityProvider,
keyboardNavigationLabelProvider: scmKeyboardNavigationLabelProvider,
......@@ -885,7 +886,9 @@ export class RepositoryPanel extends ViewletPanel {
getDragURI(element) { return 'file:///foo'; },
// getDragLabel(elements) { return 'dragging'; },
onDragStart(data, originalEvent) { },
onDragOver(data, targetElement, originalEvent) { return true; },
onDragOver(data, targetElement, targetIndex, originalEvent) {
return { accept: true, feedback: typeof targetIndex === 'undefined' ? undefined : [targetIndex - 1, targetIndex, targetIndex + 1].filter(i => i >= 0 && i < that.list.length) };
},
drop(data, targetElement, originalEvent) { }
}
}) as WorkbenchList<ISCMResourceGroup | ISCMResource>;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册