From 1dc65e064d90622abd0feb959bceedc530924a91 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 2 Jan 2019 20:40:30 -0800 Subject: [PATCH] Use new tree for settings TOC tree --- .../parts/preferences/browser/settingsTree.ts | 69 +--------- .../parts/preferences/browser/tocTree.ts | 129 +++++++----------- .../media/settingsEditor2.css | 20 +-- .../electron-browser/settingsEditor2.ts | 60 ++++---- 4 files changed, 94 insertions(+), 184 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 4f64c52040e..d3e40177a61 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -17,7 +17,7 @@ import { ISelectOptionItem, SelectBox } from 'vs/base/browser/ui/selectBox/selec import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar'; import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; -import { ITreeModel, ITreeNode, ITreeRenderer, ITreeFilter, TreeVisibility, TreeFilterResult } from 'vs/base/browser/ui/tree/tree'; +import { ITreeFilter, ITreeModel, ITreeNode, ITreeRenderer, TreeFilterResult, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { Action, IAction } from 'vs/base/common/actions'; import * as arrays from 'vs/base/common/arrays'; import { Color, RGBA } from 'vs/base/common/color'; @@ -28,7 +28,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { ISpliceable } from 'vs/base/common/sequence'; import { escapeRegExpCharacters, startsWith } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; -import { ITree, IFilter, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree'; +import { IAccessibilityProvider, ITree } from 'vs/base/parts/tree/browser/tree'; import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -1139,66 +1139,7 @@ function escapeInvisibleChars(enumValue: string): string { .replace(/\r/g, '\\r'); } -export class SettingsTreeFilter implements IFilter { - constructor( - private viewState: ISettingsEditorViewState, - ) { } - - isVisible(tree: ITree, element: SettingsTreeElement): boolean { - // Filter during search - if (this.viewState.filterToCategory && element instanceof SettingsTreeSettingElement) { - if (!this.settingContainedInGroup(element.setting, this.viewState.filterToCategory)) { - return false; - } - } - - // Non-user scope selected - if (element instanceof SettingsTreeSettingElement && this.viewState.settingsTarget !== ConfigurationTarget.USER) { - if (!element.matchesScope(this.viewState.settingsTarget)) { - return false; - } - } - - // @modified or tag - if (element instanceof SettingsTreeSettingElement && this.viewState.tagFilters) { - if (!element.matchesAllTags(this.viewState.tagFilters)) { - return false; - } - } - - // Group with no visible children - if (element instanceof SettingsTreeGroupElement) { - if (typeof element.count === 'number') { - return element.count > 0; - } - - return element.children.some(child => this.isVisible(tree, child)); - } - - // Filtered "new extensions" button - if (element instanceof SettingsTreeNewExtensionsElement) { - if ((this.viewState.tagFilters && this.viewState.tagFilters.size) || this.viewState.filterToCategory) { - return false; - } - } - - return true; - } - - private settingContainedInGroup(setting: ISetting, group: SettingsTreeGroupElement): boolean { - return group.children.some(child => { - if (child instanceof SettingsTreeGroupElement) { - return this.settingContainedInGroup(setting, child); - } else if (child instanceof SettingsTreeSettingElement) { - return child.setting.key === setting.key; - } else { - return false; - } - }); - } -} - -export class SettingsTreeFilter2 implements ITreeFilter { +export class SettingsTreeFilter implements ITreeFilter { constructor( private viewState: ISettingsEditorViewState, ) { } @@ -1362,7 +1303,7 @@ export class SettingsTree extends ObjectTree { return e.id; } }, - filter: new SettingsTreeFilter2(viewState) + filter: new SettingsTreeFilter(viewState) }); this.disposables = []; @@ -1370,7 +1311,7 @@ export class SettingsTree extends ObjectTree { const activeBorderColor = theme.getColor(focusBorder); if (activeBorderColor) { // TODO@rob - why isn't this applied when added to the stylesheet from tocTree.ts? Seems like a chromium glitch. - collector.addRule(`.settings-editor > .settings-body > .settings-toc-container .monaco-tree:focus .monaco-tree-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); + collector.addRule(`.settings-editor > .settings-body > .settings-toc-container .monaco-list:focus .monaco-list-row.focused {outline: solid 1px ${activeBorderColor}; outline-offset: -1px; }`); } const foregroundColor = theme.getColor(foreground); diff --git a/src/vs/workbench/parts/preferences/browser/tocTree.ts b/src/vs/workbench/parts/preferences/browser/tocTree.ts index 97fb24420a7..4d7d8797ec4 100644 --- a/src/vs/workbench/parts/preferences/browser/tocTree.ts +++ b/src/vs/workbench/parts/preferences/browser/tocTree.ts @@ -4,17 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as DOM from 'vs/base/browser/dom'; -import { ScrollbarVisibility } from 'vs/base/common/scrollable'; -import { IDataSource, IRenderer, ITree, ITreeConfiguration, ITreeOptions } from 'vs/base/parts/tree/browser/tree'; -import { DefaultTreestyler } from 'vs/base/parts/tree/browser/treeDefaults'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; +import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; +import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; +import { Iterator } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IListService, WorkbenchTree, WorkbenchTreeController } from 'vs/platform/list/browser/listService'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { SettingsAccessibilityProvider, SettingsTreeFilter } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { SettingsTreeFilter } from 'vs/workbench/parts/preferences/browser/settingsTree'; import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { settingsHeaderForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; @@ -82,43 +80,6 @@ export class TOCTreeModel { } } -export type TOCTreeElement = SettingsTreeGroupElement | TOCTreeModel; - -export class TOCDataSource implements IDataSource { - constructor(private _treeFilter: SettingsTreeFilter) { - } - - getId(tree: ITree, element: SettingsTreeGroupElement): string { - return element.id; - } - - hasChildren(tree: ITree, element: TOCTreeElement): boolean { - if (element instanceof TOCTreeModel) { - return true; - } - - if (element instanceof SettingsTreeGroupElement) { - // Should have child which won't be filtered out - return element.children && element.children.some(child => child instanceof SettingsTreeGroupElement && this._treeFilter.isVisible(tree, child)); - } - - return false; - } - - getChildren(tree: ITree, element: TOCTreeElement): Promise { - return Promise.resolve(this._getChildren(element)); - } - - private _getChildren(element: TOCTreeElement): SettingsTreeElement[] { - return (element.children) - .filter(child => child instanceof SettingsTreeGroupElement); - } - - getParent(tree: ITree, element: TOCTreeElement): Promise { - return Promise.resolve(element instanceof SettingsTreeGroupElement && element.parent); - } -} - const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry'; interface ITOCEntryTemplate { @@ -126,23 +87,19 @@ interface ITOCEntryTemplate { countElement: HTMLElement; } -export class TOCRenderer implements IRenderer { - getHeight(tree: ITree, element: SettingsTreeElement): number { - return 22; - } +export class TOCRenderer implements ITreeRenderer { - getTemplateId(tree: ITree, element: SettingsTreeElement): string { - return TOC_ENTRY_TEMPLATE_ID; - } + templateId = TOC_ENTRY_TEMPLATE_ID; - renderTemplate(tree: ITree, templateId: string, container: HTMLElement): ITOCEntryTemplate { + renderTemplate(container: HTMLElement): ITOCEntryTemplate { return { labelElement: DOM.append(container, $('.settings-toc-entry')), countElement: DOM.append(container, $('.settings-toc-count')) }; } - renderElement(tree: ITree, element: SettingsTreeGroupElement, templateId: string, template: ITOCEntryTemplate): void { + renderElement(node: ITreeNode, index: number, template: ITOCEntryTemplate): void { + const element = node.element; const count = element.count; const label = element.label; @@ -156,49 +113,59 @@ export class TOCRenderer implements IRenderer { } } - disposeTemplate(tree: ITree, templateId: string, templateData: any): void { + disposeTemplate(templateData: ITOCEntryTemplate): void { + } +} + +class TOCTreeDelegate implements IListVirtualDelegate { + getTemplateId(element: SettingsTreeElement): string { + return TOC_ENTRY_TEMPLATE_ID; + } + + getHeight(element: SettingsTreeElement): number { + return 22; } } -export class TOCTree extends WorkbenchTree { +export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement): Iterator> { + const groupChildren = model.children.filter(c => c instanceof SettingsTreeGroupElement); + const groupsIt = Iterator.fromArray(groupChildren); + + return Iterator.map(groupsIt, g => { + return { + element: g, + children: g instanceof SettingsTreeGroupElement ? + createTOCIterator(g) : + undefined + }; + }); +} + +export class TOCTree extends ObjectTree { constructor( container: HTMLElement, viewState: ISettingsEditorViewState, - configuration: Partial, - @IContextKeyService contextKeyService: IContextKeyService, - @IListService listService: IListService, @IThemeService themeService: IThemeService, - @IInstantiationService instantiationService: IInstantiationService, - @IConfigurationService configurationService: IConfigurationService + @IInstantiationService instantiationService: IInstantiationService ) { - const treeClass = 'settings-toc-tree'; + // test open mode const filter = instantiationService.createInstance(SettingsTreeFilter, viewState); - const fullConfiguration = { - controller: instantiationService.createInstance(WorkbenchTreeController, {}), + const options: IObjectTreeOptions = { filter, - styler: new DefaultTreestyler(DOM.createStyleSheet(container), treeClass), - dataSource: instantiationService.createInstance(TOCDataSource, filter), - accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), - - ...configuration - }; - - const options: ITreeOptions = { - showLoading: false, - twistiePixels: 15, - horizontalScrollMode: ScrollbarVisibility.Hidden + identityProvider: { + getId(e) { + return e.id; + } + } }; super(container, - fullConfiguration, - options, - contextKeyService, - listService, - themeService, - instantiationService, - configurationService); + new TOCTreeDelegate(), + [new TOCRenderer()], + options); + const treeClass = 'settings-toc-tree'; this.getHTMLElement().classList.add(treeClass); this.disposables.push(attachStyler(themeService, { diff --git a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css index 5e165b5b319..47ca9d0586a 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/electron-browser/media/settingsEditor2.css @@ -199,7 +199,7 @@ position: absolute; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree { +.settings-editor > .settings-body .settings-toc-container .monaco-list { width: 160px; pointer-events: initial; } @@ -213,11 +213,11 @@ display: none; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .content { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row .monaco-tl-contents { display: flex; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row .settings-toc-entry { overflow: hidden; text-overflow: ellipsis; line-height: 22px; @@ -225,30 +225,30 @@ flex-shrink: 1; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-count { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: none; line-height: 22px; opacity: 0.7; margin-left: 3px; } -.settings-editor.search-mode > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-count { +.settings-editor.search-mode > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: block; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.has-children > .content::before { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row.has-children > .content::before { opacity: 0.9; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.has-children.selected > .content::before { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row.has-children.selected > .content::before { opacity: 1; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-entry.no-results { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row .settings-toc-entry.no-results { opacity: 0.5; } -.settings-editor > .settings-body .settings-toc-container .monaco-tree-row.selected .settings-toc-entry { +.settings-editor > .settings-body .settings-toc-container .monaco-list-row.selected .settings-toc-entry { font-weight: bold; opacity: 1; } @@ -535,6 +535,6 @@ padding-top: 7px; } -.settings-editor.search-mode > .settings-body .settings-toc-container .monaco-tree-row .settings-toc-count { +.settings-editor.search-mode > .settings-body .settings-toc-container .monaco-list-row .settings-toc-count { display: block; } \ No newline at end of file diff --git a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts index 5ce685aa3d9..05faed9d956 100644 --- a/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts @@ -13,14 +13,12 @@ import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; import { Iterator } from 'vs/base/common/iterator'; import { isArray } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { collapseAll, expandAll } from 'vs/base/parts/tree/browser/treeUtils'; import 'vs/css!./media/settingsEditor2'; import { localize } from 'vs/nls'; import { ConfigurationTarget, ConfigurationTargetToString, IConfigurationOverrides, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { WorkbenchTree } from 'vs/platform/list/browser/listService'; import { ILogService } from 'vs/platform/log/common/log'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -36,7 +34,7 @@ import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browse import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/parts/preferences/browser/settingsTree'; import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { settingsTextInputBorder } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; -import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; +import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences'; import { IEditorGroupsService } from 'vs/workbench/services/group/common/editorGroupsService'; import { IPreferencesService, ISearchResult, ISettingsEditorModel, ISettingsEditorOptions, SettingsEditorOptions, SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; @@ -98,7 +96,7 @@ export class SettingsEditor2 extends BaseEditor { private clearFilterLinkContainer: HTMLElement; private tocTreeContainer: HTMLElement; - private tocTree: WorkbenchTree; + private tocTree: TOCTree; private settingsAriaExtraLabelsContainer: HTMLElement; @@ -552,24 +550,26 @@ export class SettingsEditor2 extends BaseEditor { this.tocTreeModel = new TOCTreeModel(this.viewState); this.tocTreeContainer = DOM.append(parent, $('.settings-toc-container')); - const tocRenderer = this.instantiationService.createInstance(TOCRenderer); - this.tocTree = this._register(this.instantiationService.createInstance(TOCTree, DOM.append(this.tocTreeContainer, $('.settings-toc-wrapper')), - this.viewState, - { - renderer: tocRenderer - })); + this.viewState)); this._register(this.tocTree.onDidChangeFocus(e => { - const element: SettingsTreeGroupElement = e.focus; + this.tocTree.setSelection(e.elements); + const element: SettingsTreeGroupElement = e.elements[0]; if (this.searchResultModel) { - this.viewState.filterToCategory = element; - this.renderTree(); - this.settingsTree.scrollTop = 0; - } else if (element && (!e.payload || !e.payload.fromScroll)) { + if (this.viewState.filterToCategory !== element) { + this.viewState.filterToCategory = element; + this.renderTree(); + this.settingsTree.scrollTop = 0; + } + } else if (element) { this.settingsTree.reveal(element, 0); } + + // else if (element && (!e.payload || !e.payload.fromScroll)) { + // this.settingsTree.reveal(element, 0); + // } })); this._register(this.tocTree.onDidFocus(() => { @@ -655,7 +655,7 @@ export class SettingsEditor2 extends BaseEditor { return; } - if (!this.tocTree.getInput()) { + if (!this.tocTreeModel) { return; } @@ -679,8 +679,8 @@ export class SettingsEditor2 extends BaseEditor { // this.tocTree.expand(element); - this.tocTree.setSelection([element]); - this.tocTree.setFocus(element, { fromScroll: true }); + // this.tocTree.setSelection([element]); + // this.tocTree.setFocus(element, { fromScroll: true }); } } @@ -870,11 +870,8 @@ export class SettingsEditor2 extends BaseEditor { this.refreshTree(); this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement; - if (this.tocTree.getInput()) { - this.tocTree.refresh(); - } else { - this.tocTree.setInput(this.tocTreeModel); - } + this.refreshTOCTree(); + this.tocTree.collapseAll(); } return Promise.resolve(void 0); @@ -943,13 +940,18 @@ export class SettingsEditor2 extends BaseEditor { } this.tocTreeModel.update(); - return this.tocTree.refresh(); + this.refreshTOCTree(); + return Promise.resolve(undefined); } private refreshTree(): void { this.settingsTree.setChildren(null, createGroupIterator(this.currentSettingsModel.root)); } + private refreshTOCTree(): void { + this.tocTree.setChildren(null, createTOCIterator(this.tocTreeModel)); + } + private updateModifiedLabelForKey(key: string): void { const dataElements = this.currentSettingsModel.getElementsByName(key); const isModified = dataElements && dataElements[0] && dataElements[0].isConfigured; // all elements are either configured or not @@ -1002,18 +1004,18 @@ export class SettingsEditor2 extends BaseEditor { this.viewState.filterToCategory = null; this.tocTreeModel.currentSearchModel = this.searchResultModel; - this.tocTree.refresh(); + this.refreshTOCTree(); this.onSearchModeToggled(); if (this.searchResultModel) { // Added a filter model this.tocTree.setSelection([]); - expandAll(this.tocTree); + this.tocTree.expandAll(); this.refreshTree(); this.renderResultCountMessages(); } else { // Leaving search mode - collapseAll(this.tocTree); + this.tocTree.collapseAll(); this.refreshTree(); this.renderResultCountMessages(); } @@ -1145,7 +1147,7 @@ export class SettingsEditor2 extends BaseEditor { this.tocTree.setSelection([]); this.viewState.filterToCategory = null; - expandAll(this.tocTree); + this.tocTree.expandAll(); return this.renderTree().then(() => result); }); @@ -1205,7 +1207,7 @@ export class SettingsEditor2 extends BaseEditor { const tocTreeHeight = listHeight - 16; this.tocTreeContainer.style.height = `${tocTreeHeight}px`; - this.tocTree.layout(tocTreeHeight, 175); + this.tocTree.layout(tocTreeHeight); } protected saveState(): void { -- GitLab