Skip to content

Commit

Permalink
Set text/uri-list when dragging editor resources (#181374)
Browse files Browse the repository at this point in the history
Fixes #181367
Fixes #181373

This moves the setting of `text/uri-list` to happen on drag instead of on drop. This allows dropping editors as `text/uri-list` across applications

Note that due to https://bugs.chromium.org/p/chromium/issues/detail?id=239745, we can only set a single uri in the real `text/uri-list` type
  • Loading branch information
mjbvz authored May 3, 2023
1 parent 50dceef commit 63f0154
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 25 deletions.
9 changes: 8 additions & 1 deletion src/vs/base/browser/dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ export const DataTransfers = {
/**
* Typically transfer type for copy/paste transfers.
*/
TEXT: Mimes.text
TEXT: Mimes.text,

/**
* Internal type used to pass around text/uri-list data.
*
* This is needed to work around https://bugs.chromium.org/p/chromium/issues/detail?id=239745.
*/
INTERNAL_URI_LIST: 'application/vnd.code.uri-list',
};

export function applyDragImage(event: DragEvent, label: string | null, clazz: string, backgroundColor?: string | null, foregroundColor?: string | null): void {
Expand Down
38 changes: 23 additions & 15 deletions src/vs/editor/browser/dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { DataTransfers } from 'vs/base/browser/dnd';
import { createFileDataTransferItem, createStringDataTransferItem, IDataTransferItem, UriList, VSDataTransfer } from 'vs/base/common/dataTransfer';
import { Mimes } from 'vs/base/common/mime';
import { URI } from 'vs/base/common/uri';
import { CodeDataTransfers, extractEditorsDropData, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd';
import { CodeDataTransfers, FileAdditionalNativeProperties } from 'vs/platform/dnd/browser/dnd';


export function toVSDataTransfer(dataTransfer: DataTransfer) {
Expand Down Expand Up @@ -38,28 +38,36 @@ const INTERNAL_DND_MIME_TYPES = Object.freeze([
CodeDataTransfers.EDITORS,
CodeDataTransfers.FILES,
DataTransfers.RESOURCES,
DataTransfers.INTERNAL_URI_LIST,
]);

export function addExternalEditorsDropData(dataTransfer: VSDataTransfer, dragEvent: DragEvent, overwriteUriList = false) {
if (dragEvent.dataTransfer && (overwriteUriList || !dataTransfer.has(Mimes.uriList))) {
const editorData = extractEditorsDropData(dragEvent)
.filter(input => input.resource)
.map(input => input.resource!.toString());
export function toExternalVSDataTransfer(sourceDataTransfer: DataTransfer, overwriteUriList = false): VSDataTransfer {
const vsDataTransfer = toVSDataTransfer(sourceDataTransfer);

// Also add in the files
for (const item of dragEvent.dataTransfer?.items) {
const file = item.getAsFile();
if (file) {
editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name);
// Try to expose the internal uri-list type as the standard type
const uriList = vsDataTransfer.get(DataTransfers.INTERNAL_URI_LIST);
if (uriList) {
vsDataTransfer.replace(Mimes.uriList, uriList);
} else {
if (overwriteUriList || !vsDataTransfer.has(Mimes.uriList)) {
// Otherwise, fallback to adding dragged resources to the uri list
const editorData: string[] = [];
for (const item of sourceDataTransfer.items) {
const file = item.getAsFile();
if (file) {
editorData.push((file as FileAdditionalNativeProperties).path ? URI.file((file as FileAdditionalNativeProperties).path!).toString() : file.name);
}
}
}

if (editorData.length) {
dataTransfer.replace(Mimes.uriList, createStringDataTransferItem(UriList.create(editorData)));
if (editorData.length) {
vsDataTransfer.replace(Mimes.uriList, createStringDataTransferItem(UriList.create(editorData)));
}
}
}

for (const internal of INTERNAL_DND_MIME_TYPES) {
dataTransfer.delete(internal);
vsDataTransfer.delete(internal);
}

return vsDataTransfer;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { coalesce } from 'vs/base/common/arrays';
import { CancelablePromise, createCancelablePromise, raceCancellation } from 'vs/base/common/async';
import { VSDataTransfer } from 'vs/base/common/dataTransfer';
import { Disposable } from 'vs/base/common/lifecycle';
import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd';
import { toExternalVSDataTransfer } from 'vs/editor/browser/dnd';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { IPosition } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
Expand Down Expand Up @@ -129,8 +129,7 @@ export class DropIntoEditorController extends Disposable implements IEditorContr
return new VSDataTransfer();
}

const dataTransfer = toVSDataTransfer(dragEvent.dataTransfer);
addExternalEditorsDropData(dataTransfer, dragEvent);
const dataTransfer = toExternalVSDataTransfer(dragEvent.dataTransfer);

if (this.treeItemsTransfer.hasData(DraggedTreeItemsIdentifier.prototype)) {
const data = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype);
Expand Down
27 changes: 26 additions & 1 deletion src/vs/workbench/browser/dnd.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { IWindowOpenable } from 'vs/platform/window/common/window';
import { IWorkspaceContextService, hasWorkspaceFileExtension, isTemporaryWorkspace } from 'vs/platform/workspace/common/workspace';
import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
import { EditorResourceAccessor, GroupIdentifier, IEditorIdentifier, isEditorIdentifier } from 'vs/workbench/common/editor';
import { EditorResourceAccessor, GroupIdentifier, IEditorIdentifier, isEditorIdentifier, isResourceDiffEditorInput, isResourceMergeEditorInput, isResourceSideBySideEditorInput } from 'vs/workbench/common/editor';
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
Expand Down Expand Up @@ -327,6 +327,31 @@ export function fillEditorsDragData(accessor: ServicesAccessor, resourcesOrEdito

if (draggedEditors.length) {
event.dataTransfer.setData(CodeDataTransfers.EDITORS, stringify(draggedEditors));

// Add a URI list entry
const uriListEntries: URI[] = [];
for (const editor of draggedEditors) {
if (editor.resource) {
uriListEntries.push(editor.resource);
} else if (isResourceDiffEditorInput(editor)) {
if (editor.modified.resource) {
uriListEntries.push(editor.modified.resource);
}
} else if (isResourceSideBySideEditorInput(editor)) {
if (editor.primary.resource) {
uriListEntries.push(editor.primary.resource);
}
} else if (isResourceMergeEditorInput(editor)) {
uriListEntries.push(editor.result.resource);
}
}

// Due to https://bugs.chromium.org/p/chromium/issues/detail?id=239745, we can only set
// a single uri for the real `text/uri-list` type. Otherwise all uris end up joined together
// However we write the full uri-list to an internal type so that other parts of VS Code
// can use the full list.
event.dataTransfer.setData(Mimes.uriList, UriList.create(uriListEntries.slice(0, 1)));
event.dataTransfer.setData(DataTransfers.INTERNAL_URI_LIST, UriList.create(uriListEntries));
}
}

Expand Down
8 changes: 3 additions & 5 deletions src/vs/workbench/browser/parts/views/treeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { IHoverService } from 'vs/workbench/services/hover/browser/hover';
import { ITreeViewsService } from 'vs/workbench/services/views/browser/treeViewsService';
import { CodeDataTransfers, LocalSelectionTransfer } from 'vs/platform/dnd/browser/dnd';
import { addExternalEditorsDropData, toVSDataTransfer } from 'vs/editor/browser/dnd';
import { toExternalVSDataTransfer } from 'vs/editor/browser/dnd';
import { CheckboxStateHandler, TreeItemCheckbox } from 'vs/workbench/browser/parts/views/checkbox';
import { setTimeout0 } from 'vs/base/common/platform';
import { AriaRole } from 'vs/base/browser/ui/aria/aria';
Expand Down Expand Up @@ -1623,8 +1623,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
}

onDragOver(data: IDragAndDropData, targetElement: ITreeItem, targetIndex: number, originalEvent: DragEvent): boolean | ITreeDragOverReaction {
const dataTransfer = toVSDataTransfer(originalEvent.dataTransfer!);
addExternalEditorsDropData(dataTransfer, originalEvent);
const dataTransfer = toExternalVSDataTransfer(originalEvent.dataTransfer!);

const types = new Set<string>(Array.from(dataTransfer.entries()).map(x => x[0]));

Expand Down Expand Up @@ -1687,8 +1686,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
willDropUuid = this.treeItemsTransfer.getData(DraggedTreeItemsIdentifier.prototype)![0].identifier;
}

const originalDataTransfer = toVSDataTransfer(originalEvent.dataTransfer);
addExternalEditorsDropData(originalDataTransfer, originalEvent, true);
const originalDataTransfer = toExternalVSDataTransfer(originalEvent.dataTransfer, true);

const outDataTransfer = new VSDataTransfer();
for (const [type, item] of originalDataTransfer.entries()) {
Expand Down

0 comments on commit 63f0154

Please sign in to comment.