mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-11 18:48:33 -05:00
Integrate drag and drop api (#16500)
* Initial implementation of drag and drop api (#122239) * Add drag and drop controller (#123542) * Tree data transfer dnd (#128666) * add drop method to sql files Co-authored-by: Alex Ross <alros@microsoft.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem } from 'vs/workbench/common/views';
|
||||
import { ITreeViewDataProvider, ITreeItem, IViewsService, ITreeView, IViewsRegistry, ITreeViewDescriptor, IRevealOptions, Extensions, ResolvableTreeItem, ITreeViewDragAndDropController, ITreeDataTransfer } from 'vs/workbench/common/views';
|
||||
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -14,6 +14,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConnectionTreeService } from 'sql/workbench/services/connection/common/connectionTreeService';
|
||||
import { TreeDataTransferConverter } from 'vs/workbench/api/common/shared/treeDataTransfer';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadTreeViews)
|
||||
export class MainThreadTreeViews extends Disposable implements MainThreadTreeViewsShape {
|
||||
@@ -33,18 +34,20 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
|
||||
}
|
||||
|
||||
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean }): Promise<void> {
|
||||
async $registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, canDragAndDrop: 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.canDragAndDrop ? new TreeViewDragAndDropController(treeViewId, 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.
|
||||
// Set all other properties first!
|
||||
viewer.showCollapseAllAction = !!options.showCollapseAll;
|
||||
viewer.canSelectMany = !!options.canSelectMany;
|
||||
viewer.dragAndDropController = dndController;
|
||||
viewer.dataProvider = dataProvider;
|
||||
this.registerListeners(treeViewId, viewer);
|
||||
this._proxy.$setVisible(treeViewId, viewer.visible);
|
||||
@@ -171,6 +174,16 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
||||
// {{SQL CARBON EDIT}}
|
||||
export type TreeItemHandle = string;
|
||||
|
||||
class TreeViewDragAndDropController implements ITreeViewDragAndDropController {
|
||||
|
||||
constructor(private readonly treeViewId: string,
|
||||
private readonly _proxy: ExtHostTreeViewsShape) { }
|
||||
|
||||
async onDrop(dataTransfer: ITreeDataTransfer, targetTreeItem: ITreeItem): Promise<void> {
|
||||
return this._proxy.$onDrop(this.treeViewId, await TreeDataTransferConverter.toTreeDataTransferDTO(dataTransfer), targetTreeItem.handle);
|
||||
}
|
||||
}
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
export class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
|
||||
@@ -195,7 +208,7 @@ export class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||
}));
|
||||
}
|
||||
|
||||
getItemsToRefresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem; }): ITreeItem[] {
|
||||
getItemsToRefresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeItem }): ITreeItem[] {
|
||||
const itemsToRefresh: ITreeItem[] = [];
|
||||
if (itemsToRefreshByHandle) {
|
||||
for (const treeItemHandle of Object.keys(itemsToRefreshByHandle)) {
|
||||
|
||||
@@ -37,6 +37,7 @@ import * as statusbar from 'vs/workbench/services/statusbar/common/statusbar';
|
||||
import { ClassifiedEvent, GDPRClassification, StrictPropertyCheck } from 'vs/platform/telemetry/common/gdprTypings';
|
||||
import { ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { ThemeColor } from 'vs/platform/theme/common/themeService';
|
||||
import { TreeDataTransferDTO } from 'vs/workbench/api/common/shared/treeDataTransfer';
|
||||
import * as tasks from 'vs/workbench/api/common/shared/tasks';
|
||||
import { IRevealOptions, ITreeItem } from 'vs/workbench/common/views';
|
||||
import { IAdapterDescriptor, IConfig, IDebugSessionReplMode } from 'vs/workbench/contrib/debug/common/debug';
|
||||
@@ -293,7 +294,7 @@ export interface MainThreadTextEditorsShape extends IDisposable {
|
||||
}
|
||||
|
||||
export interface MainThreadTreeViewsShape extends IDisposable {
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean; }): Promise<void>;
|
||||
$registerTreeViewDataProvider(treeViewId: string, options: { showCollapseAll: boolean, canSelectMany: boolean, canDragAndDrop: 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;
|
||||
@@ -1234,6 +1235,7 @@ export interface ExtHostDocumentsAndEditorsShape {
|
||||
export interface ExtHostTreeViewsShape {
|
||||
// {{SQL CARBON EDIT}}
|
||||
$getChildren(treeViewId: string, treeItemHandle?: string): Promise<sqlITreeItem[]>;
|
||||
$onDrop(treeViewId: string, treeDataTransfer: TreeDataTransferDTO, newParentTreeItemHandle: string): Promise<void>;
|
||||
$setExpanded(treeViewId: string, treeItemHandle: string, expanded: boolean): void;
|
||||
$setSelection(treeViewId: string, treeItemHandles: string[]): void;
|
||||
$setVisible(treeViewId: string, visible: boolean): void;
|
||||
|
||||
@@ -10,7 +10,7 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ExtHostTreeViewsShape, MainThreadTreeViewsShape } from './extHost.protocol';
|
||||
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions } from 'vs/workbench/common/views';
|
||||
import { ITreeItem, TreeViewItemHandleArg, ITreeItemLabel, IRevealOptions, TREE_ITEM_DATA_TRANSFER_TYPE } from 'vs/workbench/common/views';
|
||||
import { ExtHostCommands, CommandsConverter } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { TreeItemCollapsibleState, ThemeIcon, MarkdownString as MarkdownStringType } from 'vs/workbench/api/common/extHostTypes';
|
||||
@@ -22,6 +22,7 @@ import { MarkdownString } from 'vs/workbench/api/common/extHostTypeConverters';
|
||||
import { IMarkdownString } from 'vs/base/common/htmlContent';
|
||||
import { CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { Command } from 'vs/editor/common/modes';
|
||||
import { TreeDataTransferConverter, TreeDataTransferDTO } from 'vs/workbench/api/common/shared/treeDataTransfer';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import * as azdata from 'azdata';
|
||||
@@ -86,7 +87,8 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
if (!options || !options.treeDataProvider) {
|
||||
throw new Error('Options with treeDataProvider is mandatory');
|
||||
}
|
||||
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
|
||||
const canDragAndDrop = options.dragAndDropController !== undefined;
|
||||
const registerPromise = this._proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, canDragAndDrop: canDragAndDrop });
|
||||
const treeView = this.createExtHostTreeView(viewId, options, extension);
|
||||
return {
|
||||
get onDidCollapseElement() { return treeView.onDidCollapseElement; },
|
||||
@@ -129,6 +131,27 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||
return treeView.getChildren(treeItemHandle);
|
||||
}
|
||||
|
||||
async $onDrop(treeViewId: string, treeDataTransferDTO: TreeDataTransferDTO, newParentItemHandle: string): Promise<void> {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
return Promise.reject(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
|
||||
}
|
||||
|
||||
const treeDataTransfer = TreeDataTransferConverter.toITreeDataTransfer(treeDataTransferDTO);
|
||||
if (treeDataTransfer.items.has(TREE_ITEM_DATA_TRANSFER_TYPE)) {
|
||||
const sourceHandles: string[] = JSON.parse(await treeDataTransfer.items.get(TREE_ITEM_DATA_TRANSFER_TYPE)!.asString());
|
||||
const sourceElements = sourceHandles.map(handle => treeView.getExtensionElement(handle)).filter(element => !!element);
|
||||
if (sourceElements.length > 0) {
|
||||
treeDataTransfer.items.set(TREE_ITEM_DATA_TRANSFER_TYPE, {
|
||||
asString: async () => JSON.stringify(sourceElements)
|
||||
});
|
||||
} else {
|
||||
treeDataTransfer.items.delete(TREE_ITEM_DATA_TRANSFER_TYPE);
|
||||
}
|
||||
}
|
||||
return treeView.onDrop(treeDataTransfer, newParentItemHandle);
|
||||
}
|
||||
|
||||
async $hasResolve(treeViewId: string): Promise<boolean> {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
@@ -199,6 +222,7 @@ export class ExtHostTreeView<T> extends Disposable {
|
||||
private static readonly ID_HANDLE_PREFIX = '1';
|
||||
|
||||
private readonly dataProvider: vscode.TreeDataProvider<T>;
|
||||
private readonly dndController: vscode.DragAndDropController<T> | undefined;
|
||||
|
||||
private roots: TreeNode[] | null = null;
|
||||
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
|
||||
@@ -246,9 +270,11 @@ export class ExtHostTreeView<T> extends Disposable {
|
||||
}
|
||||
}
|
||||
this.dataProvider = options.treeDataProvider;
|
||||
this.dndController = options.dragAndDropController;
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
if (this.proxy) {
|
||||
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany });
|
||||
this.proxy.$registerTreeViewDataProvider(viewId, { showCollapseAll: !!options.showCollapseAll, canSelectMany: !!options.canSelectMany, canDragAndDrop: options.dragAndDropController !== undefined });
|
||||
}
|
||||
if (this.dataProvider.onDidChangeTreeData) {
|
||||
this._register(this.dataProvider.onDidChangeTreeData(element => this._onDidChangeData.fire({ message: false, element })));
|
||||
@@ -377,6 +403,14 @@ export class ExtHostTreeView<T> extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
async onDrop(treeDataTransfer: vscode.TreeDataTransfer, targetHandleOrNode: TreeItemHandle): Promise<void> {
|
||||
const target = this.getExtensionElement(targetHandleOrNode);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
return asPromise(() => this.dndController?.onDrop(treeDataTransfer, target));
|
||||
}
|
||||
|
||||
get hasResolve(): boolean {
|
||||
return !!this.dataProvider.resolveTreeItem;
|
||||
}
|
||||
|
||||
44
src/vs/workbench/api/common/shared/treeDataTransfer.ts
Normal file
44
src/vs/workbench/api/common/shared/treeDataTransfer.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITreeDataTransfer, ITreeDataTransferItem } from 'vs/workbench/common/views';
|
||||
|
||||
interface TreeDataTransferItemDTO {
|
||||
asString: string;
|
||||
}
|
||||
|
||||
export interface TreeDataTransferDTO {
|
||||
types: string[];
|
||||
items: TreeDataTransferItemDTO[];
|
||||
}
|
||||
|
||||
export namespace TreeDataTransferConverter {
|
||||
export function toITreeDataTransfer(value: TreeDataTransferDTO): ITreeDataTransfer {
|
||||
const newDataTransfer: ITreeDataTransfer = {
|
||||
items: new Map<string, ITreeDataTransferItem>()
|
||||
};
|
||||
value.types.forEach((type, index) => {
|
||||
newDataTransfer.items.set(type, {
|
||||
asString: async () => value.items[index].asString
|
||||
});
|
||||
});
|
||||
return newDataTransfer;
|
||||
}
|
||||
|
||||
export async function toTreeDataTransferDTO(value: ITreeDataTransfer): Promise<TreeDataTransferDTO> {
|
||||
const newDTO: TreeDataTransferDTO = {
|
||||
types: [],
|
||||
items: []
|
||||
};
|
||||
const entries = Array.from(value.items.entries());
|
||||
for (const entry of entries) {
|
||||
newDTO.types.push(entry[0]);
|
||||
newDTO.items.push({
|
||||
asString: await entry[1].asString()
|
||||
});
|
||||
}
|
||||
return newDTO;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user