Tree dnd feedback

This commit is contained in:
Alex Ross 2022-02-11 13:35:40 +01:00
parent 8a3b1f4c4c
commit a64e8e5673
No known key found for this signature in database
GPG key ID: 89DDDBA66CBA7840
8 changed files with 24 additions and 19 deletions

View file

@ -11,6 +11,7 @@ export namespace Mimes {
export const unknown = 'application/unknown';
export const markdown = 'text/markdown';
export const latex = 'text/latex';
export const uriList = 'text/uri-list';
}
interface MapExtToMediaMimes {

View file

@ -33,14 +33,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[] | undefined; dragMimeTypes: string[] | undefined; hasHandleDrag: boolean }): Promise<void> {
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: 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.dropMimeTypes
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes ?? [], options.hasHandleDrag, this._proxy) : undefined;
? new TreeViewDragAndDropController(treeViewId, options.dropMimeTypes, options.dragMimeTypes, options.hasHandleDrag, 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.

View file

@ -257,7 +257,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[] | undefined; dragMimeTypes: string[] | undefined; hasHandleDrag: boolean }): Promise<void>;
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean; canSelectMany: boolean; dropMimeTypes: string[]; dragMimeTypes: string[]; hasHandleDrag: 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;

View file

@ -87,8 +87,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
if (!options || !options.treeDataProvider) {
throw new Error('Options with treeDataProvider is mandatory');
}
const dropMimeTypes = options.dragAndDropController?.dropMimeTypes;
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes;
const dropMimeTypes = options.dragAndDropController?.dropMimeTypes ?? [];
const dragMimeTypes = options.dragAndDropController?.dragMimeTypes ?? [];
const hasHandleDrag = !!options.dragAndDropController?.handleDrag;
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, dropMimeTypes, dragMimeTypes, hasHandleDrag: hasHandleDrag });
const treeView = this.createExtHostTreeView(viewId, options, extension);
@ -457,7 +457,7 @@ class ExtHostTreeView<T> extends Disposable {
if (!target) {
return;
}
return asPromise(() => this.dndController?.handleDrop(treeDataTransfer, target, token));
return asPromise(() => this.dndController?.handleDrop(target, treeDataTransfer, token));
}
get hasResolve(): boolean {

View file

@ -2351,7 +2351,7 @@ export enum TreeItemCollapsibleState {
@es5ClassCompat
export class TreeDataTransferItem {
async asString(): Promise<string> {
return JSON.stringify(this.value);
return typeof this.value === 'string' ? this.value : JSON.stringify(this.value);
}
constructor(public readonly value: any) { }

View file

@ -157,12 +157,13 @@ function createDraggedEditorInputFromRawResourcesData(rawResourcesData: string |
export async function extractTreeDropData(dataTransfer: ITreeDataTransfer): Promise<Array<IDraggedResourceEditorInput>> {
const editors: IDraggedResourceEditorInput[] = [];
const resourcesKey = DataTransfers.RESOURCES.toLowerCase();
const resourcesKey = Mimes.uriList.toLowerCase();
// Data Transfer: Resources
if (dataTransfer.has(resourcesKey)) {
try {
const rawResourcesData = await dataTransfer.get(resourcesKey)?.asString();
const asString = await dataTransfer.get(resourcesKey)?.asString();
const rawResourcesData = JSON.stringify(asString?.split('\\n').filter(value => !value.startsWith('#')));
editors.push(...createDraggedEditorInputFromRawResourcesData(rawResourcesData));
} catch (error) {
// Invalid transfer

View file

@ -39,7 +39,7 @@ import { isString } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, TreeDragOverBubble } from 'vs/base/browser/ui/tree/tree';
import { IDragAndDropData } from 'vs/base/browser/dnd';
import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
@ -62,6 +62,7 @@ import { Schemas } from 'vs/base/common/network';
import { ITreeViewsDragAndDropService } from 'vs/workbench/services/views/common/treeViewsDragAndDropService';
import { generateUuid } from 'vs/base/common/uuid';
import { ILogService } from 'vs/platform/log/common/log';
import { Mimes } from 'vs/base/common/mime';
export class TreeViewPane extends ViewPane {
@ -1266,7 +1267,7 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
@IInstantiationService private readonly instantiationService: IInstantiationService,
@ITreeViewsDragAndDropService private readonly treeViewsDragAndDropService: ITreeViewsDragAndDropService<ITreeDataTransfer>,
@ILogService private readonly logService: ILogService) {
this.treeMimeType = `tree/${treeId.toLowerCase()}`;
this.treeMimeType = `application/vnd.code.tree.${treeId.toLowerCase()}`;
}
private dndController: ITreeViewDragAndDropController | undefined;
@ -1283,7 +1284,11 @@ export class CustomTreeViewDragAndDrop implements ITreeDragAndDrop<ITreeItem> {
this.dragCancellationToken = new CancellationTokenSource();
this.treeViewsDragAndDropService.addDragOperationTransfer(uuid, this.dndController.handleDrag(itemHandles, uuid, this.dragCancellationToken.token));
originalEvent.dataTransfer.setData(TREE_DRAG_UUID_MIME, uuid);
this.treeItemsTransfer.setData([new DraggedTreeItemsIdentifier(uuid)], DraggedTreeItemsIdentifier.prototype);
if (this.dndController.dragMimeTypes.find((element) => element === Mimes.uriList)) {
this.treeItemsTransfer.setData([new DraggedTreeItemsIdentifier(uuid)], DraggedTreeItemsIdentifier.prototype);
// Add the type that the editor knows
originalEvent.dataTransfer?.setData(DataTransfers.RESOURCES, '');
}
this.dndController.dragMimeTypes.forEach(supportedType => {
originalEvent.dataTransfer?.setData(supportedType, '');
});

View file

@ -68,7 +68,6 @@ declare module 'vscode' {
/**
* Provides support for drag and drop in `TreeView`.
*/
// todo@API formalize mime types, either `text/uri-list` and or `application/vnd.code.XYZ` (see NotebookOutputItem)
export interface TreeDragAndDropController<T> {
/**
@ -76,7 +75,7 @@ declare module 'vscode' {
* This could be well-defined, existing, mime types, and also mime types defined by the extension.
*
* Each tree will automatically support drops from it's own `DragAndDropController`. To support drops from other trees,
* you will need to add the mime type of that tree. The mime type of a tree is of the format `tree/treeidlowercase`.
* you will need to add the mime type of that tree. The mime type of a tree is of the format `application/vnd.code.tree.treeidlowercase`.
*
* To learn the mime type of a dragged item:
* 1. Set up your `DragAndDropController`
@ -98,8 +97,8 @@ declare module 'vscode' {
* When the items are dropped on **another tree item** in **the same tree**, your `TreeDataTransferItem` objects
* will be preserved. See the documentation for `TreeDataTransferItem` for how best to take advantage of this.
*
* To add a data transfer item that can be dragged into the editor, use the application specific mime type "resourceurls".
* The data for "resourceurls" should be an array of `toString()`ed Uris. To specify a cursor position in the file,
* To add a data transfer item that can be dragged into the editor, use the application specific mime type "text/uri-list".
* The data for "text/uri-list" should be a string with `toString()`ed Uris separated by newlines. To specify a cursor position in the file,
* set the Uri's fragment to `L3,5`, where 3 is the line number and 5 is the column number.
*
* @param source The source items for the drag and drop operation.
@ -115,9 +114,8 @@ declare module 'vscode' {
*
* @param source The data transfer items of the source of the drag.
* @param target The target tree element that the drop is occurring on.
* @param token TODO @alexr00: When would this operation actually be cancelled?
* @param token A cancellation token indicating that the drop has been cancelled.
*/
// TODO@API align order of TreeDataTransfer and T with handleDrag
handleDrop(source: TreeDataTransfer, target: T, token: CancellationToken): Thenable<void> | void;
handleDrop(target: T, source: TreeDataTransfer, token: CancellationToken): Thenable<void> | void;
}
}