提交 125ac04d 编写于 作者: S Sandeep Somavarapu

Refactor custom views:

- Custom view service to get tree item viewer
- Custom view service to register the data provider
上级 abf0a5bf
......@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
import { forEach } from 'vs/base/common/collections';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import { ExtensionMessageCollector, ExtensionsRegistry } from 'vs/platform/extensions/common/extensionsRegistry';
import { ViewLocation, ViewsRegistry, IViewDescriptor } from 'vs/workbench/common/views';
import { ViewLocation, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views';
import { CustomTreeViewPanel } from 'vs/workbench/browser/parts/views/customView';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { coalesce, } from 'vs/base/common/arrays';
......@@ -102,14 +102,15 @@ ExtensionsRegistry.registerExtensionPoint<{ [loc: string]: schema.IUserFriendlyV
const registeredViews = ViewsRegistry.getViews(location);
const viewIds = [];
const viewDescriptors = coalesce(entry.value.map(item => {
const viewDescriptor = <IViewDescriptor>{
const viewDescriptor = <ICustomViewDescriptor>{
id: item.id,
name: item.name,
ctor: CustomTreeViewPanel,
location,
when: ContextKeyExpr.deserialize(item.when),
canToggleVisibility: true,
collapsed: true
collapsed: true,
treeItemView: true
};
// validate
......
......@@ -9,7 +9,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { Disposable } from 'vs/base/common/lifecycle';
import { ExtHostContext, MainThreadTreeViewsShape, ExtHostTreeViewsShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { IMessageService, Severity } from 'vs/platform/message/common/message';
import { ViewsRegistry, ITreeViewDataProvider, ITreeItem } from 'vs/workbench/common/views';
import { ITreeViewDataProvider, ITreeItem, ICustomViewsService } from 'vs/workbench/common/views';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { assign } from 'vs/base/common/objects';
......@@ -20,27 +20,24 @@ export class MainThreadTreeViews extends Disposable implements MainThreadTreeVie
constructor(
extHostContext: IExtHostContext,
@ICustomViewsService private viewsService: ICustomViewsService,
@IMessageService private messageService: IMessageService
) {
super();
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTreeViews);
}
$registerView(treeViewId: string): void {
ViewsRegistry.registerTreeViewDataProvider(treeViewId, this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.messageService)));
$registerTreeViewDataProvider(treeViewId: string): void {
const dataProvider = this._register(new TreeViewDataProvider(treeViewId, this._proxy, this.messageService));
this.viewsService.registerTreeViewDataProvider(treeViewId, dataProvider);
}
$refresh(treeViewId: string, itemsToRefresh: { [treeItemHandle: string]: ITreeItem }): void {
const treeViewDataProvider: TreeViewDataProvider = <TreeViewDataProvider>ViewsRegistry.getTreeViewDataProvider(treeViewId);
if (treeViewDataProvider) {
treeViewDataProvider.refresh(itemsToRefresh);
const treeViewer = this.viewsService.getTreeItemViewer(treeViewId);
if (treeViewer && treeViewer.dataProvider) {
(<TreeViewDataProvider>treeViewer.dataProvider).refresh(itemsToRefresh);
}
}
dispose(): void {
ViewsRegistry.deregisterTreeViewDataProviders();
super.dispose();
}
}
type TreeItemHandle = string;
......@@ -111,10 +108,6 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
}
}
dispose(): void {
this._onDispose.fire();
}
private postGetElements(elements: ITreeItem[]): ITreeItem[] {
const result = [];
if (elements) {
......@@ -139,4 +132,8 @@ class TreeViewDataProvider implements ITreeViewDataProvider {
assign(current, treeItem);
}
}
dispose(): void {
this._onDispose.fire();
}
}
......@@ -214,7 +214,7 @@ export interface MainThreadEditorsShape extends IDisposable {
}
export interface MainThreadTreeViewsShape extends IDisposable {
$registerView(treeViewId: string): void;
$registerTreeViewDataProvider(treeViewId: string): void;
$refresh(treeViewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void;
}
......
......@@ -87,7 +87,7 @@ class ExtHostTreeView<T> extends Disposable {
constructor(private viewId: string, private dataProvider: vscode.TreeDataProvider<T>, private proxy: MainThreadTreeViewsShape, private commands: CommandsConverter) {
super();
this.proxy.$registerView(viewId);
this.proxy.$registerTreeViewDataProvider(viewId);
if (dataProvider.onDidChangeTreeData) {
this._register(debounceEvent<T, T[]>(dataProvider.onDidChangeTreeData, (last, current) => last ? [...last, current] : [current], 200)(elements => this.refresh(elements)));
}
......
......@@ -22,8 +22,7 @@ import { IProgressService } from 'vs/platform/progress/common/progress';
import { ITree, IDataSource, IRenderer, ContextMenuEvent } from 'vs/base/parts/tree/browser/tree';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { ActionItem, ActionBar, IActionItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { ViewsRegistry, TreeItemCollapsibleState, ITreeItem, ITreeViewDataProvider, TreeViewItemHandleArg } from 'vs/workbench/common/views';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
import { TreeItemCollapsibleState, ITreeItem, TreeViewItemHandleArg, ITreeItemViewer, ICustomViewsService, ITreeViewDataProvider, ViewsRegistry, ICustomViewDescriptor } from 'vs/workbench/common/views';
import { IViewletViewOptions, IViewOptions, FileIconThemableWorkbenchTree, ViewsViewletPanel } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService';
......@@ -33,8 +32,48 @@ import { basename } from 'vs/base/common/paths';
import { FileKind } from 'vs/platform/files/common/files';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
export class CustomViewsService implements ICustomViewsService {
class TreeViewer extends Disposable {
_serviceBrand: any;
private viewers: Map<string, ITreeItemViewer> = new Map<string, ITreeItemViewer>();
constructor(
@IInstantiationService private instantiationService: IInstantiationService
) {
}
getTreeItemViewer(id: string): ITreeItemViewer {
let viewer = this.viewers.get(id);
if (!viewer) {
viewer = this.createViewer(id);
if (viewer) {
this.viewers.set(id, viewer);
}
}
return viewer;
}
registerTreeViewDataProvider(id: string, dataProvider: ITreeViewDataProvider): void {
const treeViewer = this.getTreeItemViewer(id);
if (treeViewer) {
treeViewer.dataProvider = dataProvider;
dataProvider.onDispose(() => treeViewer.dataProvider = null);
}
}
private createViewer(id: string): ITreeItemViewer {
const viewDeescriptor = <ICustomViewDescriptor>ViewsRegistry.getView(id);
if (viewDeescriptor && viewDeescriptor.treeItemView) {
return this.instantiationService.createInstance(TreeItemViewer, id);
}
return null;
}
}
export class TreeItemViewer extends Disposable implements ITreeItemViewer {
private isVisible: boolean = false;
private activated: boolean = false;
......@@ -43,6 +82,7 @@ class TreeViewer extends Disposable {
private treeInputPromise: TPromise<void>;
private elementsToRefresh: ITreeItem[] = [];
private _dataProvider: ITreeViewDataProvider;
private dataProviderElementChangeListener: IDisposable;
constructor(
......@@ -52,6 +92,37 @@ class TreeViewer extends Disposable {
super();
}
get dataProvider(): ITreeViewDataProvider {
return this._dataProvider;
}
set dataProvider(dataProvider: ITreeViewDataProvider) {
this._dataProvider = dataProvider;
if (this.dataProviderElementChangeListener) {
this.dataProviderElementChangeListener.dispose();
}
if (this.dataProvider) {
this.dataProviderElementChangeListener = this._register(this.dataProvider.onDidChange(element => this.refresh(element)));
this.refresh(null);
}
}
refresh(elements: ITreeItem[]): TPromise<void> {
if (this.tree) {
if (!elements) {
const root: ITreeItem = this.tree.getInput();
root.children = null; // reset children
elements = [root];
}
if (this.isVisible) {
return this.doRefresh(elements);
} else {
this.elementsToRefresh.push(...elements);
}
}
return TPromise.as(null);
}
setTree(tree: ITree): void {
this.tree = tree;
this.setInput();
......@@ -118,61 +189,15 @@ class TreeViewer extends Disposable {
private setInput(): TPromise<void> {
if (this.tree) {
if (!this.treeInputPromise) {
if (this.listenToDataProvider()) {
this.treeInputPromise = this.tree.setInput(new Root());
} else {
this.treeInputPromise = new TPromise<void>((c, e) => {
this._register(ViewsRegistry.onTreeViewDataProviderRegistered(id => {
if (this.id === id) {
if (this.listenToDataProvider()) {
this.tree.setInput(new Root()).then(() => c(null));
}
}
}));
});
}
this.treeInputPromise = this.tree.setInput(new Root());
}
return this.treeInputPromise;
}
return TPromise.as(null);
}
private listenToDataProvider(): boolean {
let dataProvider = ViewsRegistry.getTreeViewDataProvider(this.id);
if (dataProvider) {
if (this.dataProviderElementChangeListener) {
this.dataProviderElementChangeListener.dispose();
}
this.dataProviderElementChangeListener = this._register(dataProvider.onDidChange(element => this.refresh(element)));
const disposable = dataProvider.onDispose(() => {
this.dataProviderElementChangeListener.dispose();
this.tree.setInput(new Root());
disposable.dispose();
});
return true;
}
return false;
}
private refresh(elements: ITreeItem[]): void {
if (this.tree) {
if (!elements) {
const root: ITreeItem = this.tree.getInput();
root.children = null; // reset children
elements = [root];
}
if (this.isVisible) {
this.doRefresh(elements);
} else {
this.elementsToRefresh.push(...elements);
}
}
}
private doRefresh(elements: ITreeItem[]): void {
for (const element of elements) {
this.tree.refresh(element);
}
private doRefresh(elements: ITreeItem[]): TPromise<void> {
return TPromise.join(elements.map(e => this.tree.refresh(e))) as TPromise;
}
}
......@@ -181,7 +206,7 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
private menus: Menus;
private treeContainer: HTMLElement;
private tree: WorkbenchTree;
private treeViewer: TreeViewer;
private treeViewer: TreeItemViewer;
constructor(
options: IViewletViewOptions,
......@@ -191,12 +216,13 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
@IInstantiationService private instantiationService: IInstantiationService,
@IThemeService private themeService: IWorkbenchThemeService,
@ICommandService private commandService: ICommandService,
@IConfigurationService configurationService: IConfigurationService
@IConfigurationService configurationService: IConfigurationService,
@ICustomViewsService customViewsService: ICustomViewsService,
) {
super({ ...(options as IViewOptions), ariaHeaderLabel: options.name }, keybindingService, contextMenuService, configurationService);
this.menus = this.instantiationService.createInstance(Menus, this.id);
this.menus.onDidChangeTitle(() => this.updateActions(), this, this.disposables);
this.treeViewer = this.instantiationService.createInstance(TreeViewer, this.id);
this.treeViewer = <TreeItemViewer>customViewsService.getTreeItemViewer(this.id);
this.disposables.push(this.treeViewer);
this.updateTreeVisibility();
}
......@@ -213,7 +239,7 @@ export class CustomTreeViewPanel extends ViewsViewletPanel {
renderBody(container: HTMLElement): void {
this.treeContainer = DOM.append(container, DOM.$('.tree-explorer-viewlet-tree-view'));
const actionItemProvider = (action: IAction) => this.getActionItem(action);
const dataSource = this.instantiationService.createInstance(TreeDataSource, this.id);
const dataSource = this.instantiationService.createInstance(TreeDataSource, this.treeViewer);
const renderer = this.instantiationService.createInstance(TreeRenderer, this.id, this.menus, actionItemProvider);
const controller = this.instantiationService.createInstance(TreeController, this.id, this.menus);
this.tree = this.instantiationService.createInstance(FileIconThemableWorkbenchTree,
......@@ -301,7 +327,7 @@ class Root implements ITreeItem {
class TreeDataSource implements IDataSource {
constructor(
private id: string,
private treeItemViewer: ITreeItemViewer,
@IProgressService private progressService: IProgressService
) {
}
......@@ -311,7 +337,7 @@ class TreeDataSource implements IDataSource {
}
public hasChildren(tree: ITree, node: ITreeItem): boolean {
if (!this.getDataProvider()) {
if (!this.treeItemViewer.dataProvider) {
return false;
}
return node.collapsibleState === TreeItemCollapsibleState.Collapsed || node.collapsibleState === TreeItemCollapsibleState.Expanded;
......@@ -322,9 +348,8 @@ class TreeDataSource implements IDataSource {
return TPromise.as(node.children);
}
const dataProvider = this.getDataProvider();
if (dataProvider) {
const promise = node instanceof Root ? dataProvider.getElements() : dataProvider.getChildren(node);
if (this.treeItemViewer.dataProvider) {
const promise = node instanceof Root ? this.treeItemViewer.dataProvider.getElements() : this.treeItemViewer.dataProvider.getChildren(node);
this.progressService.showWhile(promise, 100);
return promise.then(children => {
node.children = children;
......@@ -342,10 +367,6 @@ class TreeDataSource implements IDataSource {
public getParent(tree: ITree, node: any): TPromise<any> {
return TPromise.as(null);
}
private getDataProvider(): ITreeViewDataProvider {
return ViewsRegistry.getTreeViewDataProvider(this.id);
}
}
interface ITreeExplorerTemplateData {
......
......@@ -11,6 +11,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { ITreeViewDataProvider } from 'vs/workbench/common/views';
import { localize } from 'vs/nls';
import { IViewlet } from 'vs/workbench/common/viewlet';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export class ViewLocation {
......@@ -62,19 +63,13 @@ export interface IViewsRegistry {
readonly onViewsDeregistered: Event<IViewDescriptor[]>;
readonly onTreeViewDataProviderRegistered: Event<string>;
registerViews(views: IViewDescriptor[]): void;
deregisterViews(ids: string[], location: ViewLocation): void;
registerTreeViewDataProvider(id: string, factory: ITreeViewDataProvider): void;
deregisterTreeViewDataProviders(): void;
getViews(loc: ViewLocation): IViewDescriptor[];
getTreeViewDataProvider(id: string): ITreeViewDataProvider;
getView(id: string): IViewDescriptor;
}
......@@ -86,11 +81,8 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
private _onViewsDeregistered: Emitter<IViewDescriptor[]> = new Emitter<IViewDescriptor[]>();
readonly onViewsDeregistered: Event<IViewDescriptor[]> = this._onViewsDeregistered.event;
private _onTreeViewDataProviderRegistered: Emitter<string> = new Emitter<string>();
readonly onTreeViewDataProviderRegistered: Event<string> = this._onTreeViewDataProviderRegistered.event;
private _viewLocations: ViewLocation[] = [];
private _views: Map<ViewLocation, IViewDescriptor[]> = new Map<ViewLocation, IViewDescriptor[]>();
private _treeViewDataPoviders: Map<string, ITreeViewDataProvider> = new Map<string, ITreeViewDataProvider>();
registerViews(viewDescriptors: IViewDescriptor[]): void {
if (viewDescriptors.length) {
......@@ -99,6 +91,7 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
if (!views) {
views = [];
this._views.set(viewDescriptor.location, views);
this._viewLocations.push(viewDescriptor.location);
}
if (views.some(v => v.id === viewDescriptor.id)) {
throw new Error(localize('duplicateId', "A view with id `{0}` is already registered in the location `{1}`", viewDescriptor.id, viewDescriptor.location.id));
......@@ -119,36 +112,30 @@ export const ViewsRegistry: IViewsRegistry = new class implements IViewsRegistry
const viewsToDeregister = views.filter(view => ids.indexOf(view.id) !== -1);
if (viewsToDeregister.length) {
this._views.set(location, views.filter(view => ids.indexOf(view.id) === -1));
const remaningViews = views.filter(view => ids.indexOf(view.id) === -1);
if (remaningViews.length) {
this._views.set(location, remaningViews);
} else {
this._views.delete(location);
this._viewLocations.splice(this._viewLocations.indexOf(location), 1);
}
}
this._onViewsDeregistered.fire(viewsToDeregister);
}
registerTreeViewDataProvider(id: string, factory: ITreeViewDataProvider) {
if (!this.isDataProviderRegistered(id)) {
// TODO: throw error
}
this._treeViewDataPoviders.set(id, factory);
this._onTreeViewDataProviderRegistered.fire(id);
}
deregisterTreeViewDataProviders(): void {
this._treeViewDataPoviders.clear();
}
getViews(loc: ViewLocation): IViewDescriptor[] {
return this._views.get(loc) || [];
}
getTreeViewDataProvider(id: string): ITreeViewDataProvider {
return this._treeViewDataPoviders.get(id);
}
private isDataProviderRegistered(id: string): boolean {
let registered = false;
this._views.forEach(views => registered = registered || views.some(view => view.id === id));
return registered;
getView(id: string): IViewDescriptor {
for (const viewLocation of this._viewLocations) {
const viewDescriptor = (this._views.get(viewLocation) || []).filter(v => v.id === id)[0];
if (viewDescriptor) {
return viewDescriptor;
}
}
return null;
}
};
......@@ -158,7 +145,31 @@ export interface IViewsViewlet extends IViewlet {
}
// Custom view
// Custom views
export interface ITreeItemViewer {
dataProvider: ITreeViewDataProvider;
refresh(treeItems: ITreeItem[]): TPromise<void>;
}
export interface ICustomViewDescriptor extends IViewDescriptor {
treeItemView?: boolean;
}
export const ICustomViewsService = createDecorator<ICustomViewsService>('customViewsService');
export interface ICustomViewsService {
_serviceBrand: any;
getTreeItemViewer(id: string): ITreeItemViewer;
registerTreeViewDataProvider(id: string, ITreeViewDataProvider): void;
}
export type TreeViewItemHandleArg = {
$treeViewId: string,
......
......@@ -97,6 +97,8 @@ import URI from 'vs/base/common/uri';
import { IListService, ListService } from 'vs/platform/list/browser/listService';
import { domEvent } from 'vs/base/browser/event';
import { InputFocusedContext } from 'vs/platform/workbench/common/contextkeys';
import { ICustomViewsService } from 'vs/workbench/common/views';
import { CustomViewsService } from 'vs/workbench/browser/parts/views/customView';
export const MessagesVisibleContext = new RawContextKey<boolean>('globalMessageVisible', false);
export const EditorsVisibleContext = new RawContextKey<boolean>('editorIsOpen', false);
......@@ -541,6 +543,10 @@ export class Workbench implements IPartService {
this.toUnbind.push({ dispose: () => this.panelPart.shutdown() });
serviceCollection.set(IPanelService, this.panelPart);
// Custom views service
const customViewsService = this.instantiationService.createInstance(CustomViewsService);
serviceCollection.set(ICustomViewsService, customViewsService);
// Activity service (activitybar part)
this.activitybarPart = this.instantiationService.createInstance(ActivitybarPart, Identifiers.ACTIVITYBAR_PART);
this.toUnbind.push({ dispose: () => this.activitybarPart.shutdown() });
......
......@@ -27,7 +27,7 @@ suite('ExtHostTreeView', function () {
onRefresh = new Emitter<{ [treeItemHandle: string]: ITreeItem }>();
$registerView(treeViewId: string): void {
$registerTreeViewDataProvider(treeViewId: string): void {
}
$refresh(viewId: string, itemsToRefresh?: { [treeItemHandle: string]: ITreeItem }): void {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册