mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Feature/tree component (#2077)
*added tree component to the model builder
This commit is contained in:
@@ -11,6 +11,7 @@ import * as vscode from 'vscode';
|
|||||||
import SplitPropertiesPanel from './splitPropertiesPanel';
|
import SplitPropertiesPanel from './splitPropertiesPanel';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import {TreeNode, TreeDataProvider} from './treeDataProvider';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main controller class that initializes the extension
|
* The main controller class that initializes the extension
|
||||||
@@ -67,6 +68,67 @@ export default class MainController implements vscode.Disposable {
|
|||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async getTab3Content(view: sqlops.ModelView): Promise<void> {
|
||||||
|
let treeData = {
|
||||||
|
label: '1',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '11',
|
||||||
|
id: '11',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '111',
|
||||||
|
id: '111',
|
||||||
|
checked: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '112',
|
||||||
|
id: '112',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
label: '1121',
|
||||||
|
id: '1121',
|
||||||
|
checked: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '1122',
|
||||||
|
id: '1122',
|
||||||
|
checked: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '12',
|
||||||
|
id: '12',
|
||||||
|
checked: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
id: '1'
|
||||||
|
};
|
||||||
|
let root = TreeNode.createTree(treeData);
|
||||||
|
|
||||||
|
let treeDataProvider = new TreeDataProvider(root);
|
||||||
|
|
||||||
|
let tree: sqlops.TreeComponent<TreeNode> = view.modelBuilder.tree<TreeNode>().withProperties({
|
||||||
|
'withCheckbox': true
|
||||||
|
}).component();
|
||||||
|
tree.registerDataProvider(treeDataProvider);
|
||||||
|
let formModel = view.modelBuilder.formContainer()
|
||||||
|
.withFormItems([{
|
||||||
|
component: tree,
|
||||||
|
title: 'Tree'
|
||||||
|
}], {
|
||||||
|
horizontal: false,
|
||||||
|
componentWidth: 800,
|
||||||
|
componentHeight: 800
|
||||||
|
}).component();
|
||||||
|
let formWrapper = view.modelBuilder.loadingComponent().withItem(formModel).component();
|
||||||
|
formWrapper.loading = false;
|
||||||
|
|
||||||
|
await view.initializeModel(formWrapper);
|
||||||
|
}
|
||||||
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
|
private async getTabContent(view: sqlops.ModelView, customButton1: sqlops.window.modelviewdialog.Button, customButton2: sqlops.window.modelviewdialog.Button, componentWidth: number | string): Promise<void> {
|
||||||
let inputBox = view.modelBuilder.inputBox()
|
let inputBox = view.modelBuilder.inputBox()
|
||||||
.withProperties({
|
.withProperties({
|
||||||
@@ -271,8 +333,9 @@ export default class MainController implements vscode.Disposable {
|
|||||||
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
|
let tab1 = sqlops.window.modelviewdialog.createTab('Test tab 1');
|
||||||
|
|
||||||
let tab2 = sqlops.window.modelviewdialog.createTab('Test tab 2');
|
let tab2 = sqlops.window.modelviewdialog.createTab('Test tab 2');
|
||||||
|
let tab3 = sqlops.window.modelviewdialog.createTab('Test tab 3');
|
||||||
tab2.content = 'sqlservices';
|
tab2.content = 'sqlservices';
|
||||||
dialog.content = [tab1, tab2];
|
dialog.content = [tab1, tab2, tab3];
|
||||||
dialog.okButton.onClick(() => console.log('ok clicked!'));
|
dialog.okButton.onClick(() => console.log('ok clicked!'));
|
||||||
dialog.cancelButton.onClick(() => console.log('cancel clicked!'));
|
dialog.cancelButton.onClick(() => console.log('cancel clicked!'));
|
||||||
dialog.okButton.label = 'ok';
|
dialog.okButton.label = 'ok';
|
||||||
@@ -285,6 +348,10 @@ export default class MainController implements vscode.Disposable {
|
|||||||
tab1.registerContent(async (view) => {
|
tab1.registerContent(async (view) => {
|
||||||
await this.getTabContent(view, customButton1, customButton2, 400);
|
await this.getTabContent(view, customButton1, customButton2, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
tab3.registerContent(async (view) => {
|
||||||
|
await this.getTab3Content(view);
|
||||||
|
});
|
||||||
sqlops.window.modelviewdialog.openDialog(dialog);
|
sqlops.window.modelviewdialog.openDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
299
samples/sqlservices/src/controllers/treeDataProvider.ts
Normal file
299
samples/sqlservices/src/controllers/treeDataProvider.ts
Normal file
@@ -0,0 +1,299 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 * as vscode from 'vscode';
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
export enum TreeCheckboxState {
|
||||||
|
Intermediate = 0,
|
||||||
|
Checked = 1,
|
||||||
|
Unchecked = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TreeComponentDataModel {
|
||||||
|
label?: string;
|
||||||
|
children?: TreeComponentDataModel[];
|
||||||
|
id?: string;
|
||||||
|
checked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TreeNode implements sqlops.TreeComponentItem {
|
||||||
|
private _onNodeChange = new vscode.EventEmitter<void>();
|
||||||
|
private _onTreeChange = new vscode.EventEmitter<TreeNode>();
|
||||||
|
private _data: TreeComponentDataModel;
|
||||||
|
private _parent?: TreeNode;
|
||||||
|
private _root: TreeNode;
|
||||||
|
private _isAlwaysLeaf: boolean;
|
||||||
|
private _nodeMap: Map<string, TreeNode>;
|
||||||
|
private _children: TreeNode[];
|
||||||
|
|
||||||
|
public readonly onNodeChange: vscode.Event<void> = this._onNodeChange.event;
|
||||||
|
public readonly onTreeChange: vscode.Event<TreeNode> = this._onTreeChange.event;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new instance of tree node
|
||||||
|
* @param data the underlining data that's bind to the tree node, any change in the tree will affect the same node in data
|
||||||
|
* @param root the root node of the tree. If passed null, the current node will be the root
|
||||||
|
*/
|
||||||
|
constructor(data: TreeComponentDataModel, root: TreeNode) {
|
||||||
|
if (!data) {
|
||||||
|
throw new Error(`Invalid tree node data`);
|
||||||
|
}
|
||||||
|
if (root === undefined) {
|
||||||
|
root = this;
|
||||||
|
root._nodeMap = new Map<string, TreeNode>();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._root = root;
|
||||||
|
if (this.findNode(data.id)) {
|
||||||
|
throw new Error(`tree node with id: '${data.id}' already exists`);
|
||||||
|
}
|
||||||
|
this._data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* id for TreeNode
|
||||||
|
*/
|
||||||
|
public get id(): string {
|
||||||
|
return this.data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public set id(value: string) {
|
||||||
|
this.data.id = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Label to display to the user, describing this node
|
||||||
|
*/
|
||||||
|
public set label(value: string) {
|
||||||
|
this.data.label = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get label(): string {
|
||||||
|
return this.data.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this a leaf node (in which case no children can be generated) or is it expandable?
|
||||||
|
*/
|
||||||
|
public get isAlwaysLeaf(): boolean {
|
||||||
|
return this._isAlwaysLeaf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent of this node
|
||||||
|
*/
|
||||||
|
public get parent(): TreeNode {
|
||||||
|
return this._parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get root(): TreeNode {
|
||||||
|
return this._root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path identifying this node
|
||||||
|
*/
|
||||||
|
public get nodePath(): string {
|
||||||
|
return `${this.parent ? this.parent.nodePath + '-' : ''}${this.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get data(): TreeComponentDataModel {
|
||||||
|
if (this._data === undefined) {
|
||||||
|
this._data = {
|
||||||
|
label: undefined
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return this._data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public changeNodeCheckedState(value: boolean, fromParent?: boolean): void {
|
||||||
|
if (value !== this.checked) {
|
||||||
|
if (value !== undefined && this.children) {
|
||||||
|
this.children.forEach(child => {
|
||||||
|
child.changeNodeCheckedState(value, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.checked = value;
|
||||||
|
if (!fromParent && this.parent) {
|
||||||
|
this.parent.refreshState();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onValueChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public set checked(value: boolean) {
|
||||||
|
this.data.checked = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshState(): void {
|
||||||
|
if (this.hasChildren) {
|
||||||
|
if (this.children.every(c => c.checked)) {
|
||||||
|
this.changeNodeCheckedState(true);
|
||||||
|
} else if (this.children.every(c => c.checked !== undefined && !c.checked)) {
|
||||||
|
this.changeNodeCheckedState(false);
|
||||||
|
} else {
|
||||||
|
this.changeNodeCheckedState(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hasChildren(): boolean {
|
||||||
|
return this.children !== undefined && this.children.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get checked(): boolean {
|
||||||
|
return this.data.checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
private onValueChanged(): void {
|
||||||
|
this._onNodeChange.fire();
|
||||||
|
if (this.root) {
|
||||||
|
this.root._onTreeChange.fire(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get checkboxState(): TreeCheckboxState {
|
||||||
|
if (this.checked === undefined) {
|
||||||
|
return TreeCheckboxState.Intermediate;
|
||||||
|
} else {
|
||||||
|
return this.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public findNode(id: string): TreeNode {
|
||||||
|
if (this.id === id) {
|
||||||
|
return this;
|
||||||
|
} else if (this.root) {
|
||||||
|
return this.root._nodeMap.has(id) ? this.root._nodeMap.get(id) : undefined;
|
||||||
|
} else {
|
||||||
|
let node: TreeNode;
|
||||||
|
if (this.children) {
|
||||||
|
this.children.forEach(child => {
|
||||||
|
node = child.findNode(id);
|
||||||
|
if (node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Children of this node
|
||||||
|
*/
|
||||||
|
public get children(): TreeNode[] {
|
||||||
|
return this._children;
|
||||||
|
}
|
||||||
|
|
||||||
|
public addChildNode(node: TreeNode): void {
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
if (!node.root) {
|
||||||
|
node._root = this.root;
|
||||||
|
}
|
||||||
|
if (!node.parent) {
|
||||||
|
node._parent = this;
|
||||||
|
}
|
||||||
|
if (node.root) {
|
||||||
|
node.root._nodeMap.set(node.id, node);
|
||||||
|
}
|
||||||
|
this._children.push(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createNode(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
|
||||||
|
let rootNode = root || (parent !== undefined ? parent.root : undefined);
|
||||||
|
let treeNode = new TreeNode(nodeData, rootNode);
|
||||||
|
|
||||||
|
treeNode._parent = parent;
|
||||||
|
return treeNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static createTree(nodeData: TreeComponentDataModel, parent?: TreeNode, root?: TreeNode): TreeNode {
|
||||||
|
if (nodeData) {
|
||||||
|
let treeNode = TreeNode.createNode(nodeData, parent, root);
|
||||||
|
|
||||||
|
if (nodeData.children && nodeData.children.length > 0) {
|
||||||
|
treeNode._isAlwaysLeaf = false;
|
||||||
|
treeNode._children = [];
|
||||||
|
nodeData.children.forEach(childNode => {
|
||||||
|
if (childNode) {
|
||||||
|
let childTreeNode = TreeNode.createTree(childNode, treeNode, root || treeNode.root);
|
||||||
|
treeNode.addChildNode(childTreeNode);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
treeNode.refreshState();
|
||||||
|
} else {
|
||||||
|
treeNode._isAlwaysLeaf = true;
|
||||||
|
}
|
||||||
|
return treeNode;
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TreeDataProvider implements sqlops.TreeComponentDataProvider<TreeNode> {
|
||||||
|
private _onDidChangeTreeData = new vscode.EventEmitter<TreeNode>();
|
||||||
|
constructor(private _root: TreeNode) {
|
||||||
|
if(this._root) {
|
||||||
|
this._root.onTreeChange(node => {
|
||||||
|
this._onDidChangeTreeData.fire(node);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onDidChangeTreeData?: vscode.Event<TreeNode | undefined | null> = this._onDidChangeTreeData.event ;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get [TreeItem](#TreeItem) representation of the `element`
|
||||||
|
*
|
||||||
|
* @param element The element for which [TreeItem](#TreeItem) representation is asked for.
|
||||||
|
* @return [TreeItem](#TreeItem) representation of the element
|
||||||
|
*/
|
||||||
|
getTreeItem(element: TreeNode): sqlops.TreeComponentItem | Thenable<sqlops.TreeComponentItem> {
|
||||||
|
let item: sqlops.TreeComponentItem = {};
|
||||||
|
item.label = element.label;
|
||||||
|
item.checked = element.checked;
|
||||||
|
item.iconPath = vscode.Uri.file(path.join(__dirname, '..', 'media', 'monitor.svg'));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the children of `element` or root if no element is passed.
|
||||||
|
*
|
||||||
|
* @param element The element from which the provider gets children. Can be `undefined`.
|
||||||
|
* @return Children of `element` or root if no element is passed.
|
||||||
|
*/
|
||||||
|
getChildren(element?: TreeNode): vscode.ProviderResult<TreeNode[]> {
|
||||||
|
if (element) {
|
||||||
|
return Promise.resolve(element.children);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(this._root.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getParent(element?: TreeNode): vscode.ProviderResult<TreeNode> {
|
||||||
|
if (element) {
|
||||||
|
return Promise.resolve(element.parent);
|
||||||
|
} else {
|
||||||
|
return Promise.resolve(this._root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeCheckedChanged(element: TreeNode, checked: boolean): void {
|
||||||
|
if (element) {
|
||||||
|
element.changeNodeCheckedState(checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -57,7 +57,7 @@ import { OperatorsViewComponent } from 'sql/parts/jobManagement/views/operatorsV
|
|||||||
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
|
import { ProxiesViewComponent } from 'sql/parts/jobManagement/views/proxiesView.component';
|
||||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
|
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
|
||||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
|
|
||||||
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
let baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||||
|
|||||||
@@ -71,6 +71,12 @@ export abstract class ComponentBase extends Disposable implements IComponent, On
|
|||||||
|
|
||||||
abstract setLayout(layout: any): void;
|
abstract setLayout(layout: any): void;
|
||||||
|
|
||||||
|
public setDataProvider(handle: number, componentId: string, context: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshDataProvider(item: any): void {
|
||||||
|
}
|
||||||
|
|
||||||
public setProperties(properties: { [key: string]: any; }): void {
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
if (!properties) {
|
if (!properties) {
|
||||||
this.properties = {};
|
this.properties = {};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import DeclarativeTableComponent from './declarativeTable.component';
|
|||||||
import ListBoxComponent from './listbox.component';
|
import ListBoxComponent from './listbox.component';
|
||||||
import ButtonComponent from './button.component';
|
import ButtonComponent from './button.component';
|
||||||
import CheckBoxComponent from './checkbox.component';
|
import CheckBoxComponent from './checkbox.component';
|
||||||
|
import TreeComponent from './tree/tree.component';
|
||||||
import RadioButtonComponent from './radioButton.component';
|
import RadioButtonComponent from './radioButton.component';
|
||||||
import WebViewComponent from './webview.component';
|
import WebViewComponent from './webview.component';
|
||||||
import TableComponent from './table.component';
|
import TableComponent from './table.component';
|
||||||
@@ -73,6 +74,9 @@ registerComponentType(TABLE_COMPONENT, ModelComponentTypes.Table, TableComponent
|
|||||||
export const LOADING_COMPONENT = 'loading-component';
|
export const LOADING_COMPONENT = 'loading-component';
|
||||||
registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, LoadingComponent);
|
registerComponentType(LOADING_COMPONENT, ModelComponentTypes.LoadingComponent, LoadingComponent);
|
||||||
|
|
||||||
|
export const TREE_COMPONENT = 'tree-component';
|
||||||
|
registerComponentType(TREE_COMPONENT, ModelComponentTypes.TreeComponent, TreeComponent);
|
||||||
|
|
||||||
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
|
export const FILEBROWSERTREE_COMPONENT = 'filebrowsertree-component';
|
||||||
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
|
registerComponentType(FILEBROWSERTREE_COMPONENT, ModelComponentTypes.FileBrowserTree, FileBrowserTreeComponent);
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
|
|||||||
import { Event, Emitter } from 'vs/base/common/event';
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
|
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
|
||||||
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
import { ISelectData } from 'vs/base/browser/ui/selectBox/selectBox';
|
||||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
import * as nls from 'vs/nls';
|
import * as nls from 'vs/nls';
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export interface IComponent {
|
|||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
readonly valid?: boolean;
|
readonly valid?: boolean;
|
||||||
validate(): Thenable<boolean>;
|
validate(): Thenable<boolean>;
|
||||||
|
setDataProvider(handle: number, componentId: string, context: any): void;
|
||||||
|
refreshDataProvider(item: any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
|
export const COMPONENT_CONFIG = new InjectionToken<IComponentConfig>('component_config');
|
||||||
|
|||||||
148
src/sql/parts/modelComponents/tree/tree.component.ts
Normal file
148
src/sql/parts/modelComponents/tree/tree.component.ts
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
import 'vs/css!sql/parts/modelComponents/tree/treeComponent';
|
||||||
|
import 'vs/css!sql/media/icons/common-icons';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Component, Input, Inject, ChangeDetectorRef, forwardRef,
|
||||||
|
ViewChild, ElementRef, OnDestroy, AfterViewInit
|
||||||
|
} from '@angular/core';
|
||||||
|
|
||||||
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
|
import { ComponentBase } from 'sql/parts/modelComponents/componentBase';
|
||||||
|
import { IComponent, IComponentDescriptor, IModelStore, ComponentEventType } from 'sql/parts/modelComponents/interfaces';
|
||||||
|
import { Tree } from 'vs/base/parts/tree/browser/treeImpl';
|
||||||
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
|
import { TreeComponentRenderer } from 'sql/parts/modelComponents/tree/treeComponentRenderer';
|
||||||
|
import { TreeComponentDataSource } from 'sql/parts/modelComponents/tree/treeDataSource';
|
||||||
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { attachListStyler } from 'vs/platform/theme/common/styler';
|
||||||
|
import { DefaultFilter, DefaultAccessibilityProvider, DefaultController } from 'vs/base/parts/tree/browser/treeDefaults';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { ITreeComponentItem, IModelViewTreeViewDataProvider } from 'sql/workbench/common/views';
|
||||||
|
import { TreeViewDataProvider } from './treeViewDataProvider';
|
||||||
|
|
||||||
|
class Root implements ITreeComponentItem {
|
||||||
|
label = 'root';
|
||||||
|
handle = '0';
|
||||||
|
parentHandle = null;
|
||||||
|
collapsibleState = 0;
|
||||||
|
children = void 0;
|
||||||
|
options = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'modelview-tree',
|
||||||
|
template: `
|
||||||
|
<div #input style="width: 100%;height:100%"></div>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export default class TreeComponent extends ComponentBase implements IComponent, OnDestroy, AfterViewInit {
|
||||||
|
@Input() descriptor: IComponentDescriptor;
|
||||||
|
@Input() modelStore: IModelStore;
|
||||||
|
private _tree: Tree;
|
||||||
|
private _treeRenderer: TreeComponentRenderer;
|
||||||
|
private _dataProvider: TreeViewDataProvider;
|
||||||
|
|
||||||
|
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||||
|
constructor(
|
||||||
|
@Inject(forwardRef(() => CommonServiceInterface)) private _commonService: CommonServiceInterface,
|
||||||
|
@Inject(forwardRef(() => ChangeDetectorRef)) changeRef: ChangeDetectorRef,
|
||||||
|
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||||
|
@Inject(IContextViewService) private contextViewService: IContextViewService,
|
||||||
|
@Inject(IInstantiationService) private _instantiationService: IInstantiationService) {
|
||||||
|
super(changeRef);
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.baseInit();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ngAfterViewInit(): void {
|
||||||
|
if (this._inputContainer) {
|
||||||
|
this.createTreeControl();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.baseDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setDataProvider(handle: number, componentId: string, context: any): any {
|
||||||
|
this._dataProvider = new TreeViewDataProvider(handle, componentId, context);
|
||||||
|
this.createTreeControl();
|
||||||
|
}
|
||||||
|
|
||||||
|
public refreshDataProvider(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeComponentItem }): void {
|
||||||
|
if (this._dataProvider) {
|
||||||
|
this._dataProvider.refresh(itemsToRefreshByHandle);
|
||||||
|
}
|
||||||
|
if (this._tree) {
|
||||||
|
for (const item of Object.values(itemsToRefreshByHandle)) {
|
||||||
|
this._tree.refresh(<ITreeComponentItem>item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createTreeControl(): void {
|
||||||
|
if (!this._tree && this._dataProvider) {
|
||||||
|
const dataSource = this._instantiationService.createInstance(TreeComponentDataSource, this._dataProvider);
|
||||||
|
const renderer = this._instantiationService.createInstance(TreeComponentRenderer, this.themeService, { withCheckbox: this.withCheckbox });
|
||||||
|
this._treeRenderer = renderer;
|
||||||
|
const controller = new DefaultController();
|
||||||
|
const filter = new DefaultFilter();
|
||||||
|
const sorter = undefined;
|
||||||
|
const dnd = undefined;
|
||||||
|
const accessibilityProvider = new DefaultAccessibilityProvider();
|
||||||
|
|
||||||
|
this._tree = new Tree(this._inputContainer.nativeElement,
|
||||||
|
{ dataSource, renderer, controller, dnd, filter, sorter, accessibilityProvider },
|
||||||
|
{
|
||||||
|
indentPixels: 10,
|
||||||
|
twistiePixels: 20,
|
||||||
|
ariaLabel: 'Tree Node'
|
||||||
|
});
|
||||||
|
this._tree.setInput(new Root());
|
||||||
|
this._tree.domFocus();
|
||||||
|
this._register(this._tree);
|
||||||
|
this._register(attachListStyler(this._tree, this.themeService));
|
||||||
|
this._tree.refresh();
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// IComponent implementation
|
||||||
|
|
||||||
|
public layout(): void {
|
||||||
|
this._changeRef.detectChanges();
|
||||||
|
this.createTreeControl();
|
||||||
|
if (this._tree) {
|
||||||
|
this._tree.layout(this.convertSizeToNumber(this.width), this.convertSizeToNumber(this.height));
|
||||||
|
this._tree.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public setLayout(layout: any): void {
|
||||||
|
// TODO allow configuring the look and feel
|
||||||
|
|
||||||
|
this.layout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public setProperties(properties: { [key: string]: any; }): void {
|
||||||
|
super.setProperties(properties);
|
||||||
|
this._treeRenderer.options.withCheckbox = this.withCheckbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get withCheckbox(): boolean {
|
||||||
|
return this.getPropertyOrDefault<sqlops.TreeProperties, boolean>((props) => props.withCheckbox, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public set withCheckbox(newValue: boolean) {
|
||||||
|
this.setPropertyFromUI<sqlops.TreeProperties, boolean>((properties, value) => { properties.withCheckbox = value; }, newValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/sql/parts/modelComponents/tree/treeComponent.css
Normal file
8
src/sql/parts/modelComponents/tree/treeComponent.css
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
.tree-component-node-tile {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-component-node-tile .model-view-tree-node-item-icon{
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
}
|
||||||
168
src/sql/parts/modelComponents/tree/treeComponentRenderer.ts
Normal file
168
src/sql/parts/modelComponents/tree/treeComponentRenderer.ts
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 'vs/css!sql/media/icons/common-icons';
|
||||||
|
import * as dom from 'vs/base/browser/dom';
|
||||||
|
import { localize } from 'vs/nls';
|
||||||
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
import { ITree, IRenderer } from 'vs/base/parts/tree/browser/tree';
|
||||||
|
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
|
||||||
|
import { LIGHT } from 'vs/platform/theme/common/themeService';
|
||||||
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
import { Event, Emitter } from 'vs/base/common/event';
|
||||||
|
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||||
|
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||||
|
|
||||||
|
export enum TreeCheckboxState {
|
||||||
|
Intermediate = 0,
|
||||||
|
Checked = 1,
|
||||||
|
Unchecked = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
export class TreeDataTemplate extends Disposable {
|
||||||
|
root: HTMLElement;
|
||||||
|
label: HTMLSpanElement;
|
||||||
|
icon: HTMLElement;
|
||||||
|
private _checkbox: HTMLInputElement;
|
||||||
|
model: ITreeComponentItem;
|
||||||
|
private _onChange = new Emitter<boolean>();
|
||||||
|
|
||||||
|
public readonly onChange: Event<boolean> = this._onChange.event;
|
||||||
|
|
||||||
|
public set checkbox(input: HTMLInputElement) {
|
||||||
|
this._checkbox = input;
|
||||||
|
this.handleOnChange(this._checkbox, () => {
|
||||||
|
this._onChange.fire(this._checkbox.checked);
|
||||||
|
if (this.model && this.model.onCheckedChanged) {
|
||||||
|
this.model.onCheckedChanged(this._checkbox.checked);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public get checkboxState(): TreeCheckboxState {
|
||||||
|
if (this._checkbox.indeterminate) {
|
||||||
|
return TreeCheckboxState.Intermediate;
|
||||||
|
} else {
|
||||||
|
return this.checkbox.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public set checkboxState(value: TreeCheckboxState) {
|
||||||
|
if (this.checkboxState !== value) {
|
||||||
|
switch (value) {
|
||||||
|
case TreeCheckboxState.Checked:
|
||||||
|
this._checkbox.indeterminate = false;
|
||||||
|
this._checkbox.checked = true;
|
||||||
|
break;
|
||||||
|
case TreeCheckboxState.Unchecked:
|
||||||
|
this._checkbox.indeterminate = false;
|
||||||
|
this._checkbox.checked = false;
|
||||||
|
break;
|
||||||
|
case TreeCheckboxState.Intermediate:
|
||||||
|
this._checkbox.indeterminate = true;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public get checkbox(): HTMLInputElement {
|
||||||
|
return this._checkbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected handleOnChange(domNode: HTMLElement, listener: (e: Event<void>) => void): void {
|
||||||
|
this._register(dom.addDisposableListener(domNode, dom.EventType.CHANGE, listener));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the tree items.
|
||||||
|
* Uses the dom template to render connection groups and connections.
|
||||||
|
*/
|
||||||
|
export class TreeComponentRenderer extends Disposable implements IRenderer {
|
||||||
|
|
||||||
|
public static DEFAULT_TEMPLATE = 'DEFAULT_TEMPLATE';
|
||||||
|
public static DEFAULT_HEIGHT = 20;
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private themeService: IWorkbenchThemeService,
|
||||||
|
public options?: { withCheckbox: boolean }
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the element's height in the tree, in pixels.
|
||||||
|
*/
|
||||||
|
public getHeight(tree: ITree, element: any): number {
|
||||||
|
return TreeComponentRenderer.DEFAULT_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a template ID for a given element.
|
||||||
|
*/
|
||||||
|
public getTemplateId(tree: ITree, element: any): string {
|
||||||
|
|
||||||
|
return TreeComponentRenderer.DEFAULT_TEMPLATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render template in a dom element based on template id
|
||||||
|
*/
|
||||||
|
public renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
|
||||||
|
|
||||||
|
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
|
||||||
|
const nodeTemplate: TreeDataTemplate = new TreeDataTemplate();
|
||||||
|
nodeTemplate.root = dom.append(container, dom.$('.tree-component-node-tile'));
|
||||||
|
nodeTemplate.icon = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-icon'));
|
||||||
|
if (this.options && this.options.withCheckbox) {
|
||||||
|
let checkboxWrapper = dom.append(nodeTemplate.root, dom.$('div.checkboxWrapper'));
|
||||||
|
nodeTemplate.checkbox = dom.append(checkboxWrapper, dom.$<HTMLInputElement>('input.checkbox', { type: 'checkbox' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeTemplate.label = dom.append(nodeTemplate.root, dom.$('div.model-view-tree-node-item-label'));
|
||||||
|
return nodeTemplate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render a element, given an object bag returned by the template
|
||||||
|
*/
|
||||||
|
public renderElement(tree: ITree, element: ITreeComponentItem, templateId: string, templateData: TreeDataTemplate): void {
|
||||||
|
const icon = this.themeService.getTheme().type === LIGHT ? element.icon : element.iconDark;
|
||||||
|
templateData.icon.style.backgroundImage = icon ? `url('${icon}')` : '';
|
||||||
|
dom.toggleClass(templateData.icon, 'model-view-tree-node-item-icon', !!icon);
|
||||||
|
if (element && !templateData.model) {
|
||||||
|
templateData.model = element;
|
||||||
|
}
|
||||||
|
if (templateId === TreeComponentRenderer.DEFAULT_TEMPLATE) {
|
||||||
|
this.renderNode(element, templateData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private renderNode(treeNode: ITreeComponentItem, templateData: TreeDataTemplate): void {
|
||||||
|
let label = treeNode.label;
|
||||||
|
templateData.label.textContent = label;
|
||||||
|
templateData.root.title = label;
|
||||||
|
templateData.checkboxState = this.getCheckboxState(treeNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCheckboxState(treeNode: ITreeComponentItem): TreeCheckboxState {
|
||||||
|
if (treeNode.checked === undefined) {
|
||||||
|
return TreeCheckboxState.Intermediate;
|
||||||
|
} else {
|
||||||
|
return treeNode.checked ? TreeCheckboxState.Checked : TreeCheckboxState.Unchecked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
|
||||||
|
this.dispose();
|
||||||
|
// no op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
57
src/sql/parts/modelComponents/tree/treeDataSource.ts
Normal file
57
src/sql/parts/modelComponents/tree/treeDataSource.ts
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { ITree, IDataSource } from 'vs/base/parts/tree/browser/tree';
|
||||||
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the DataSource(that returns a parent/children of an element) for the recent connection tree
|
||||||
|
*/
|
||||||
|
export class TreeComponentDataSource implements IDataSource {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private _dataProvider: IModelViewTreeViewDataProvider) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the unique identifier of the given element.
|
||||||
|
* No more than one element may use a given identifier.
|
||||||
|
*/
|
||||||
|
public getId(tree: ITree, node: ITreeComponentItem): string {
|
||||||
|
return node.handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a boolean value indicating whether the element has children.
|
||||||
|
*/
|
||||||
|
public hasChildren(tree: ITree, node: ITreeComponentItem): boolean {
|
||||||
|
return this._dataProvider !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the element's children as an array in a promise.
|
||||||
|
*/
|
||||||
|
public getChildren(tree: ITree, node: ITreeComponentItem): TPromise<any> {
|
||||||
|
if (this._dataProvider) {
|
||||||
|
if (node && node.handle === '0') {
|
||||||
|
return this._dataProvider.getChildren(undefined);
|
||||||
|
} else {
|
||||||
|
return this._dataProvider.getChildren(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TPromise.as([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getParent(tree: ITree, node: any): TPromise<any> {
|
||||||
|
return TPromise.as(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
39
src/sql/parts/modelComponents/tree/treeViewDataProvider.ts
Normal file
39
src/sql/parts/modelComponents/tree/treeViewDataProvider.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 { ExtHostModelViewTreeViewsShape, SqlExtHostContext } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
|
import { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
|
import { IModelViewTreeViewDataProvider, ITreeComponentItem } from 'sql/workbench/common/views';
|
||||||
|
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||||
|
import * as vsTreeView from 'vs/workbench/api/electron-browser/mainThreadTreeViews';
|
||||||
|
|
||||||
|
|
||||||
|
export class TreeViewDataProvider extends vsTreeView.TreeViewDataProvider implements IModelViewTreeViewDataProvider {
|
||||||
|
constructor(handle: number, treeViewId: string,
|
||||||
|
context: IExtHostContext,
|
||||||
|
notificationService?: INotificationService
|
||||||
|
) {
|
||||||
|
super(`${handle}-${treeViewId}`, context.getProxy(SqlExtHostContext.ExtHostModelViewTreeViews), notificationService);
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeCheckedChanged(treeViewId: string, treeItemHandle?: string, checked?: boolean) {
|
||||||
|
(<ExtHostModelViewTreeViewsShape>this._proxy).$onNodeCheckedChanged(treeViewId, treeItemHandle, checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected postGetChildren(elements: ITreeComponentItem[]): ITreeComponentItem[] {
|
||||||
|
const result = [];
|
||||||
|
if (elements) {
|
||||||
|
for (const element of elements) {
|
||||||
|
element.onCheckedChanged = (checked: boolean) => {
|
||||||
|
this.onNodeCheckedChanged(this.treeViewId, element.handle, checked);
|
||||||
|
};
|
||||||
|
this.itemsMap.set(element.handle, element);
|
||||||
|
result.push(element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||||||
import nls = require('vs/nls');
|
import nls = require('vs/nls');
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
import { IModelStore, IComponentDescriptor, IComponent, IComponentEventArgs } from './interfaces';
|
import { IModelStore, IComponentDescriptor, IComponent } from './interfaces';
|
||||||
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, ModelComponentTypes, IComponentShape } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService';
|
import { IModelView, IModelViewEventArgs } from 'sql/services/model/modelViewService';
|
||||||
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
import { Extensions, IComponentRegistry } from 'sql/platform/dashboard/common/modelComponentRegistry';
|
||||||
@@ -97,6 +97,10 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
this.queueAction(componentId, (component) => component.setProperties(properties));
|
this.queueAction(componentId, (component) => component.setProperties(properties));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshDataProvider(componentId: string, item: any): void {
|
||||||
|
this.queueAction(componentId, (component) => component.refreshDataProvider(item));
|
||||||
|
}
|
||||||
|
|
||||||
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
private queueAction<T>(componentId: string, action: (component: IComponent) => T): void {
|
||||||
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
|
this.modelStore.eventuallyRunOnComponent(componentId, action).catch(err => {
|
||||||
// TODO add error handling
|
// TODO add error handling
|
||||||
@@ -122,4 +126,8 @@ export abstract class ViewBase extends AngularDisposable implements IModelView {
|
|||||||
public validate(componentId: string): Thenable<boolean> {
|
public validate(componentId: string): Thenable<boolean> {
|
||||||
return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate())));
|
return new Promise(resolve => this.modelStore.eventuallyRunOnComponent(componentId, component => resolve(component.validate())));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setDataProvider(handle: number, componentId: string, context: any): any {
|
||||||
|
return this.queueAction(componentId, (component) => component.setDataProvider(handle, componentId, context));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,7 +22,7 @@ import { IBootstrapParams, ISelector, providerIterator } from 'sql/services/boot
|
|||||||
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
import { CommonServiceInterface } from 'sql/services/common/commonServiceInterface.service';
|
||||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||||
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox.component';
|
||||||
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editabledropdown.component';
|
import { EditableDropDown } from 'sql/base/browser/ui/editableDropdown/editableDropdown.component';
|
||||||
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
import { InputBox } from 'sql/base/browser/ui/inputBox/inputBox.component';
|
||||||
|
|
||||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ export interface IModelView extends IView {
|
|||||||
addToContainer(containerId: string, item: IItemConfig): void;
|
addToContainer(containerId: string, item: IItemConfig): void;
|
||||||
setLayout(componentId: string, layout: any): void;
|
setLayout(componentId: string, layout: any): void;
|
||||||
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
setProperties(componentId: string, properties: { [key: string]: any }): void;
|
||||||
|
setDataProvider(handle: number, componentId: string, context: any): void;
|
||||||
|
refreshDataProvider(componentId: string, item: any): void;
|
||||||
registerEvent(componentId: string);
|
registerEvent(componentId: string);
|
||||||
onEvent: Event<IModelViewEventArgs>;
|
onEvent: Event<IModelViewEventArgs>;
|
||||||
validate(componentId: string): Thenable<boolean>;
|
validate(componentId: string): Thenable<boolean>;
|
||||||
|
|||||||
22
src/sql/sqlops.proposed.d.ts
vendored
22
src/sql/sqlops.proposed.d.ts
vendored
@@ -27,6 +27,7 @@ declare module 'sqlops' {
|
|||||||
text(): ComponentBuilder<TextComponent>;
|
text(): ComponentBuilder<TextComponent>;
|
||||||
button(): ComponentBuilder<ButtonComponent>;
|
button(): ComponentBuilder<ButtonComponent>;
|
||||||
dropDown(): ComponentBuilder<DropDownComponent>;
|
dropDown(): ComponentBuilder<DropDownComponent>;
|
||||||
|
tree<T>(): ComponentBuilder<TreeComponent<T>>;
|
||||||
listBox(): ComponentBuilder<ListBoxComponent>;
|
listBox(): ComponentBuilder<ListBoxComponent>;
|
||||||
table(): ComponentBuilder<TableComponent>;
|
table(): ComponentBuilder<TableComponent>;
|
||||||
declarativeTable(): ComponentBuilder<DeclarativeTableComponent>;
|
declarativeTable(): ComponentBuilder<DeclarativeTableComponent>;
|
||||||
@@ -39,6 +40,17 @@ declare module 'sqlops' {
|
|||||||
fileBrowserTree(): ComponentBuilder<FileBrowserTreeComponent>;
|
fileBrowserTree(): ComponentBuilder<FileBrowserTreeComponent>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TreeComponentDataProvider<T> extends vscode.TreeDataProvider<T> {
|
||||||
|
getTreeItem(element: T): TreeComponentItem | Thenable<TreeComponentItem>;
|
||||||
|
|
||||||
|
onNodeCheckedChanged?(element: T, checked: boolean): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class TreeComponentItem extends vscode.TreeItem {
|
||||||
|
checked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ComponentBuilder<T extends Component> {
|
export interface ComponentBuilder<T extends Component> {
|
||||||
component(): T;
|
component(): T;
|
||||||
withProperties<U>(properties: U): ComponentBuilder<T>;
|
withProperties<U>(properties: U): ComponentBuilder<T>;
|
||||||
@@ -370,7 +382,7 @@ declare module 'sqlops' {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface TableColumn {
|
export interface TableColumn {
|
||||||
value: string
|
value: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TableComponentProperties extends ComponentProperties {
|
export interface TableComponentProperties extends ComponentProperties {
|
||||||
@@ -389,6 +401,10 @@ declare module 'sqlops' {
|
|||||||
label?: string;
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TreeProperties {
|
||||||
|
withCheckbox?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export enum DeclarativeDataType {
|
export enum DeclarativeDataType {
|
||||||
string = 'string',
|
string = 'string',
|
||||||
category = 'category',
|
category = 'category',
|
||||||
@@ -514,6 +530,10 @@ declare module 'sqlops' {
|
|||||||
onDidChange: vscode.Event<any>;
|
onDidChange: vscode.Event<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TreeComponent<T> extends Component, TreeProperties {
|
||||||
|
registerDataProvider<T>(dataProvider: TreeComponentDataProvider<T>): any;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WebViewComponent extends Component {
|
export interface WebViewComponent extends Component {
|
||||||
html: string;
|
html: string;
|
||||||
message: any;
|
message: any;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
import { TreeItem } from 'vs/workbench/api/node/extHostTypes';
|
||||||
|
|
||||||
// SQL added extension host types
|
// SQL added extension host types
|
||||||
export enum ServiceOptionType {
|
export enum ServiceOptionType {
|
||||||
string = 'string',
|
string = 'string',
|
||||||
@@ -146,6 +148,7 @@ export enum ModelComponentTypes {
|
|||||||
Group,
|
Group,
|
||||||
Toolbar,
|
Toolbar,
|
||||||
LoadingComponent,
|
LoadingComponent,
|
||||||
|
TreeComponent,
|
||||||
FileBrowserTree,
|
FileBrowserTree,
|
||||||
Editor
|
Editor
|
||||||
}
|
}
|
||||||
@@ -281,6 +284,11 @@ export enum CardType {
|
|||||||
VerticalButton = 'VerticalButton',
|
VerticalButton = 'VerticalButton',
|
||||||
Details = 'Details'
|
Details = 'Details'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class TreeComponentItem extends TreeItem {
|
||||||
|
checked?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export class SqlThemeIcon {
|
export class SqlThemeIcon {
|
||||||
|
|
||||||
static readonly Folder = new SqlThemeIcon('Folder');
|
static readonly Folder = new SqlThemeIcon('Folder');
|
||||||
|
|||||||
@@ -13,14 +13,18 @@ import * as nls from 'vs/nls';
|
|||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import * as sqlops from 'sqlops';
|
import * as sqlops from 'sqlops';
|
||||||
|
|
||||||
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
import { SqlMainContext, ExtHostModelViewShape, MainThreadModelViewShape, ExtHostModelViewTreeViewsShape } from 'sql/workbench/api/node/sqlExtHost.protocol';
|
||||||
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
import { IItemConfig, ModelComponentTypes, IComponentShape, IComponentEventArgs, ComponentEventType } from 'sql/workbench/api/common/sqlExtHostTypes';
|
||||||
|
|
||||||
class ModelBuilderImpl implements sqlops.ModelBuilder {
|
class ModelBuilderImpl implements sqlops.ModelBuilder {
|
||||||
private nextComponentId: number;
|
private nextComponentId: number;
|
||||||
private readonly _componentBuilders = new Map<string, ComponentBuilderImpl<any>>();
|
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;
|
this.nextComponentId = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +70,13 @@ class ModelBuilderImpl implements sqlops.ModelBuilder {
|
|||||||
return builder;
|
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> {
|
inputBox(): sqlops.ComponentBuilder<sqlops.InputBoxComponent> {
|
||||||
let id = this.getNextComponentId();
|
let id = this.getNextComponentId();
|
||||||
let builder: ComponentBuilderImpl<sqlops.InputBoxComponent> = this.getComponentBuilder(new InputBoxWrapper(this._proxy, this._handle, id), id);
|
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> {
|
protected async setProperty(key: string, value: any): Promise<void> {
|
||||||
if (!this.properties[key] || this.properties[key] !== value) {
|
if (!this.properties[key] || this.properties[key] !== value) {
|
||||||
// Only notify the front end if a value has been updated
|
// 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 {
|
class ModelViewImpl implements sqlops.ModelView {
|
||||||
|
|
||||||
public onClosedEmitter = new Emitter<any>();
|
public onClosedEmitter = new Emitter<any>();
|
||||||
@@ -1051,9 +1089,11 @@ class ModelViewImpl implements sqlops.ModelView {
|
|||||||
private readonly _proxy: MainThreadModelViewShape,
|
private readonly _proxy: MainThreadModelViewShape,
|
||||||
private readonly _handle: number,
|
private readonly _handle: number,
|
||||||
private readonly _connection: sqlops.connection.Connection,
|
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> {
|
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>();
|
private readonly _handlers = new Map<string, (view: sqlops.ModelView) => void>();
|
||||||
|
|
||||||
constructor(
|
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 {
|
$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 {
|
$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._modelViews.set(handle, view);
|
||||||
this._handlers.get(id)(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 { IExtHostContext } from 'vs/workbench/api/node/extHost.protocol';
|
||||||
import { Disposable } from 'vs/base/common/lifecycle';
|
import { Disposable } from 'vs/base/common/lifecycle';
|
||||||
|
|
||||||
import * as sqlops from 'sqlops';
|
|
||||||
|
|
||||||
import { IModelViewService } from 'sql/services/modelComponents/modelViewService';
|
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';
|
import { IModelView } from 'sql/services/model/modelViewService';
|
||||||
|
|
||||||
|
|
||||||
@@ -22,15 +21,14 @@ export class MainThreadModelView extends Disposable implements MainThreadModelVi
|
|||||||
private static _handlePool = 0;
|
private static _handlePool = 0;
|
||||||
private readonly _proxy: ExtHostModelViewShape;
|
private readonly _proxy: ExtHostModelViewShape;
|
||||||
private readonly _dialogs = new Map<number, IModelView>();
|
private readonly _dialogs = new Map<number, IModelView>();
|
||||||
|
|
||||||
private knownWidgets = new Array<string>();
|
private knownWidgets = new Array<string>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
context: IExtHostContext,
|
private _context: IExtHostContext,
|
||||||
@IModelViewService viewService: IModelViewService
|
@IModelViewService viewService: IModelViewService
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this._proxy = context.getProxy(SqlExtHostContext.ExtHostModelView);
|
this._proxy = _context.getProxy(SqlExtHostContext.ExtHostModelView);
|
||||||
viewService.onRegisteredModelView(view => {
|
viewService.onRegisteredModelView(view => {
|
||||||
if (this.knownWidgets.includes(view.id)) {
|
if (this.knownWidgets.includes(view.id)) {
|
||||||
let handle = MainThreadModelView._handlePool++;
|
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> {
|
$setProperties(handle: number, componentId: string, properties: { [key: string]: any; }): Thenable<void> {
|
||||||
return this.execModelViewAction(handle, (modelView) => modelView.setProperties(componentId, properties));
|
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 { ExtHostObjectExplorer } from 'sql/workbench/api/node/extHostObjectExplorer';
|
||||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||||
import { ExtHostModelViewDialog } from 'sql/workbench/api/node/extHostModelViewDialog';
|
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 { ExtHostQueryEditor } from 'sql/workbench/api/node/extHostQueryEditor';
|
||||||
import { ExtHostBackgroundTaskManagement } from './extHostBackgroundTaskManagement';
|
import { ExtHostBackgroundTaskManagement } from './extHostBackgroundTaskManagement';
|
||||||
|
|
||||||
@@ -66,7 +67,8 @@ export function createApiFactory(
|
|||||||
const extHostTasks = rpcProtocol.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(rpcProtocol, logService));
|
const extHostTasks = rpcProtocol.set(SqlExtHostContext.ExtHostTasks, new ExtHostTasks(rpcProtocol, logService));
|
||||||
const extHostBackgroundTaskManagement = rpcProtocol.set(SqlExtHostContext.ExtHostBackgroundTaskManagement, new ExtHostBackgroundTaskManagement(rpcProtocol));
|
const extHostBackgroundTaskManagement = rpcProtocol.set(SqlExtHostContext.ExtHostBackgroundTaskManagement, new ExtHostBackgroundTaskManagement(rpcProtocol));
|
||||||
const extHostWebviewWidgets = rpcProtocol.set(SqlExtHostContext.ExtHostDashboardWebviews, new ExtHostDashboardWebviews(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 extHostDashboard = rpcProtocol.set(SqlExtHostContext.ExtHostDashboard, new ExtHostDashboard(rpcProtocol));
|
||||||
const extHostModelViewDialog = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewDialog, new ExtHostModelViewDialog(rpcProtocol, extHostModelView, extHostBackgroundTaskManagement));
|
const extHostModelViewDialog = rpcProtocol.set(SqlExtHostContext.ExtHostModelViewDialog, new ExtHostModelViewDialog(rpcProtocol, extHostModelView, extHostBackgroundTaskManagement));
|
||||||
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
const extHostQueryEditor = rpcProtocol.set(SqlExtHostContext.ExtHostQueryEditor, new ExtHostQueryEditor(rpcProtocol));
|
||||||
@@ -415,7 +417,8 @@ export function createApiFactory(
|
|||||||
ui: ui,
|
ui: ui,
|
||||||
StatusIndicator: sqlExtHostTypes.StatusIndicator,
|
StatusIndicator: sqlExtHostTypes.StatusIndicator,
|
||||||
CardType: sqlExtHostTypes.CardType,
|
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 sqlops from 'sqlops';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
|
||||||
|
import { ITreeComponentItem } from 'sql/workbench/common/views';
|
||||||
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
|
import { ITaskHandlerDescription } from 'sql/platform/tasks/common/tasks';
|
||||||
import {
|
import {
|
||||||
IItemConfig, ModelComponentTypes, IComponentShape, IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails,
|
IItemConfig, ModelComponentTypes, IComponentShape, IModelViewDialogDetails, IModelViewTabDetails, IModelViewButtonDetails,
|
||||||
@@ -531,6 +532,7 @@ export const SqlExtHostContext = {
|
|||||||
ExtHostBackgroundTaskManagement: createExtId<ExtHostBackgroundTaskManagementShape>('ExtHostBackgroundTaskManagement'),
|
ExtHostBackgroundTaskManagement: createExtId<ExtHostBackgroundTaskManagementShape>('ExtHostBackgroundTaskManagement'),
|
||||||
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
|
ExtHostDashboardWebviews: createExtId<ExtHostDashboardWebviewsShape>('ExtHostDashboardWebviews'),
|
||||||
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
|
ExtHostModelView: createExtId<ExtHostModelViewShape>('ExtHostModelView'),
|
||||||
|
ExtHostModelViewTreeViews: createExtId<ExtHostModelViewTreeViewsShape>('ExtHostModelViewTreeViews'),
|
||||||
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard'),
|
ExtHostDashboard: createExtId<ExtHostDashboardShape>('ExtHostDashboard'),
|
||||||
ExtHostModelViewDialog: createExtId<ExtHostModelViewDialogShape>('ExtHostModelViewDialog'),
|
ExtHostModelViewDialog: createExtId<ExtHostModelViewDialogShape>('ExtHostModelViewDialog'),
|
||||||
ExtHostQueryEditor: createExtId<ExtHostQueryEditorShape>('ExtHostQueryEditor')
|
ExtHostQueryEditor: createExtId<ExtHostQueryEditorShape>('ExtHostQueryEditor')
|
||||||
@@ -590,6 +592,12 @@ export interface ExtHostModelViewShape {
|
|||||||
$runCustomValidations(handle: number, id: string): Thenable<boolean>;
|
$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 {
|
export interface ExtHostBackgroundTaskManagementShape {
|
||||||
$onTaskRegistered(operationId: string): void;
|
$onTaskRegistered(operationId: string): void;
|
||||||
$onTaskCanceled(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>;
|
$setProperties(handle: number, componentId: string, properties: { [key: string]: any }): Thenable<void>;
|
||||||
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
$registerEvent(handle: number, componentId: string): Thenable<void>;
|
||||||
$validate(handle: number, componentId: string): Thenable<boolean>;
|
$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 {
|
export interface ExtHostObjectExplorerShape {
|
||||||
|
|||||||
26
src/sql/workbench/common/views.ts
Normal file
26
src/sql/workbench/common/views.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import { TPromise } from 'vs/base/common/winjs.base';
|
||||||
|
import { Event } from 'vs/base/common/event';
|
||||||
|
import { ITreeViewDataProvider, ITreeItem } from 'vs/workbench/common/views';
|
||||||
|
|
||||||
|
export interface ITreeComponentItem extends ITreeItem {
|
||||||
|
checked?: boolean;
|
||||||
|
onCheckedChanged?: (checked: boolean) => void;
|
||||||
|
children?: ITreeComponentItem[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IModelViewTreeViewDataProvider extends ITreeViewDataProvider {
|
||||||
|
refresh(itemsToRefreshByHandle: { [treeItemHandle: string]: ITreeComponentItem });
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IModelViewTreeViewDataProvider {
|
||||||
|
onDidChange: Event<ITreeComponentItem[] | undefined | null>;
|
||||||
|
|
||||||
|
onDispose: Event<void>;
|
||||||
|
|
||||||
|
getChildren(element?: ITreeComponentItem): TPromise<ITreeComponentItem[]>;
|
||||||
|
}
|
||||||
@@ -47,7 +47,7 @@ suite('ExtHostModelView Validation Tests', () => {
|
|||||||
mockProxy.setup(x => x.$setProperties(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
|
mockProxy.setup(x => x.$setProperties(It.isAny(), It.isAny(), It.isAny())).returns(() => Promise.resolve());
|
||||||
|
|
||||||
// Register a model view of an input box and drop down box inside a form container inside a flex container
|
// Register a model view of an input box and drop down box inside a form container inside a flex container
|
||||||
extHostModelView = new ExtHostModelView(mainContext);
|
extHostModelView = new ExtHostModelView(mainContext, undefined);
|
||||||
extHostModelView.$registerProvider(widgetId, async view => {
|
extHostModelView.$registerProvider(widgetId, async view => {
|
||||||
modelView = view;
|
modelView = view;
|
||||||
inputBox = view.modelBuilder.inputBox()
|
inputBox = view.modelBuilder.inputBox()
|
||||||
|
|||||||
@@ -60,9 +60,11 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type TreeItemHandle = string;
|
// {{SQL CARBON EDIT}}
|
||||||
|
export type TreeItemHandle = string;
|
||||||
|
|
||||||
class TreeViewDataProvider implements ITreeViewDataProvider {
|
// {{SQL CARBON EDIT}}
|
||||||
|
export class TreeViewDataProvider implements ITreeViewDataProvider {
|
||||||
|
|
||||||
private readonly _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
|
private readonly _onDidChange: Emitter<ITreeItem[] | undefined | null> = new Emitter<ITreeItem[] | undefined | null>();
|
||||||
readonly onDidChange: Event<ITreeItem[] | undefined | null> = this._onDidChange.event;
|
readonly onDidChange: Event<ITreeItem[] | undefined | null> = this._onDidChange.event;
|
||||||
@@ -70,11 +72,13 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
|
|||||||
private readonly _onDispose: Emitter<void> = new Emitter<void>();
|
private readonly _onDispose: Emitter<void> = new Emitter<void>();
|
||||||
readonly onDispose: Event<void> = this._onDispose.event;
|
readonly onDispose: Event<void> = this._onDispose.event;
|
||||||
|
|
||||||
private itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected itemsMap: Map<TreeItemHandle, ITreeItem> = new Map<TreeItemHandle, ITreeItem>();
|
||||||
|
|
||||||
constructor(private treeViewId: string,
|
// {{SQL CARBON EDIT}}
|
||||||
private _proxy: ExtHostTreeViewsShape,
|
constructor(protected treeViewId: string,
|
||||||
private notificationService: INotificationService
|
protected _proxy: ExtHostTreeViewsShape,
|
||||||
|
protected notificationService: INotificationService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +125,8 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private postGetChildren(elements: ITreeItem[]): ITreeItem[] {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected postGetChildren(elements: ITreeItem[]): ITreeItem[] {
|
||||||
const result = [];
|
const result = [];
|
||||||
if (elements) {
|
if (elements) {
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ import { asWinJsPromise } from 'vs/base/common/async';
|
|||||||
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
|
import { TreeItemCollapsibleState, ThemeIcon } from 'vs/workbench/api/node/extHostTypes';
|
||||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||||
|
|
||||||
type TreeItemHandle = string;
|
// {{SQL CARBON EDIT}}
|
||||||
|
export type TreeItemHandle = string;
|
||||||
|
|
||||||
export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
||||||
|
|
||||||
@@ -79,24 +80,30 @@ export class ExtHostTreeViews implements ExtHostTreeViewsShape {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TreeNode {
|
// {{SQL CARBON EDIT}}
|
||||||
|
export interface TreeNode {
|
||||||
item: ITreeItem;
|
item: ITreeItem;
|
||||||
parent: TreeNode;
|
parent: TreeNode;
|
||||||
children: TreeNode[];
|
children: TreeNode[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class ExtHostTreeView<T> extends Disposable {
|
// {{SQL CARBON EDIT}}
|
||||||
|
export class ExtHostTreeView<T> extends Disposable {
|
||||||
|
|
||||||
private static LABEL_HANDLE_PREFIX = '0';
|
private static LABEL_HANDLE_PREFIX = '0';
|
||||||
private static ID_HANDLE_PREFIX = '1';
|
private static ID_HANDLE_PREFIX = '1';
|
||||||
|
|
||||||
private roots: TreeNode[] = null;
|
private roots: TreeNode[] = null;
|
||||||
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
|
private elements: Map<TreeItemHandle, T> = new Map<TreeItemHandle, T>();
|
||||||
private nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected nodes: Map<T, TreeNode> = new Map<T, TreeNode>();
|
||||||
|
|
||||||
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
|
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
|
||||||
super();
|
super();
|
||||||
|
// {{SQL CARBON EDIT}}
|
||||||
|
if (this.proxy) {
|
||||||
this.proxy.$registerTreeViewDataProvider(viewId);
|
this.proxy.$registerTreeViewDataProvider(viewId);
|
||||||
|
}
|
||||||
if (this.dataProvider.onDidChangeTreeData) {
|
if (this.dataProvider.onDidChangeTreeData) {
|
||||||
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
|
this._register(debounceEvent<T, T[]>(this.dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
|
||||||
}
|
}
|
||||||
@@ -127,7 +134,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), options)));
|
.then(treeNode => this.proxy.$reveal(this.viewId, treeNode.item, parentChain.map(p => p.item), options)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveUnknownParentChain(element: T): TPromise<TreeNode[]> {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected resolveUnknownParentChain(element: T): TPromise<TreeNode[]> {
|
||||||
return this.resolveParent(element)
|
return this.resolveParent(element)
|
||||||
.then((parent) => {
|
.then((parent) => {
|
||||||
if (!parent) {
|
if (!parent) {
|
||||||
@@ -150,7 +158,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
return asWinJsPromise(() => this.dataProvider.getParent(element));
|
return asWinJsPromise(() => this.dataProvider.getParent(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
private resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected resolveTreeNode(element: T, parent?: TreeNode): TPromise<TreeNode> {
|
||||||
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
|
return asWinJsPromise(() => this.dataProvider.getTreeItem(element))
|
||||||
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
|
.then(extTreeItem => this.createHandle(element, extTreeItem, parent, true))
|
||||||
.then(handle => this.getChildren(parent ? parent.item.handle : null)
|
.then(handle => this.getChildren(parent ? parent.item.handle : null)
|
||||||
@@ -194,7 +203,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
.then(nodes => nodes.filter(n => !!n));
|
.then(nodes => nodes.filter(n => !!n));
|
||||||
}
|
}
|
||||||
|
|
||||||
private refresh(elements: T[]): void {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected refresh(elements: T[]): void {
|
||||||
const hasRoot = elements.some(element => !element);
|
const hasRoot = elements.some(element => !element);
|
||||||
if (hasRoot) {
|
if (hasRoot) {
|
||||||
this.clearAll(); // clear cache
|
this.clearAll(); // clear cache
|
||||||
@@ -207,7 +217,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected getHandlesToRefresh(elements: T[]): TreeItemHandle[] {
|
||||||
const elementsToUpdate = new Set<TreeItemHandle>();
|
const elementsToUpdate = new Set<TreeItemHandle>();
|
||||||
for (const element of elements) {
|
for (const element of elements) {
|
||||||
let elementNode = this.nodes.get(element);
|
let elementNode = this.nodes.get(element);
|
||||||
@@ -237,7 +248,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
return handlesToUpdate;
|
return handlesToUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected refreshHandles(itemHandles: TreeItemHandle[]): TPromise<void> {
|
||||||
const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {};
|
const itemsToRefresh: { [treeItemHandle: string]: ITreeItem } = {};
|
||||||
return TPromise.join(itemHandles.map(treeItemHandle =>
|
return TPromise.join(itemHandles.map(treeItemHandle =>
|
||||||
this.refreshNode(treeItemHandle)
|
this.refreshNode(treeItemHandle)
|
||||||
@@ -249,7 +261,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
|
.then(() => Object.keys(itemsToRefresh).length ? this.proxy.$refresh(this.viewId, itemsToRefresh) : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private refreshNode(treeItemHandle: TreeItemHandle): TPromise<TreeNode> {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected refreshNode(treeItemHandle: TreeItemHandle): TPromise<TreeNode> {
|
||||||
const extElement = this.getExtensionElement(treeItemHandle);
|
const extElement = this.getExtensionElement(treeItemHandle);
|
||||||
const existing = this.nodes.get(extElement);
|
const existing = this.nodes.get(extElement);
|
||||||
this.clearChildren(extElement); // clear children cache
|
this.clearChildren(extElement); // clear children cache
|
||||||
@@ -274,7 +287,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected createTreeNode(element: T, extensionTreeItem: vscode.TreeItem, parent: TreeNode): TreeNode {
|
||||||
return {
|
return {
|
||||||
item: this.createTreeItem(element, extensionTreeItem, parent),
|
item: this.createTreeItem(element, extensionTreeItem, parent),
|
||||||
parent,
|
parent,
|
||||||
@@ -282,7 +296,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent?: TreeNode): ITreeItem {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected createTreeItem(element: T, extensionTreeItem: vscode.TreeItem, parent?: TreeNode): ITreeItem {
|
||||||
|
|
||||||
const handle = this.createHandle(element, extensionTreeItem, parent);
|
const handle = this.createHandle(element, extensionTreeItem, parent);
|
||||||
const icon = this.getLightIconPath(extensionTreeItem);
|
const icon = this.getLightIconPath(extensionTreeItem);
|
||||||
@@ -354,7 +369,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
this.nodes.set(element, node);
|
this.nodes.set(element, node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected updateNodeCache(element: T, newNode: TreeNode, existing: TreeNode, parentNode: TreeNode): void {
|
||||||
// Remove from the cache
|
// Remove from the cache
|
||||||
this.elements.delete(newNode.item.handle);
|
this.elements.delete(newNode.item.handle);
|
||||||
this.nodes.delete(element);
|
this.nodes.delete(element);
|
||||||
@@ -418,7 +434,8 @@ class ExtHostTreeView<T> extends Disposable {
|
|||||||
this.elements.delete(node.item.handle);
|
this.elements.delete(node.item.handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
private clearAll(): void {
|
// {{SQL CARBON EDIT}}
|
||||||
|
protected clearAll(): void {
|
||||||
this.roots = null;
|
this.roots = null;
|
||||||
this.elements.clear();
|
this.elements.clear();
|
||||||
this.nodes.clear();
|
this.nodes.clear();
|
||||||
|
|||||||
Reference in New Issue
Block a user