mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-17 01:25:36 -05:00
Feature/tree component (#2077)
*added tree component to the model builder
This commit is contained in:
@@ -13,14 +13,18 @@ import * as nls from 'vs/nls';
|
||||
import * as vscode from 'vscode';
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType} from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape, ExtHostModelViewTreeViewsShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
|
||||
class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
private nextComponentId: number;
|
||||
private readonly _componentBuilders = new Map<string, ComponentBuilderImpl<any>>();
|
||||
|
||||
constructor(private readonly _proxy: MainThreadModelViewShape, private readonly _handle: number) {
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadModelViewShape,
|
||||
private readonly _handle: number,
|
||||
private readonly _mainContext: IMainContext,
|
||||
private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape) {
|
||||
this.nextComponentId = 0;
|
||||
}
|
||||
|
||||
@@ -66,6 +70,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||
return builder;
|
||||
}
|
||||
|
||||
tree<T>(): sqlops.ComponentBuilder<sqlops.TreeComponent<T>> {
|
||||
let id = this.getNextComponentId();
|
||||
let builder: ComponentBuilderImpl<sqlops.TreeComponent<T>> = this.getComponentBuilder(new TreeComponentWrapper(this._extHostModelViewTree, this._proxy, this._handle, id), id);
|
||||
this._componentBuilders.set(id, builder);
|
||||
return builder;
|
||||
}
|
||||
|
||||
inputBox(): sqlops.ComponentBuilder<sqlops.InputBoxComponent> {
|
||||
let id = this.getNextComponentId();
|
||||
let builder: ComponentBuilderImpl<sqlops.InputBoxComponent> = this.getComponentBuilder(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
||||
@@ -500,6 +511,11 @@ class ComponentWrapper implements sqlops.Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected setDataProvider(): Thenable<void> {
|
||||
return this._proxy.$setDataProvider(this._handle, this._id);
|
||||
}
|
||||
|
||||
protected async setProperty(key: string, value: any): Promise<void> {
|
||||
if (!this.properties[key] || this.properties[key] !== value) {
|
||||
// Only notify the front end if a value has been updated
|
||||
@@ -1038,6 +1054,28 @@ class FileBrowserTreeComponentWrapper extends ComponentWrapper implements sqlops
|
||||
}
|
||||
}
|
||||
|
||||
class TreeComponentWrapper<T> extends ComponentWrapper implements sqlops.TreeComponent<T> {
|
||||
|
||||
constructor(
|
||||
private _extHostModelViewTree: ExtHostModelViewTreeViewsShape,
|
||||
proxy: MainThreadModelViewShape, handle: number, id: string) {
|
||||
super(proxy, handle, ModelComponentTypes.TreeComponent, id);
|
||||
this.properties = {};
|
||||
}
|
||||
|
||||
public registerDataProvider<T>(dataProvider: sqlops.TreeComponentDataProvider<T>): vscode.TreeView<T> {
|
||||
this.setDataProvider();
|
||||
return this._extHostModelViewTree.$createTreeView(this._handle, this.id, { treeDataProvider: dataProvider });
|
||||
}
|
||||
|
||||
public get withCheckbox(): boolean {
|
||||
return this.properties['withCheckbox'];
|
||||
}
|
||||
public set withCheckbox(v: boolean) {
|
||||
this.setProperty('withCheckbox', v);
|
||||
}
|
||||
}
|
||||
|
||||
class ModelViewImpl implements sqlops.ModelView {
|
||||
|
||||
public onClosedEmitter = new Emitter<any>();
|
||||
@@ -1051,9 +1089,11 @@ class ModelViewImpl implements sqlops.ModelView {
|
||||
private readonly _proxy: MainThreadModelViewShape,
|
||||
private readonly _handle: number,
|
||||
private readonly _connection: sqlops.connection.Connection,
|
||||
private readonly _serverInfo: sqlops.ServerInfo
|
||||
private readonly _serverInfo: sqlops.ServerInfo,
|
||||
private readonly mainContext: IMainContext,
|
||||
private readonly _extHostModelViewTree: ExtHostModelViewTreeViewsShape
|
||||
) {
|
||||
this._modelBuilder = new ModelBuilderImpl(this._proxy, this._handle);
|
||||
this._modelBuilder = new ModelBuilderImpl(this._proxy, this._handle, this.mainContext, this._extHostModelViewTree);
|
||||
}
|
||||
|
||||
public get onClosed(): vscode.Event<any> {
|
||||
@@ -1106,9 +1146,10 @@ export class ExtHostModelView implements ExtHostModelViewShape {
|
||||
private readonly _handlers = new Map<string, (view: sqlops.ModelView) => void>();
|
||||
|
||||
constructor(
|
||||
mainContext: IMainContext
|
||||
private _mainContext: IMainContext,
|
||||
private _extHostModelViewTree: ExtHostModelViewTreeViewsShape
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(SqlMainContext.MainThreadModelView);
|
||||
this._proxy = _mainContext.getProxy(SqlMainContext.MainThreadModelView);
|
||||
}
|
||||
|
||||
$onClosed(handle: number): void {
|
||||
@@ -1123,7 +1164,7 @@ export class ExtHostModelView implements ExtHostModelViewShape {
|
||||
}
|
||||
|
||||
$registerWidget(handle: number, id: string, connection: sqlops.connection.Connection, serverInfo: sqlops.ServerInfo): void {
|
||||
let view = new ModelViewImpl(this._proxy, handle, connection, serverInfo);
|
||||
let view = new ModelViewImpl(this._proxy, handle, connection, serverInfo, this._mainContext, this._extHostModelViewTree);
|
||||
this._modelViews.set(handle, view);
|
||||
this._handlers.get(id)(view);
|
||||
}
|
||||
|
||||
140
src/sql/workbench/api/node/extHostModelViewTree.ts
Normal file
140
src/sql/workbench/api/node/extHostModelViewTree.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import * as vscode from 'vscode';
|
||||
import { TPromise } from 'vs/base/common/winjs.base';
|
||||
import { SqlMainContext, ExtHostModelViewTreeViewsShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { CommandsConverter } from 'vs/workbench/api/node/extHostCommands';
|
||||
import { asWinJsPromise } from 'vs/base/common/async';
|
||||
import { IMainContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vsTreeExt from 'vs/workbench/api/node/extHostTreeViews';
|
||||
|
||||
export class ExtHostModelViewTreeViews implements ExtHostModelViewTreeViewsShape {
|
||||
private _proxy: MainThreadModelViewShape;
|
||||
|
||||
private treeViews: Map<string, ExtHostTreeView<any>> = new Map<string, ExtHostTreeView<any>>();
|
||||
|
||||
|
||||
constructor(
|
||||
private _mainContext: IMainContext
|
||||
) {
|
||||
this._proxy = this._mainContext.getProxy(SqlMainContext.MainThreadModelView);
|
||||
}
|
||||
|
||||
$createTreeView<T>(handle: number, componentId: string, options: { treeDataProvider: sqlops.TreeComponentDataProvider<T> }): vscode.TreeView<T> {
|
||||
if (!options || !options.treeDataProvider) {
|
||||
throw new Error('Options with treeDataProvider is mandatory');
|
||||
}
|
||||
|
||||
const treeView = this.createExtHostTreeViewer(handle, componentId, options.treeDataProvider);
|
||||
return {
|
||||
reveal: (element: T, options?: { select?: boolean }): Thenable<void> => {
|
||||
return treeView.reveal(element, options);
|
||||
},
|
||||
dispose: () => {
|
||||
this.treeViews.delete(componentId);
|
||||
treeView.dispose();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeComponentItem[]> {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (!treeView) {
|
||||
|
||||
return TPromise.wrapError<ITreeComponentItem[]>(new Error(localize('treeView.notRegistered', 'No tree view with id \'{0}\' registered.', treeViewId)));
|
||||
}
|
||||
return treeView.getChildren(treeItemHandle);
|
||||
}
|
||||
|
||||
$onNodeCheckedChanged(treeViewId: string, treeItemHandle?: string, checked?: boolean): void {
|
||||
const treeView = this.treeViews.get(treeViewId);
|
||||
if (treeView) {
|
||||
treeView.onNodeCheckedChanged(treeItemHandle, checked);
|
||||
}
|
||||
}
|
||||
|
||||
private createExtHostTreeViewer<T>(handle: number, id: string, dataProvider: sqlops.TreeComponentDataProvider<T>): ExtHostTreeView<T> {
|
||||
const treeView = new ExtHostTreeView<T>(handle, id, dataProvider, this._proxy, undefined);
|
||||
this.treeViews.set(`${handle}-${id}`, treeView);
|
||||
return treeView;
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTreeView<T> extends vsTreeExt.ExtHostTreeView<T> {
|
||||
|
||||
constructor(private handle: number, private componentId: string, private componentDataProvider: sqlops.TreeComponentDataProvider<T>, private modelViewProxy: MainThreadModelViewShape, commands: CommandsConverter) {
|
||||
super(componentId, componentDataProvider, undefined, commands);
|
||||
}
|
||||
|
||||
onNodeCheckedChanged(parentHandle?: vsTreeExt.TreeItemHandle, checked?: boolean): void {
|
||||
const parentElement = parentHandle ? this.getExtensionElement(parentHandle) : void 0;
|
||||
if (parentHandle && !parentElement) {
|
||||
console.error(`No tree item with id \'${parentHandle}\' found.`);
|
||||
}
|
||||
|
||||
this.componentDataProvider.onNodeCheckedChanged(parentElement, checked);
|
||||
}
|
||||
|
||||
reveal(element: T, options?: { select?: boolean }): TPromise<void> {
|
||||
if (typeof this.componentDataProvider.getParent !== 'function') {
|
||||
return TPromise.wrapError(new Error(`Required registered TreeDataProvider to implement 'getParent' method to access 'reveal' method`));
|
||||
}
|
||||
let i: void;
|
||||
return this.resolveUnknownParentChain(element)
|
||||
.then(parentChain => this.resolveTreeNode(element, parentChain[parentChain.length - 1])
|
||||
.then(treeNode => i));
|
||||
}
|
||||
|
||||
protected refresh(elements: T[]): void {
|
||||
const hasRoot = elements.some(element => !element);
|
||||
if (hasRoot) {
|
||||
this.clearAll(); // clear cache
|
||||
this.modelViewProxy.$refreshDataProvider(this.handle, this.componentId);
|
||||
} else {
|
||||
const handlesToRefresh = this.getHandlesToRefresh(elements);
|
||||
if (handlesToRefresh.length) {
|
||||
this.refreshHandles(handlesToRefresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected refreshHandles(itemHandles: vsTreeExt.TreeItemHandle[]): TPromise<void> {
|
||||
const itemsToRefresh: { [treeItemHandle: string]: ITreeComponentItem } = {};
|
||||
return TPromise.join(itemHandles.map(treeItemHandle =>
|
||||
this.refreshNode(treeItemHandle)
|
||||
.then(node => {
|
||||
if (node) {
|
||||
itemsToRefresh[treeItemHandle] = node.item;
|
||||
}
|
||||
})))
|
||||
.then(() => Object.keys(itemsToRefresh).length ? this.modelViewProxy.$refreshDataProvider(this.handle, this.componentId, itemsToRefresh) : null);
|
||||
}
|
||||
|
||||
protected refreshNode(treeItemHandle: vsTreeExt.TreeItemHandle): TPromise<vsTreeExt.TreeNode> {
|
||||
const extElement = this.getExtensionElement(treeItemHandle);
|
||||
const existing = this.nodes.get(extElement);
|
||||
//this.clearChildren(extElement); // clear children cache
|
||||
return asWinJsPromise(() => this.componentDataProvider.getTreeItem(extElement))
|
||||
.then(extTreeItem => {
|
||||
if (extTreeItem) {
|
||||
const newNode = this.createTreeNode(extElement, extTreeItem, existing.parent);
|
||||
this.updateNodeCache(extElement, newNode, existing, existing.parent);
|
||||
return newNode;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
protected createTreeItem(element: T, extensionTreeItem: sqlops.TreeComponentItem, parent?: vsTreeExt.TreeNode): ITreeComponentItem {
|
||||
let item = super.createTreeItem(element, extensionTreeItem, parent);
|
||||
item = Object.assign({}, item, { checked: extensionTreeItem.checked });
|
||||
return item;
|
||||
}
|
||||
}
|
||||
@@ -9,10 +9,9 @@ import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostC
|
||||
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
||||
import * as sqlops from 'sqlops';
|
||||
|
||||
import { IModelViewService } from 'sql/services/modelComponents/modelViewService';
|
||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IItemConfig, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||
import { IModelView } from 'sql/services/model/modelViewService';
|
||||
|
||||
|
||||
@@ -22,15 +21,14 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
||||
private static _handlePool = 0;
|
||||
private readonly _proxy: ExtHostModelViewShape;
|
||||
private readonly _dialogs = new Map<number, IModelView>();
|
||||
|
||||
private knownWidgets = new Array<string>();
|
||||
|
||||
constructor(
|
||||
context: IExtHostContext,
|
||||
private _context: IExtHostContext,
|
||||
@IModelViewService viewService: IModelViewService
|
||||
) {
|
||||
super();
|
||||
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelView);
|
||||
this._proxy = _context.getProxy(SqlExtHostContext.ExtHostModelView);
|
||||
viewService.onRegisteredModelView(view => {
|
||||
if (this.knownWidgets.includes(view.id)) {
|
||||
let handle = MainThreadModelView._handlePool++;
|
||||
@@ -79,6 +77,14 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
||||
});
|
||||
}
|
||||
|
||||
$setDataProvider(handle: number, componentId: string): Thenable<void> {
|
||||
return this.execModelViewAction(handle, (modelView) => modelView.setDataProvider(handle, componentId, this._context));
|
||||
}
|
||||
|
||||
$refreshDataProvider(handle: number, componentId: string, item?: any): Thenable<void> {
|
||||
return this.execModelViewAction(handle, (modelView) => modelView.refreshDataProvider(componentId, item));
|
||||
}
|
||||
|
||||
$setProperties(handle: number, componentId: string, properties: { [key: string]: any; }): Thenable<void> {
|
||||
return this.execModelViewAction(handle, (modelView) => modelView.setProperties(componentId, properties));
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import { ExtHostDashboard } from 'sql/workbench/api/node/extHostDashboard';
|
||||
import { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
||||
import { ExtHostModelViewTreeViews } from 'sql/workbench/api/node/extHostModelViewTree';
|
||||
import { ExtHostQueryEditor } from 'sql/workbench/api/node/extHostQueryEditor';
|
||||
import { ExtHostBackgroundTaskManagement } from './extHostBackgroundTaskManagement';
|
||||
|
||||
@@ -66,7 +67,8 @@ export function createApiFactory(
|
||||
const extHostTasks = rpcProtocol.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(rpcProtocol, logService));
|
||||
const extHostBackgroundTaskManagement = rpcProtocol.set(SqlExtHostContext.ExtHostBackgroundTaskManagement, new ExtHostBackgroundTaskManagement(rpcProtocol));
|
||||
const extHostWebviewWidgets = rpcProtocol.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(rpcProtocol));
|
||||
const extHostModelView = rpcProtocol.set(SqlExtHostContext.ExtHostModelView, new ExtHostModelView(rpcProtocol));
|
||||
const extHostModelViewTree = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewTreeViews, new ExtHostModelViewTreeViews(rpcProtocol));
|
||||
const extHostModelView = rpcProtocol.set(SqlExtHostContext.ExtHostModelView, new ExtHostModelView(rpcProtocol, extHostModelViewTree));
|
||||
const extHostDashboard = rpcProtocol.set(SqlExtHostContext.ExtHostDashboard, new ExtHostDashboard(rpcProtocol));
|
||||
const extHostModelViewDialog = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewDialog, new ExtHostModelViewDialog(rpcProtocol, extHostModelView, extHostBackgroundTaskManagement));
|
||||
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
||||
@@ -415,7 +417,8 @@ export function createApiFactory(
|
||||
ui: ui,
|
||||
StatusIndicator: sqlExtHostTypes.StatusIndicator,
|
||||
CardType: sqlExtHostTypes.CardType,
|
||||
SqlThemeIcon: sqlExtHostTypes.SqlThemeIcon
|
||||
SqlThemeIcon: sqlExtHostTypes.SqlThemeIcon,
|
||||
TreeComponentItem: sqlExtHostTypes.TreeComponentItem
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as sqlops from 'sqlops';
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
|
||||
import {
|
||||
IItemConfig, ModelComponentTypes, IComponentShape, IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails,
|
||||
@@ -531,6 +532,7 @@ export const SqlExtHostContext = {
|
||||
ExtHostBackgroundTaskManagement: createExtId<ExtHostBackgroundTaskManagementShape>('ExtHostBackgroundTaskManagement'),
|
||||
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
|
||||
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
|
||||
ExtHostModelViewTreeViews: createExtId<ExtHostModelViewTreeViewsShape>('ExtHostModelViewTreeViews'),
|
||||
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard'),
|
||||
ExtHostModelViewDialog: createExtId<ExtHostModelViewDialogShape>('ExtHostModelViewDialog'),
|
||||
ExtHostQueryEditor: createExtId<ExtHostQueryEditorShape>('ExtHostQueryEditor')
|
||||
@@ -590,6 +592,12 @@ export interface ExtHostModelViewShape {
|
||||
$runCustomValidations(handle: number, id: string): Thenable<boolean>;
|
||||
}
|
||||
|
||||
export interface ExtHostModelViewTreeViewsShape {
|
||||
$getChildren(treeViewId: string, treeItemHandle?: string): TPromise<ITreeComponentItem[]>;
|
||||
$createTreeView(handle: number, componentId: string, options: { treeDataProvider: vscode.TreeDataProvider<any> }): vscode.TreeView<any>;
|
||||
$onNodeCheckedChanged(treeViewId: string, treeItemHandle?: string, checked?: boolean): void;
|
||||
}
|
||||
|
||||
export interface ExtHostBackgroundTaskManagementShape {
|
||||
$onTaskRegistered(operationId: string): void;
|
||||
$onTaskCanceled(operationId: string): void;
|
||||
@@ -611,6 +619,8 @@ export interface MainThreadModelViewShape extends IDisposable {
|
||||
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
|
||||
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
||||
$validate(handle: number, componentId: string): Thenable<boolean>;
|
||||
$setDataProvider(handle: number, componentId: string): Thenable<void>;
|
||||
$refreshDataProvider(handle: number, componentId: string, item?: any): Thenable<void>;
|
||||
}
|
||||
|
||||
export interface ExtHostObjectExplorerShape {
|
||||
|
||||
Reference in New Issue
Block a user