Path: blob/main/src/vs/workbench/contrib/preferences/browser/tocTree.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import * as DOM from '../../../../base/browser/dom.js';6import * as domStylesheetsJs from '../../../../base/browser/domStylesheets.js';7import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js';8import { DefaultStyleController, IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js';9import { RenderIndentGuides } from '../../../../base/browser/ui/tree/abstractTree.js';10import { ITreeElement, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js';11import { Iterable } from '../../../../base/common/iterator.js';12import { DisposableStore } from '../../../../base/common/lifecycle.js';13import { localize } from '../../../../nls.js';14import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';15import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';16import { IHoverService } from '../../../../platform/hover/browser/hover.js';17import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';18import { IListService, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js';19import { getListStyles } from '../../../../platform/theme/browser/defaultStyles.js';20import { editorBackground, focusBorder } from '../../../../platform/theme/common/colorRegistry.js';21import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js';22import { settingsHeaderForeground, settingsHeaderHoverForeground } from '../common/settingsEditorColorRegistry.js';23import { SettingsTreeFilter } from './settingsTree.js';24import { ISettingsEditorViewState, SearchResultModel, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeSettingElement } from './settingsTreeModels.js';2526const $ = DOM.$;2728export class TOCTreeModel {2930private _currentSearchModel: SearchResultModel | null = null;31private _settingsTreeRoot!: SettingsTreeGroupElement;3233constructor(34private _viewState: ISettingsEditorViewState,35@IWorkbenchEnvironmentService private environmentService: IWorkbenchEnvironmentService36) {37}3839get settingsTreeRoot(): SettingsTreeGroupElement {40return this._settingsTreeRoot;41}4243set settingsTreeRoot(value: SettingsTreeGroupElement) {44this._settingsTreeRoot = value;45this.update();46}4748get currentSearchModel(): SearchResultModel | null {49return this._currentSearchModel;50}5152set currentSearchModel(model: SearchResultModel | null) {53this._currentSearchModel = model;54this.update();55}5657get children(): SettingsTreeElement[] {58return this._settingsTreeRoot.children;59}6061update(): void {62if (this._settingsTreeRoot) {63this.updateGroupCount(this._settingsTreeRoot);64}65}6667private updateGroupCount(group: SettingsTreeGroupElement): void {68group.children.forEach(child => {69if (child instanceof SettingsTreeGroupElement) {70this.updateGroupCount(child);71}72});7374const childCount = group.children75.filter(child => child instanceof SettingsTreeGroupElement)76.reduce((acc, cur) => acc + (<SettingsTreeGroupElement>cur).count!, 0);7778group.count = childCount + this.getGroupCount(group);79}8081private getGroupCount(group: SettingsTreeGroupElement): number {82return group.children.filter(child => {83if (!(child instanceof SettingsTreeSettingElement)) {84return false;85}8687if (this._currentSearchModel && !this._currentSearchModel.root.containsSetting(child.setting.key)) {88return false;89}9091// Check everything that the SettingsFilter checks except whether it's filtered by a category92const isRemote = !!this.environmentService.remoteAuthority;93return child.matchesScope(this._viewState.settingsTarget, isRemote) &&94child.matchesAllTags(this._viewState.tagFilters) &&95child.matchesAnyFeature(this._viewState.featureFilters) &&96child.matchesAnyExtension(this._viewState.extensionFilters) &&97child.matchesAnyId(this._viewState.idFilters);98}).length;99}100}101102const TOC_ENTRY_TEMPLATE_ID = 'settings.toc.entry';103104interface ITOCEntryTemplate {105labelElement: HTMLElement;106countElement: HTMLElement;107elementDisposables: DisposableStore;108}109110export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, never, ITOCEntryTemplate> {111112templateId = TOC_ENTRY_TEMPLATE_ID;113114constructor(private readonly _hoverService: IHoverService) {115}116117renderTemplate(container: HTMLElement): ITOCEntryTemplate {118return {119labelElement: DOM.append(container, $('.settings-toc-entry')),120countElement: DOM.append(container, $('.settings-toc-count')),121elementDisposables: new DisposableStore()122};123}124125renderElement(node: ITreeNode<SettingsTreeGroupElement>, index: number, template: ITOCEntryTemplate): void {126template.elementDisposables.clear();127128const element = node.element;129const count = element.count;130const label = element.label;131132template.labelElement.textContent = label;133template.elementDisposables.add(this._hoverService.setupDelayedHover(template.labelElement, { content: label }));134135if (count) {136template.countElement.textContent = ` (${count})`;137} else {138template.countElement.textContent = '';139}140}141142disposeTemplate(templateData: ITOCEntryTemplate): void {143templateData.elementDisposables.dispose();144}145}146147class TOCTreeDelegate implements IListVirtualDelegate<SettingsTreeElement> {148getTemplateId(element: SettingsTreeElement): string {149return TOC_ENTRY_TEMPLATE_ID;150}151152getHeight(element: SettingsTreeElement): number {153return 22;154}155}156157export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterable<ITreeElement<SettingsTreeGroupElement>> {158const groupChildren = <SettingsTreeGroupElement[]>model.children.filter(c => c instanceof SettingsTreeGroupElement);159160return Iterable.map(groupChildren, g => {161const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement);162163return {164element: g,165collapsed: undefined,166collapsible: hasGroupChildren,167children: g instanceof SettingsTreeGroupElement ?168createTOCIterator(g, tree) :169undefined170};171});172}173174class SettingsAccessibilityProvider implements IListAccessibilityProvider<SettingsTreeGroupElement> {175getWidgetAriaLabel(): string {176return localize({177key: 'settingsTOC',178comment: ['A label for the table of contents for the full settings list']179},180"Settings Table of Contents");181}182183getAriaLabel(element: SettingsTreeElement): string {184if (!element) {185return '';186}187188if (element instanceof SettingsTreeGroupElement) {189return localize('groupRowAriaLabel', "{0}, group", element.label);190}191192return '';193}194195getAriaLevel(element: SettingsTreeGroupElement): number {196let i = 1;197while (element instanceof SettingsTreeGroupElement && element.parent) {198i++;199element = element.parent;200}201202return i;203}204}205206export class TOCTree extends WorkbenchObjectTree<SettingsTreeGroupElement> {207constructor(208container: HTMLElement,209viewState: ISettingsEditorViewState,210@IContextKeyService contextKeyService: IContextKeyService,211@IListService listService: IListService,212@IConfigurationService configurationService: IConfigurationService,213@IHoverService hoverService: IHoverService,214@IInstantiationService instantiationService: IInstantiationService,215) {216// test open mode217218const filter = instantiationService.createInstance(SettingsTreeFilter, viewState);219const options: IWorkbenchObjectTreeOptions<SettingsTreeGroupElement, void> = {220filter,221multipleSelectionSupport: false,222identityProvider: {223getId(e) {224return e.id;225}226},227styleController: id => new DefaultStyleController(domStylesheetsJs.createStyleSheet(container), id),228accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider),229collapseByDefault: true,230horizontalScrolling: false,231hideTwistiesOfChildlessElements: true,232renderIndentGuides: RenderIndentGuides.None233};234235super(236'SettingsTOC',237container,238new TOCTreeDelegate(),239[new TOCRenderer(hoverService)],240options,241instantiationService,242contextKeyService,243listService,244configurationService,245);246247this.style(getListStyles({248listBackground: editorBackground,249listFocusOutline: focusBorder,250listActiveSelectionBackground: editorBackground,251listActiveSelectionForeground: settingsHeaderForeground,252listFocusAndSelectionBackground: editorBackground,253listFocusAndSelectionForeground: settingsHeaderForeground,254listFocusBackground: editorBackground,255listFocusForeground: settingsHeaderHoverForeground,256listHoverForeground: settingsHeaderHoverForeground,257listHoverBackground: editorBackground,258listInactiveSelectionBackground: editorBackground,259listInactiveSelectionForeground: settingsHeaderForeground,260listInactiveFocusBackground: editorBackground,261listInactiveFocusOutline: editorBackground,262treeIndentGuidesStroke: undefined,263treeInactiveIndentGuidesStroke: undefined264}));265}266}267268269