Skip to content

Commit ddb50a0

Browse files
authored
Refactor indicators to use uniform interface (microsoft#165960)
* Refactor indicators to use uniform interface * Fix styling
1 parent 7c96708 commit ddb50a0

File tree

2 files changed

+96
-71
lines changed

2 files changed

+96
-71
lines changed

src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -379,9 +379,7 @@
379379
opacity: 0.9;
380380
}
381381

382-
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title > .misc-label .setting-item-overrides.with-custom-hover:hover,
383-
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title > .misc-label .setting-item-ignored:hover,
384-
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title > .misc-label .setting-item-default-overridden:hover {
382+
.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title > .misc-label .setting-indicator:hover {
385383
text-decoration: underline;
386384
}
387385

src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts

Lines changed: 95 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,25 @@ export interface ISettingOverrideClickEvent {
3131
settingKey: string;
3232
}
3333

34+
interface SettingIndicator {
35+
element: HTMLElement;
36+
label: SimpleIconLabel;
37+
hover: MutableDisposable<ICustomHover>;
38+
}
39+
3440
/**
3541
* Renders the indicators next to a setting, such as "Also Modified In".
3642
*/
3743
export class SettingsTreeIndicatorsLabel implements IDisposable {
3844
private indicatorsContainerElement: HTMLElement;
39-
private workspaceTrustElement: HTMLElement;
40-
private scopeOverridesElement: HTMLElement;
41-
private scopeOverridesLabel: SimpleIconLabel;
42-
private scopeOverridesHover: MutableDisposable<ICustomHover>;
43-
private syncIgnoredElement: HTMLElement;
44-
private defaultOverrideIndicatorElement: HTMLElement;
4545
private hoverDelegate: IHoverDelegate;
46-
private profilesEnabled: boolean;
4746

48-
// Holds hovers that contain a fixed message.
49-
private simpleHoverStore: DisposableStore;
47+
private workspaceTrustIndicator: SettingIndicator;
48+
private scopeOverridesIndicator: SettingIndicator;
49+
private syncIgnoredIndicator: SettingIndicator;
50+
private defaultOverrideIndicator: SettingIndicator;
51+
52+
private profilesEnabled: boolean;
5053

5154
constructor(
5255
container: HTMLElement,
@@ -68,19 +71,16 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
6871
placement: 'element'
6972
};
7073

71-
this.simpleHoverStore = new DisposableStore();
72-
const scopeOverridesIndicator = this.createScopeOverridesIndicator();
73-
this.workspaceTrustElement = this.createWorkspaceTrustElement();
74-
this.scopeOverridesElement = scopeOverridesIndicator.element;
75-
this.scopeOverridesLabel = scopeOverridesIndicator.label;
76-
this.syncIgnoredElement = this.createSyncIgnoredElement();
77-
this.defaultOverrideIndicatorElement = this.createDefaultOverrideIndicator();
78-
this.scopeOverridesHover = new MutableDisposable();
7974
this.profilesEnabled = this.userDataProfilesService.isEnabled();
75+
76+
this.workspaceTrustIndicator = this.createWorkspaceTrustIndicator();
77+
this.scopeOverridesIndicator = this.createScopeOverridesIndicator();
78+
this.syncIgnoredIndicator = this.createSyncIgnoredIndicator();
79+
this.defaultOverrideIndicator = this.createDefaultOverrideIndicator();
8080
}
8181

82-
private createWorkspaceTrustElement(): HTMLElement {
83-
const workspaceTrustElement = $('span.setting-item-workspace-trust');
82+
private createWorkspaceTrustIndicator(): SettingIndicator {
83+
const workspaceTrustElement = $('span.setting-indicator.setting-item-workspace-trust');
8484
const workspaceTrustLabel = new SimpleIconLabel(workspaceTrustElement);
8585
workspaceTrustLabel.text = '$(warning) ' + localize('workspaceUntrustedLabel', "Setting value not applied");
8686
const contentFallback = localize('trustLabel', "The setting value can only be applied in a trusted workspace.");
@@ -94,65 +94,86 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
9494
},
9595
markdownNotSupportedFallback: contentFallback
9696
};
97+
98+
const hover = new MutableDisposable<ICustomHover>();
9799
const options: IUpdatableHoverOptions = {
98100
linkHandler: (url: string) => {
99101
this.commandService.executeCommand('workbench.trust.manage');
100-
this.scopeOverridesHover.value?.hide();
102+
hover.value?.hide();
101103
}
102104
};
103-
104-
this.simpleHoverStore.add(setupCustomHover(this.hoverDelegate, workspaceTrustElement, content, options));
105-
return workspaceTrustElement;
105+
hover.value = setupCustomHover(this.hoverDelegate, workspaceTrustElement, content, options);
106+
return {
107+
element: workspaceTrustElement,
108+
label: workspaceTrustLabel,
109+
hover
110+
};
106111
}
107112

108-
private createScopeOverridesIndicator(): { element: HTMLElement; label: SimpleIconLabel } {
113+
private createScopeOverridesIndicator(): SettingIndicator {
114+
// Don't add .setting-indicator class here, because it gets conditionally added later.
109115
const otherOverridesElement = $('span.setting-item-overrides');
110116
const otherOverridesLabel = new SimpleIconLabel(otherOverridesElement);
111-
return { element: otherOverridesElement, label: otherOverridesLabel };
117+
return {
118+
element: otherOverridesElement,
119+
label: otherOverridesLabel,
120+
hover: new MutableDisposable<ICustomHover>()
121+
};
112122
}
113123

114-
private createSyncIgnoredElement(): HTMLElement {
115-
const syncIgnoredElement = $('span.setting-item-ignored');
124+
private createSyncIgnoredIndicator(): SettingIndicator {
125+
const syncIgnoredElement = $('span.setting-indicator.setting-item-ignored');
116126
const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement);
117127
syncIgnoredLabel.text = localize('extensionSyncIgnoredLabel', 'Not synced');
128+
118129
const syncIgnoredHoverContent = localize('syncIgnoredTitle', "This setting is ignored during sync");
119-
this.simpleHoverStore.add(setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent));
120-
return syncIgnoredElement;
130+
const hover = new MutableDisposable<ICustomHover>();
131+
hover.value = setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent);
132+
return {
133+
element: syncIgnoredElement,
134+
label: syncIgnoredLabel,
135+
hover
136+
};
121137
}
122138

123-
private createDefaultOverrideIndicator(): HTMLElement {
124-
const defaultOverrideIndicator = $('span.setting-item-default-overridden');
139+
private createDefaultOverrideIndicator(): SettingIndicator {
140+
const defaultOverrideIndicator = $('span.setting-indicator.setting-item-default-overridden');
125141
const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator);
126142
defaultOverrideLabel.text = localize('defaultOverriddenLabel', "Default value changed");
127-
return defaultOverrideIndicator;
143+
144+
return {
145+
element: defaultOverrideIndicator,
146+
label: defaultOverrideLabel,
147+
hover: new MutableDisposable<ICustomHover>()
148+
};
128149
}
129150

130151
private render() {
131-
const elementsToShow = [this.workspaceTrustElement, this.scopeOverridesElement, this.syncIgnoredElement, this.defaultOverrideIndicatorElement].filter(element => {
132-
return element.style.display !== 'none';
152+
const indicatorsToShow = [this.workspaceTrustIndicator, this.scopeOverridesIndicator, this.syncIgnoredIndicator, this.defaultOverrideIndicator].filter(indicator => {
153+
return indicator.element.style.display !== 'none';
133154
});
134155

135156
this.indicatorsContainerElement.innerText = '';
136157
this.indicatorsContainerElement.style.display = 'none';
137-
if (elementsToShow.length) {
158+
if (indicatorsToShow.length) {
138159
this.indicatorsContainerElement.style.display = 'inline';
139160
DOM.append(this.indicatorsContainerElement, $('span', undefined, '('));
140-
for (let i = 0; i < elementsToShow.length - 1; i++) {
141-
DOM.append(this.indicatorsContainerElement, elementsToShow[i]);
161+
for (let i = 0; i < indicatorsToShow.length - 1; i++) {
162+
DOM.append(this.indicatorsContainerElement, indicatorsToShow[i].element);
142163
DOM.append(this.indicatorsContainerElement, $('span.comma', undefined, ' • '));
143164
}
144-
DOM.append(this.indicatorsContainerElement, elementsToShow[elementsToShow.length - 1]);
165+
DOM.append(this.indicatorsContainerElement, indicatorsToShow[indicatorsToShow.length - 1].element);
145166
DOM.append(this.indicatorsContainerElement, $('span', undefined, ')'));
146167
}
147168
}
148169

149170
updateWorkspaceTrust(element: SettingsTreeSettingElement) {
150-
this.workspaceTrustElement.style.display = element.isUntrusted ? 'inline' : 'none';
171+
this.workspaceTrustIndicator.element.style.display = element.isUntrusted ? 'inline' : 'none';
151172
this.render();
152173
}
153174

154175
updateSyncIgnored(element: SettingsTreeSettingElement, ignoredSettings: string[]) {
155-
this.syncIgnoredElement.style.display = this.userDataSyncEnablementService.isEnabled()
176+
this.syncIgnoredIndicator.element.style.display = this.userDataSyncEnablementService.isEnabled()
156177
&& ignoredSettings.includes(element.setting.key) ? 'inline' : 'none';
157178
this.render();
158179
}
@@ -169,19 +190,22 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
169190
}
170191

171192
dispose() {
172-
this.scopeOverridesHover.dispose();
173-
this.simpleHoverStore.dispose();
193+
const indicators = [this.workspaceTrustIndicator, this.scopeOverridesIndicator,
194+
this.syncIgnoredIndicator, this.defaultOverrideIndicator];
195+
for (const indicator of indicators) {
196+
indicator.hover.dispose();
197+
}
174198
}
175199

176200
updateScopeOverrides(element: SettingsTreeSettingElement, elementDisposables: DisposableStore, onDidClickOverrideElement: Emitter<ISettingOverrideClickEvent>, onApplyFilter: Emitter<string>) {
177-
this.scopeOverridesElement.innerText = '';
178-
this.scopeOverridesElement.style.display = 'none';
201+
this.scopeOverridesIndicator.element.innerText = '';
202+
this.scopeOverridesIndicator.element.style.display = 'none';
179203
if (element.hasPolicyValue) {
180204
// If the setting falls under a policy, then no matter what the user sets, the policy value takes effect.
181-
this.scopeOverridesElement.style.display = 'inline';
182-
this.scopeOverridesElement.classList.add('with-custom-hover');
205+
this.scopeOverridesIndicator.element.style.display = 'inline';
206+
this.scopeOverridesIndicator.element.classList.add('setting-indicator');
183207

184-
this.scopeOverridesLabel.text = '$(warning) ' + localize('policyLabelText', "Setting value not applied");
208+
this.scopeOverridesIndicator.label.text = '$(warning) ' + localize('policyLabelText', "Setting value not applied");
185209
const contentFallback = localize('policyDescription', "This setting is managed by your organization and its applied value cannot be changed.");
186210
const contentMarkdownString = contentFallback + ` [${localize('policyFilterLink', "View policy settings")}](policy-settings).`;
187211
const content: ITooltipMarkdownString = {
@@ -193,43 +217,45 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
193217
markdownNotSupportedFallback: contentFallback
194218
};
195219
const options: IUpdatableHoverOptions = {
196-
linkHandler: (url: string) => {
220+
linkHandler: _ => {
197221
onApplyFilter.fire(`@${POLICY_SETTING_TAG}`);
198-
this.scopeOverridesHover.value?.hide();
222+
this.scopeOverridesIndicator.hover.value?.hide();
199223
}
200224
};
201-
this.scopeOverridesHover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesElement, content, options);
225+
this.scopeOverridesIndicator.hover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesIndicator.element, content, options);
226+
202227
} else if (this.profilesEnabled && element.matchesScope(ConfigurationTarget.APPLICATION, false)) {
203228
// If the setting is an application-scoped setting, there are no overrides so we can use this
204229
// indicator to display that information instead.
205-
this.scopeOverridesElement.style.display = 'inline';
206-
this.scopeOverridesElement.classList.add('with-custom-hover');
230+
this.scopeOverridesIndicator.element.style.display = 'inline';
231+
this.scopeOverridesIndicator.element.classList.add('setting-indicator');
207232

208233
const applicationSettingText = localize('applicationSetting', "Applies to all profiles");
209-
this.scopeOverridesLabel.text = applicationSettingText;
234+
this.scopeOverridesIndicator.label.text = applicationSettingText;
210235

211236
const content = localize('applicationSettingDescription', "The setting is not specific to the current profile, and will retain its value when switching profiles.");
212-
this.scopeOverridesHover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesElement, content);
237+
this.scopeOverridesIndicator.hover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesIndicator.element, content);
213238
} else if (element.overriddenScopeList.length || element.overriddenDefaultsLanguageList.length) {
214239
if ((MODIFIED_INDICATOR_USE_INLINE_ONLY && element.overriddenScopeList.length) ||
215240
(element.overriddenScopeList.length === 1 && !element.overriddenDefaultsLanguageList.length)) {
241+
// This branch renders some info inline!
216242
// Render inline if we have the flag and there are scope overrides to render,
217243
// or if there is only one scope override to render and no language overrides.
218-
this.scopeOverridesElement.style.display = 'inline';
219-
this.scopeOverridesElement.classList.remove('with-custom-hover');
220-
this.scopeOverridesHover.value = undefined;
244+
this.scopeOverridesIndicator.element.style.display = 'inline';
245+
this.scopeOverridesIndicator.element.classList.remove('setting-indicator');
246+
this.scopeOverridesIndicator.hover.value = undefined;
221247

222248
// Just show all the text in the label.
223249
const prefaceText = element.isConfigured ?
224250
localize('alsoConfiguredIn', "Also modified in") :
225251
localize('configuredIn', "Modified in");
226-
this.scopeOverridesLabel.text = `${prefaceText} `;
252+
this.scopeOverridesIndicator.label.text = `${prefaceText} `;
227253

228254
for (let i = 0; i < element.overriddenScopeList.length; i++) {
229255
const overriddenScope = element.overriddenScopeList[i];
230-
const view = DOM.append(this.scopeOverridesElement, $('a.modified-scope', undefined, this.getInlineScopeDisplayText(overriddenScope)));
256+
const view = DOM.append(this.scopeOverridesIndicator.element, $('a.modified-scope', undefined, this.getInlineScopeDisplayText(overriddenScope)));
231257
if (i !== element.overriddenScopeList.length - 1) {
232-
DOM.append(this.scopeOverridesElement, $('span.comma', undefined, ', '));
258+
DOM.append(this.scopeOverridesIndicator.element, $('span.comma', undefined, ', '));
233259
}
234260
elementDisposables.add(
235261
DOM.addStandardDisposableListener(view, DOM.EventType.CLICK, (e: IMouseEvent) => {
@@ -247,12 +273,12 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
247273
// Even if the check above fails, we want to
248274
// show the text in a custom hover only if
249275
// the feature flag isn't on.
250-
this.scopeOverridesElement.style.display = 'inline';
251-
this.scopeOverridesElement.classList.add('with-custom-hover');
276+
this.scopeOverridesIndicator.element.style.display = 'inline';
277+
this.scopeOverridesIndicator.element.classList.add('setting-indicator');
252278
const scopeOverridesLabelText = element.isConfigured ?
253279
localize('alsoConfiguredElsewhere', "Also modified elsewhere") :
254280
localize('configuredElsewhere', "Modified elsewhere");
255-
this.scopeOverridesLabel.text = scopeOverridesLabelText;
281+
this.scopeOverridesIndicator.label.text = scopeOverridesLabelText;
256282

257283
let contentMarkdownString = '';
258284
let contentFallback = '';
@@ -298,22 +324,23 @@ export class SettingsTreeIndicatorsLabel implements IDisposable {
298324
scope: scope as ScopeString,
299325
language
300326
});
301-
this.scopeOverridesHover.value?.hide();
327+
this.scopeOverridesIndicator.hover.value?.hide();
302328
}
303329
};
304-
this.scopeOverridesHover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesElement, content, options);
330+
this.scopeOverridesIndicator.hover.value = setupCustomHover(this.hoverDelegate, this.scopeOverridesIndicator.element, content, options);
305331
}
306332
}
307333
this.render();
308334
}
309335

310336
updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) {
311-
this.defaultOverrideIndicatorElement.style.display = 'none';
337+
this.defaultOverrideIndicator.element.style.display = 'none';
312338
const sourceToDisplay = getDefaultValueSourceToDisplay(element);
313339
if (sourceToDisplay !== undefined) {
314-
this.defaultOverrideIndicatorElement.style.display = 'inline';
340+
this.defaultOverrideIndicator.element.style.display = 'inline';
341+
315342
const defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay);
316-
setupCustomHover(this.hoverDelegate, this.defaultOverrideIndicatorElement, defaultOverrideHoverContent);
343+
this.defaultOverrideIndicator.hover.value = setupCustomHover(this.hoverDelegate, this.defaultOverrideIndicator.element, defaultOverrideHoverContent);
317344
}
318345
this.render();
319346
}

0 commit comments

Comments
 (0)