Skip to content

Commit

Permalink
Enable file DataTransfer on tree views (#150328)
Browse files Browse the repository at this point in the history
  • Loading branch information
mjbvz committed May 25, 2022
1 parent 61861d7 commit d891b49
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 44 deletions.
5 changes: 3 additions & 2 deletions src/vs/workbench/api/browser/mainThreadTreeViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}

async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise<void> {
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise<void> {
this.logService.trace('MainThreadTreeViews#$registerTreeViewDataProvider', treeViewId, options);

this.extensionService.whenInstalledExtensionsRegistered().then(() => {
const dataProvider = new TreeViewDataProvider(treeViewId, this._proxy, this.notificationService);
this._dataProviders.set(treeViewId, dataProvider);
const dndController = (options.hasHandleDrag || options.hasHandleDrop)
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, this._proxy) : undefined;
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, options.supportsFileDataTransfers, this._proxy) : undefined;
const viewer = this.getTreeView(treeViewId);
if (viewer) {
// Order is important here. The internal tree isn't created until the dataProvider is set.
Expand Down Expand Up @@ -201,6 +201,7 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
readonly dropMimeTypes: string[],
readonly dragMimeTypes: string[],
readonly hasWillDrop: boolean,
readonly supportsFileDataTransfers: boolean,
private readonly _proxy: ExtHostTreeViewsShape) { }

async handleDrop(dataTransfer: VSDataTransfer, targetTreeItem: ITreeItem | undefined, token: CancellationToken,
Expand Down
2 changes: 1 addition & 1 deletion src/vs/workbench/api/common/extHost.protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
}

export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean }): Promise<void>;
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: readonly string[]; dragMimeTypes: readonly string[]; hasHandleDrag: boolean; hasHandleDrop: boolean; supportsFileDataTransfers: boolean }): Promise<void>;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): Promise<void>;
$reveal(treeViewId: string, itemInfo: { item: ITreeItem; parentChain: ITreeItem[] } | undefined, options: IRevealOptions): Promise<void>;
$setMessage(treeViewId: string, message: string): void;
Expand Down
5 changes: 3 additions & 2 deletions src/vs/workbench/api/common/extHostTreeViews.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { Command } from 'vs/editor/common/languages';
import { ITreeViewsService, TreeviewsService } from 'vs/workbench/services/views/common/treeViewsService';
import { checkProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';

type TreeItemHandle = string;

Expand Down Expand Up @@ -92,7 +92,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? [];
const hasHandleDrag = !!options.dragAndDropController?.handleDrag;
const hasHandleDrop = !!options.dragAndDropController?.handleDrop;
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop });
const supportsFileDataTransfers = isProposedApiEnabled(extension, 'dataTransferFiles');
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag, hasHandleDrop, supportsFileDataTransfers });
const treeView = this.createExtHostTreeView(viewId, options, extension);
return {
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ suite('MainThreadHostTreeView', function () {
}
drain(): any { return null; }
}, new TestViewsService(), new TestNotificationService(), testExtensionService, new NullLogService());
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false });
mainThreadTreeViews.$registerTreeViewDataProvider(testTreeViewId, { showCollapseAll: false, canSelectMany: false, dropMimeTypes: [], dragMimeTypes: [], hasHandleDrag: false, hasHandleDrop: false, supportsFileDataTransfers: false });
await testExtensionService.whenInstalledExtensionsRegistered();
});

Expand Down
64 changes: 26 additions & 38 deletions src/vs/workbench/browser/parts/views/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
import { ThemeSettings } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService';
import { CodeDataTransfers } from 'vs/platform/dnd/browser/dnd';
import { CodeDataTransfers, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd';

export class TreeViewPane extends ViewPane {

Expand Down Expand Up @@ -1503,40 +1503,21 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
}
const treeDataTransfer = new VSDataTransfer();
const uris: URI[] = [];
let itemsCount = Array.from(originalEvent.dataTransfer.items).reduce((previous, current) => {
if ((current.kind === 'string') || (current.kind === 'file')) {
return previous + 1;
}
return previous;
}, 0);

let treeSourceInfo: TreeDragSourceInfo | undefined;
let willDropUuid: string | undefined;
if (this.treeItemsTransfer.hasData(DraggedTreeItemsIdentifier.prototype)) {
willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier;
}
await new Promise<void>(resolve => {
function decrementStringCount() {
itemsCount--;
if (itemsCount === 0) {
// Check if there are uris to add and add them
if (uris.length) {
treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n'));
}
resolve();
}
}

if (!originalEvent.dataTransfer) {
return;
}
for (const dataItem of originalEvent.dataTransfer.items) {
const type = dataItem.type;
const kind = dataItem.kind;
const convertedType = this.convertKnownMimes(type, kind).type;
if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0)
&& (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) {
if (dataItem.kind === 'string') {
await Promise.all([...originalEvent.dataTransfer.items].map(async dataItem => {
const type = dataItem.type;
const kind = dataItem.kind;
const convertedType = this.convertKnownMimes(type, kind).type;
if ((INTERNAL_MIME_TYPES.indexOf(convertedType) < 0)
&& (convertedType === this.treeMimeType) || (dndController.dropMimeTypes.indexOf(convertedType) >= 0)) {
if (dataItem.kind === 'string') {
await new Promise<void>(resolve =>
dataItem.getAsString(dataValue => {
if (convertedType === this.treeMimeType) {
treeSourceInfo = JSON.parse(dataValue);
Expand All @@ -1545,20 +1526,27 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
const converted = this.convertKnownMimes(type, kind, dataValue);
treeDataTransfer.setString(converted.type, converted.value + '');
}
decrementStringCount();
});
} else if (dataItem.kind === 'file') {
const dataValue = dataItem.getAsFile();
if (dataValue) {
uris.push(URI.file(dataValue.path));
resolve();
}));
} else if (dataItem.kind === 'file') {
const file = dataItem.getAsFile();
if (file) {
uris.push(URI.file(file.path));
const uri = (file as FileAdditionalNativeProperties).path ? URI.parse((file as FileAdditionalNativeProperties).path!) : undefined;
if (dndController.supportsFileDataTransfers) {
treeDataTransfer.setFile(type, file.name, uri, async () => {
return new Uint8Array(await file.arrayBuffer());
});
}
decrementStringCount();
}
} else if (dataItem.kind === 'string' || dataItem.kind === 'file') {
decrementStringCount();
}
}
});
}));

// Check if there are uris to add and add them
if (uris.length) {
treeDataTransfer.setString(Mimes.uriList, uris.map(uri => uri.toString()).join('\n'));
}

const additionalWillDropPromise = this.treeViewsDragAndDropService.removeDragOperationTransfer(willDropUuid);
if (!additionalWillDropPromise) {
Expand Down
1 change: 1 addition & 0 deletions src/vs/workbench/common/views.ts
Original file line number Diff line number Diff line change
Expand Up @@ -831,6 +831,7 @@ export interface ITreeViewDataProvider {
export interface ITreeViewDragAndDropController {
readonly dropMimeTypes: string[];
readonly dragMimeTypes: string[];
readonly supportsFileDataTransfers: boolean;
handleDrag(sourceTreeItemHandles: string[], operationUuid: string, token: CancellationToken): Promise<VSDataTransfer | undefined>;
handleDrop(elements: VSDataTransfer, target: ITreeItem | undefined, token: CancellationToken, operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise<void>;
}
Expand Down

0 comments on commit d891b49

Please sign in to comment.