Change:
<!-- Do Not Delete This! featurerequesttemplate --<!-- Please read our Rules of Conduct: --<!-- Please search existing issues to avoid creating duplicates. --<!-- Describe the feature you'd like. --Ref The Settings editor indicators area currently shows in which other scopes the setting has been modified, whether the setting can sync, whether the default value of the setting has been overridden, and so on. That area might show more information about a setting in the future. The indicators area is already overcrowded. This feature tracks moving more of the information in the indicators area out to custom hovers, therefore reducing the amount of information displayed to the user until they hover over the different indicators.
closedWith

Discussion:
import * as DOM from 'vs/base/browser/dom'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; <ins> import { IHoverDelegate, IHoverDelegateOptions } from 'vs/base/browser/ui/iconLabel/iconHoverDelegate'; import { setupCustomHover } from 'vs/base/browser/ui/iconLabel/iconLabelHover'; </ins> import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { Emitter } from 'vs/base/common/event'; import { DisposableStore } from 'vs/base/common/lifecycle';

import { getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; import { getDefaultIgnoredSettings, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync'; import { SettingsTreeSettingElement } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; <ins> import { IHoverService } from 'vs/workbench/services/hover/browser/hover'; </ins> const $ = DOM.$;

* Renders the indicators next to a setting, such as "Also Modified In". */ export class SettingsTreeIndicatorsLabel { <del> /** * This element wraps around the other elements. */ private labelElement: HTMLElement; </del> <ins> private indicatorsContainerElement: HTMLElement; </ins> private scopeOverridesElement: HTMLElement; private syncIgnoredElement: HTMLElement; private defaultOverrideIndicatorElement: HTMLElement; <del> private defaultOverrideIndicatorLabel: SimpleIconLabel; </del> <ins> private hoverDelegate: IHoverDelegate; </ins> constructor( container: HTMLElement, <ins> @IConfigurationService configurationService: IConfigurationService, @IHoverService hoverService: IHoverService, </ins> @IUserDataSyncEnablementService private readonly userDataSyncEnablementService: IUserDataSyncEnablementService) { <del> this.labelElement = DOM.append(container, $('.misc-label')); this.labelElement.style.display = 'inline'; </del> <ins> this.indicatorsContainerElement = DOM.append(container, $('.misc-label')); this.indicatorsContainerElement.style.display = 'inline'; </ins> this.scopeOverridesElement = this.createScopeOverridesElement(); this.syncIgnoredElement = this.createSyncIgnoredElement(); <del> const { element: defaultOverrideElement, label: defaultOverrideLabel } = this.createDefaultOverrideIndicator(); this.defaultOverrideIndicatorElement = defaultOverrideElement; this.defaultOverrideIndicatorLabel = defaultOverrideLabel; </del> <ins> this.defaultOverrideIndicatorElement = this.createDefaultOverrideIndicator(); this.hoverDelegate = { showHover: (options: IHoverDelegateOptions, focus?: boolean) => { return hoverService.showHover(options, focus); }, delay: configurationService.getValue<number>('workbench.hover.delay'), placement: 'element' }; </ins> } private createScopeOverridesElement(): HTMLElement {

private createSyncIgnoredElement(): HTMLElement { const syncIgnoredElement = $('span.setting-item-ignored'); const syncIgnoredLabel = new SimpleIconLabel(syncIgnoredElement); <del> syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Setting not synced'); syncIgnoredLabel.title = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); </del> <ins> syncIgnoredLabel.text = '$(info) ' + localize('extensionSyncIgnoredLabel', 'Not synced'); const syncIgnoredHoverContent = localize('syncIgnoredTitle', "Settings sync does not sync this setting"); setupCustomHover(this.hoverDelegate, syncIgnoredElement, syncIgnoredHoverContent); </ins> return syncIgnoredElement; } <del> private createDefaultOverrideIndicator(): { element: HTMLElement; label: SimpleIconLabel } { </del> <ins> private createDefaultOverrideIndicator(): HTMLElement { </ins> const defaultOverrideIndicator = $('span.setting-item-default-overridden'); const defaultOverrideLabel = new SimpleIconLabel(defaultOverrideIndicator); <del> return { element: defaultOverrideIndicator, label: defaultOverrideLabel }; </del> <ins> defaultOverrideLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); return defaultOverrideIndicator; </ins> } private render() {

return element.style.display !== 'none'; }); <del> this.labelElement.innerText = ''; this.labelElement.style.display = 'none'; </del> <ins> this.indicatorsContainerElement.innerText = ''; this.indicatorsContainerElement.style.display = 'none'; </ins> if (elementsToShow.length) { <del> this.labelElement.style.display = 'inline'; DOM.append(this.labelElement, $('span', undefined, '(')); </del> <ins> this.indicatorsContainerElement.style.display = 'inline'; DOM.append(this.indicatorsContainerElement, $('span', undefined, '(')); </ins> for (let i = 0; i < elementsToShow.length - 1; i++) { <del> DOM.append(this.labelElement, elementsToShow[i]); DOM.append(this.labelElement, $('span.comma', undefined, ' • ')); </del> <ins> DOM.append(this.indicatorsContainerElement, elementsToShow[i]); DOM.append(this.indicatorsContainerElement, $('span.comma', undefined, ' • ')); </ins> } <del> DOM.append(this.labelElement, elementsToShow[elementsToShow.length - 1]); DOM.append(this.labelElement, $('span', undefined, ')')); </del> <ins> DOM.append(this.indicatorsContainerElement, elementsToShow[elementsToShow.length - 1]); DOM.append(this.indicatorsContainerElement, $('span', undefined, ')')); </ins> } }

updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { this.defaultOverrideIndicatorElement.style.display = 'none'; <del> const defaultValueSource = element.defaultValueSource; if (defaultValueSource) { </del> <ins> const sourceToDisplay = getDefaultValueSourceToDisplay(element); if (sourceToDisplay !== undefined) { </ins> this.defaultOverrideIndicatorElement.style.display = 'inline'; <del> let sourceToDisplay = ''; if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; } else if (typeof defaultValueSource === 'string') { sourceToDisplay = defaultValueSource; } if (sourceToDisplay) { this.defaultOverrideIndicatorLabel.title = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); this.defaultOverrideIndicatorLabel.text = '$(info) ' + localize('defaultOverriddenLabel', "Default value changed"); } </del> <ins> const defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); setupCustomHover(this.hoverDelegate, this.defaultOverrideIndicatorElement, defaultOverrideHoverContent); </ins> } this.render(); } } <ins> function getDefaultValueSourceToDisplay(element: SettingsTreeSettingElement): string | undefined { let sourceToDisplay: string | undefined; const defaultValueSource = element.defaultValueSource; if (defaultValueSource) { if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; } else if (typeof defaultValueSource === 'string') { sourceToDisplay = defaultValueSource; } } return sourceToDisplay; } </ins> export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, configurationService: IConfigurationService): string { const ariaLabelSections: string[] = [];

} // Add default override indicator text <del> if (element.defaultValueSource) { const defaultValueSource = element.defaultValueSource; let sourceToDisplay = ''; if (typeof defaultValueSource !== 'string' && defaultValueSource.id !== element.setting.extensionInfo?.id) { sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; } else if (typeof defaultValueSource === 'string') { sourceToDisplay = defaultValueSource; } if (sourceToDisplay) { ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); } </del> <ins> const sourceToDisplay = getDefaultValueSourceToDisplay(element); if (sourceToDisplay !== undefined) { ariaLabelSections.push(localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay)); </ins> } const ariaLabel = ariaLabelSections.join('. ');