Path: blob/main/src/vs/workbench/contrib/preferences/browser/preferencesEditor.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 './media/preferencesEditor.css';6import * as DOM from '../../../../base/browser/dom.js';7import { localize } from '../../../../nls.js';8import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';9import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';10import { IStorageService } from '../../../../platform/storage/common/storage.js';11import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';12import { Event } from '../../../../base/common/event.js';13import { getInputBoxStyle } from '../../../../platform/theme/browser/defaultStyles.js';14import { IThemeService } from '../../../../platform/theme/common/themeService.js';15import { EditorPane } from '../../../browser/parts/editor/editorPane.js';16import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';17import { CONTEXT_PREFERENCES_SEARCH_FOCUS } from '../common/preferences.js';18import { settingsTextInputBorder } from '../common/settingsEditorColorRegistry.js';19import { SearchWidget } from './preferencesWidgets.js';20import { ActionBar, ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js';21import { Registry } from '../../../../platform/registry/common/platform.js';22import { IPreferencesEditorPaneRegistry, Extensions, IPreferencesEditorPaneDescriptor, IPreferencesEditorPane } from './preferencesEditorRegistry.js';23import { Action } from '../../../../base/common/actions.js';24import { CancellationToken } from '../../../../base/common/cancellation.js';25import { IEditorOptions } from '../../../../platform/editor/common/editor.js';26import { IEditorOpenContext } from '../../../common/editor.js';27import { EditorInput } from '../../../common/editor/editorInput.js';28import { MutableDisposable } from '../../../../base/common/lifecycle.js';2930class PreferenceTabAction extends Action {31constructor(readonly descriptor: IPreferencesEditorPaneDescriptor, actionCallback: () => void) {32super(descriptor.id, descriptor.title, '', true, actionCallback);33}34}3536export class PreferencesEditor extends EditorPane {3738static readonly ID: string = 'workbench.editor.preferences';3940private readonly editorPanesRegistry = Registry.as<IPreferencesEditorPaneRegistry>(Extensions.PreferencesEditorPane);4142private readonly element: HTMLElement;43private readonly bodyElement: HTMLElement;44private readonly searchWidget: SearchWidget;45private readonly preferencesTabActionBar: ActionBar;46private readonly preferencesTabActions: PreferenceTabAction[] = [];47private readonly preferencesEditorPane = this._register(new MutableDisposable<IPreferencesEditorPane>());4849private readonly searchFocusContextKey: IContextKey<boolean>;5051private dimension: DOM.Dimension | undefined;5253constructor(54group: IEditorGroup,55@ITelemetryService telemetryService: ITelemetryService,56@IThemeService themeService: IThemeService,57@IStorageService storageService: IStorageService,58@IInstantiationService private readonly instantiationService: IInstantiationService,59@IContextKeyService contextKeyService: IContextKeyService,60) {61super(PreferencesEditor.ID, group, telemetryService, themeService, storageService);6263this.searchFocusContextKey = CONTEXT_PREFERENCES_SEARCH_FOCUS.bindTo(contextKeyService);6465this.element = DOM.$('.preferences-editor');66const headerContainer = DOM.append(this.element, DOM.$('.preferences-editor-header'));6768const searchContainer = DOM.append(headerContainer, DOM.$('.search-container'));69this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, {70focusKey: this.searchFocusContextKey,71inputBoxStyles: getInputBoxStyle({72inputBorder: settingsTextInputBorder73})74}));75this._register(Event.debounce(this.searchWidget.onDidChange, () => undefined, 300)(() => {76this.preferencesEditorPane.value?.search(this.searchWidget.getValue());77}));7879const preferencesTabsContainer = DOM.append(headerContainer, DOM.$('.preferences-tabs-container'));80this.preferencesTabActionBar = this._register(new ActionBar(preferencesTabsContainer, {81orientation: ActionsOrientation.HORIZONTAL,82focusOnlyEnabledItems: true,83ariaLabel: localize('preferencesTabSwitcherBarAriaLabel', "Preferences Tab Switcher"),84ariaRole: 'tablist',85}));86this.onDidChangePreferencesEditorPane(this.editorPanesRegistry.getPreferencesEditorPanes(), []);87this._register(this.editorPanesRegistry.onDidRegisterPreferencesEditorPanes(descriptors => this.onDidChangePreferencesEditorPane(descriptors, [])));88this._register(this.editorPanesRegistry.onDidDeregisterPreferencesEditorPanes(descriptors => this.onDidChangePreferencesEditorPane([], descriptors)));8990this.bodyElement = DOM.append(this.element, DOM.$('.preferences-editor-body'));91}9293protected createEditor(parent: HTMLElement): void {94DOM.append(parent, this.element);95}9697layout(dimension: DOM.Dimension): void {98this.dimension = dimension;99this.searchWidget.layout(dimension);100this.searchWidget.inputBox.inputElement.style.paddingRight = `12px`;101102this.preferencesEditorPane.value?.layout(new DOM.Dimension(this.bodyElement.clientWidth, dimension.height - 87 /* header height */));103}104105override async setInput(input: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {106await super.setInput(input, options, context, token);107if (this.preferencesTabActions.length) {108this.onDidSelectPreferencesEditorPane(this.preferencesTabActions[0].id);109}110}111112private onDidChangePreferencesEditorPane(toAdd: readonly IPreferencesEditorPaneDescriptor[], toRemove: readonly IPreferencesEditorPaneDescriptor[]): void {113for (const desc of toRemove) {114const index = this.preferencesTabActions.findIndex(action => action.id === desc.id);115if (index !== -1) {116this.preferencesTabActionBar.pull(index);117this.preferencesTabActions[index].dispose();118this.preferencesTabActions.splice(index, 1);119}120}121if (toAdd.length > 0) {122const all = this.editorPanesRegistry.getPreferencesEditorPanes();123for (const desc of toAdd) {124const index = all.findIndex(action => action.id === desc.id);125if (index !== -1) {126const action = new PreferenceTabAction(desc, () => this.onDidSelectPreferencesEditorPane(desc.id));127this.preferencesTabActions.splice(index, 0, action);128this.preferencesTabActionBar.push(action, { index });129}130}131}132}133134private onDidSelectPreferencesEditorPane(id: string): void {135let selectedAction: PreferenceTabAction | undefined;136for (const action of this.preferencesTabActions) {137if (action.id === id) {138action.checked = true;139selectedAction = action;140} else {141action.checked = false;142}143}144145if (selectedAction) {146this.searchWidget.inputBox.setPlaceHolder(localize('FullTextSearchPlaceholder', "Search {0}", selectedAction.descriptor.title));147this.searchWidget.inputBox.setAriaLabel(localize('FullTextSearchPlaceholder', "Search {0}", selectedAction.descriptor.title));148}149150this.renderBody(selectedAction?.descriptor);151152if (this.dimension) {153this.layout(this.dimension);154}155}156157private renderBody(descriptor?: IPreferencesEditorPaneDescriptor): void {158this.preferencesEditorPane.value = undefined;159DOM.clearNode(this.bodyElement);160161if (descriptor) {162const editorPane = this.instantiationService.createInstance<IPreferencesEditorPane>(descriptor.ctorDescriptor.ctor);163this.preferencesEditorPane.value = editorPane;164this.bodyElement.appendChild(editorPane.getDomNode());165}166}167168override dispose(): void {169super.dispose();170this.preferencesTabActions.forEach(action => action.dispose());171}172}173174175176