Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts
3296 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import './media/preferencesEditor.css';
7
import * as DOM from '../../../../base/browser/dom.js';
8
import { localize } from '../../../../nls.js';
9
import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
10
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
11
import { IStorageService } from '../../../../platform/storage/common/storage.js';
12
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
13
import { Event } from '../../../../base/common/event.js';
14
import { getInputBoxStyle } from '../../../../platform/theme/browser/defaultStyles.js';
15
import { IThemeService } from '../../../../platform/theme/common/themeService.js';
16
import { EditorPane } from '../../../browser/parts/editor/editorPane.js';
17
import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js';
18
import { CONTEXT_PREFERENCES_SEARCH_FOCUS } from '../common/preferences.js';
19
import { settingsTextInputBorder } from '../common/settingsEditorColorRegistry.js';
20
import { SearchWidget } from './preferencesWidgets.js';
21
import { ActionBar, ActionsOrientation } from '../../../../base/browser/ui/actionbar/actionbar.js';
22
import { Registry } from '../../../../platform/registry/common/platform.js';
23
import { IPreferencesEditorPaneRegistry, Extensions, IPreferencesEditorPaneDescriptor, IPreferencesEditorPane } from './preferencesEditorRegistry.js';
24
import { Action } from '../../../../base/common/actions.js';
25
import { CancellationToken } from '../../../../base/common/cancellation.js';
26
import { IEditorOptions } from '../../../../platform/editor/common/editor.js';
27
import { IEditorOpenContext } from '../../../common/editor.js';
28
import { EditorInput } from '../../../common/editor/editorInput.js';
29
import { MutableDisposable } from '../../../../base/common/lifecycle.js';
30
31
class PreferenceTabAction extends Action {
32
constructor(readonly descriptor: IPreferencesEditorPaneDescriptor, actionCallback: () => void) {
33
super(descriptor.id, descriptor.title, '', true, actionCallback);
34
}
35
}
36
37
export class PreferencesEditor extends EditorPane {
38
39
static readonly ID: string = 'workbench.editor.preferences';
40
41
private readonly editorPanesRegistry = Registry.as<IPreferencesEditorPaneRegistry>(Extensions.PreferencesEditorPane);
42
43
private readonly element: HTMLElement;
44
private readonly bodyElement: HTMLElement;
45
private readonly searchWidget: SearchWidget;
46
private readonly preferencesTabActionBar: ActionBar;
47
private readonly preferencesTabActions: PreferenceTabAction[] = [];
48
private readonly preferencesEditorPane = this._register(new MutableDisposable<IPreferencesEditorPane>());
49
50
private readonly searchFocusContextKey: IContextKey<boolean>;
51
52
private dimension: DOM.Dimension | undefined;
53
54
constructor(
55
group: IEditorGroup,
56
@ITelemetryService telemetryService: ITelemetryService,
57
@IThemeService themeService: IThemeService,
58
@IStorageService storageService: IStorageService,
59
@IInstantiationService private readonly instantiationService: IInstantiationService,
60
@IContextKeyService contextKeyService: IContextKeyService,
61
) {
62
super(PreferencesEditor.ID, group, telemetryService, themeService, storageService);
63
64
this.searchFocusContextKey = CONTEXT_PREFERENCES_SEARCH_FOCUS.bindTo(contextKeyService);
65
66
this.element = DOM.$('.preferences-editor');
67
const headerContainer = DOM.append(this.element, DOM.$('.preferences-editor-header'));
68
69
const searchContainer = DOM.append(headerContainer, DOM.$('.search-container'));
70
this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, {
71
focusKey: this.searchFocusContextKey,
72
inputBoxStyles: getInputBoxStyle({
73
inputBorder: settingsTextInputBorder
74
})
75
}));
76
this._register(Event.debounce(this.searchWidget.onDidChange, () => undefined, 300)(() => {
77
this.preferencesEditorPane.value?.search(this.searchWidget.getValue());
78
}));
79
80
const preferencesTabsContainer = DOM.append(headerContainer, DOM.$('.preferences-tabs-container'));
81
this.preferencesTabActionBar = this._register(new ActionBar(preferencesTabsContainer, {
82
orientation: ActionsOrientation.HORIZONTAL,
83
focusOnlyEnabledItems: true,
84
ariaLabel: localize('preferencesTabSwitcherBarAriaLabel', "Preferences Tab Switcher"),
85
ariaRole: 'tablist',
86
}));
87
this.onDidChangePreferencesEditorPane(this.editorPanesRegistry.getPreferencesEditorPanes(), []);
88
this._register(this.editorPanesRegistry.onDidRegisterPreferencesEditorPanes(descriptors => this.onDidChangePreferencesEditorPane(descriptors, [])));
89
this._register(this.editorPanesRegistry.onDidDeregisterPreferencesEditorPanes(descriptors => this.onDidChangePreferencesEditorPane([], descriptors)));
90
91
this.bodyElement = DOM.append(this.element, DOM.$('.preferences-editor-body'));
92
}
93
94
protected createEditor(parent: HTMLElement): void {
95
DOM.append(parent, this.element);
96
}
97
98
layout(dimension: DOM.Dimension): void {
99
this.dimension = dimension;
100
this.searchWidget.layout(dimension);
101
this.searchWidget.inputBox.inputElement.style.paddingRight = `12px`;
102
103
this.preferencesEditorPane.value?.layout(new DOM.Dimension(this.bodyElement.clientWidth, dimension.height - 87 /* header height */));
104
}
105
106
override async setInput(input: EditorInput, options: IEditorOptions | undefined, context: IEditorOpenContext, token: CancellationToken): Promise<void> {
107
await super.setInput(input, options, context, token);
108
if (this.preferencesTabActions.length) {
109
this.onDidSelectPreferencesEditorPane(this.preferencesTabActions[0].id);
110
}
111
}
112
113
private onDidChangePreferencesEditorPane(toAdd: readonly IPreferencesEditorPaneDescriptor[], toRemove: readonly IPreferencesEditorPaneDescriptor[]): void {
114
for (const desc of toRemove) {
115
const index = this.preferencesTabActions.findIndex(action => action.id === desc.id);
116
if (index !== -1) {
117
this.preferencesTabActionBar.pull(index);
118
this.preferencesTabActions[index].dispose();
119
this.preferencesTabActions.splice(index, 1);
120
}
121
}
122
if (toAdd.length > 0) {
123
const all = this.editorPanesRegistry.getPreferencesEditorPanes();
124
for (const desc of toAdd) {
125
const index = all.findIndex(action => action.id === desc.id);
126
if (index !== -1) {
127
const action = new PreferenceTabAction(desc, () => this.onDidSelectPreferencesEditorPane(desc.id));
128
this.preferencesTabActions.splice(index, 0, action);
129
this.preferencesTabActionBar.push(action, { index });
130
}
131
}
132
}
133
}
134
135
private onDidSelectPreferencesEditorPane(id: string): void {
136
let selectedAction: PreferenceTabAction | undefined;
137
for (const action of this.preferencesTabActions) {
138
if (action.id === id) {
139
action.checked = true;
140
selectedAction = action;
141
} else {
142
action.checked = false;
143
}
144
}
145
146
if (selectedAction) {
147
this.searchWidget.inputBox.setPlaceHolder(localize('FullTextSearchPlaceholder', "Search {0}", selectedAction.descriptor.title));
148
this.searchWidget.inputBox.setAriaLabel(localize('FullTextSearchPlaceholder', "Search {0}", selectedAction.descriptor.title));
149
}
150
151
this.renderBody(selectedAction?.descriptor);
152
153
if (this.dimension) {
154
this.layout(this.dimension);
155
}
156
}
157
158
private renderBody(descriptor?: IPreferencesEditorPaneDescriptor): void {
159
this.preferencesEditorPane.value = undefined;
160
DOM.clearNode(this.bodyElement);
161
162
if (descriptor) {
163
const editorPane = this.instantiationService.createInstance<IPreferencesEditorPane>(descriptor.ctorDescriptor.ctor);
164
this.preferencesEditorPane.value = editorPane;
165
this.bodyElement.appendChild(editorPane.getDomNode());
166
}
167
}
168
169
override dispose(): void {
170
super.dispose();
171
this.preferencesTabActions.forEach(action => action.dispose());
172
}
173
}
174
175
176