From fc77396089c478a4fabf0c8569b0a1821ea85dbb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 14 May 2018 16:58:00 -0700 Subject: [PATCH] Settings editor - click description to expand --- .../browser/media/settingsEditor2.css | 11 ++ .../preferences/browser/settingsEditor2.ts | 133 ++++++++++++++---- 2 files changed, 120 insertions(+), 24 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css index 905f57e13b5..1bf6c8f3d85 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsEditor2.css @@ -84,6 +84,7 @@ flex: 1; border-spacing: 0; border-collapse: separate; + position: relative; } .settings-editor > .settings-body > .settings-list-container .monaco-list-row { @@ -148,6 +149,16 @@ margin-top: 5px; height: 36px; overflow: hidden; + white-space: pre-wrap; +} + +.settings-editor > .settings-body > .settings-list-container .monaco-list-row.is-expanded .setting-item-description, +.settings-editor > .settings-body > .settings-list-container .monaco-list-row.setting-item-measure-helper .setting-item-description { + height: initial; +} + +.settings-editor > .settings-body > .settings-list-container .monaco-list-row.is-expandable .setting-item-description { + cursor: pointer; } .settings-editor > .settings-body > .settings-list-container .monaco-list-row .setting-item-value { diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index 251e9c72707..ce3892fc5fb 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -51,11 +51,14 @@ interface IListEntry { } interface ISettingItemEntry extends IListEntry { + templateId: typeof SETTINGS_ENTRY_TEMPLATE_ID; key: string; value: any; isConfigured: boolean; description: string; overriddenScopeList: string[]; + isExpanded: boolean; + isExpandable?: boolean; type?: string | string[]; enum?: string[]; } @@ -67,14 +70,18 @@ enum ExpandState { } interface IGroupTitleEntry extends IListEntry { + templateId: typeof SETTINGS_GROUP_ENTRY_TEMPLATE_ID; title: string; expandState: ExpandState; } interface IButtonRowEntry extends IListEntry { + templateId: typeof BUTTON_ROW_ENTRY_TEMPLATE; label: string; } +type ListEntry = ISettingItemEntry | IGroupTitleEntry | IButtonRowEntry; + enum SearchResultIdx { Local = 0, Remote = 1 @@ -103,7 +110,7 @@ export class SettingsEditor2 extends BaseEditor { private showConfiguredSettingsOnlyCheckbox: HTMLInputElement; private settingsListContainer: HTMLElement; - private settingsList: List; + private settingsList: List; private dimension: DOM.Dimension; private searchFocusContextKey: IContextKey; @@ -120,6 +127,7 @@ export class SettingsEditor2 extends BaseEditor { private pendingSettingModifiedReport: { key: string, value: any }; private groupExpanded = new Map(); + private itemExpanded = new Map(); constructor( @ITelemetryService telemetryService: ITelemetryService, @@ -251,9 +259,13 @@ export class SettingsEditor2 extends BaseEditor { private createList(parent: HTMLElement): void { this.settingsListContainer = DOM.append(parent, $('.settings-list-container')); - const settingItemRenderer = this.instantiationService.createInstance(SettingItemRenderer); + const settingItemRenderer = this.instantiationService.createInstance(SettingItemRenderer, this.settingsListContainer); this._register(settingItemRenderer.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value))); this._register(settingItemRenderer.onDidOpenSettings(() => this.openSettingsFile())); + this._register(settingItemRenderer.onDidToggleExpandSetting(e => { + this.itemExpanded.set(e.key, e.expandState === ExpandState.Expanded); + this.renderEntries(); + })); const buttonItemRenderer = new ButtonRowRenderer(); this._register(buttonItemRenderer.onDidClick(e => this.onShowAllSettingsClicked())); @@ -268,7 +280,7 @@ export class SettingsEditor2 extends BaseEditor { this.settingsList = this._register(this.instantiationService.createInstance( WorkbenchList, this.settingsListContainer, - new SettingItemDelegate(), + new SettingItemDelegate(this.settingsListContainer), [settingItemRenderer, groupTitleRenderer, buttonItemRenderer], { identityProvider: e => e.id, @@ -278,7 +290,7 @@ export class SettingsEditor2 extends BaseEditor { keyboardSupport: false, mouseSupport: false }) - ) as WorkbenchList; + ) as WorkbenchList; this.settingsList.style({ listHoverBackground: Color.transparent, listFocusOutline: Color.transparent }); } @@ -489,7 +501,7 @@ export class SettingsEditor2 extends BaseEditor { }); } - private getEntriesFromSearch(searchResults: ISearchResult[]): IListEntry[] { + private getEntriesFromSearch(searchResults: ISearchResult[]): ListEntry[] { const entries: ISettingItemEntry[] = []; const seenSettings = new Set(); @@ -537,8 +549,8 @@ export class SettingsEditor2 extends BaseEditor { } } - private getEntriesFromModel(): IListEntry[] { - const entries: IListEntry[] = []; + private getEntriesFromModel(): ListEntry[] { + const entries: ListEntry[] = []; for (let groupIdx = 0; groupIdx < this.defaultSettingsEditorModel.settingsGroups.length; groupIdx++) { if (groupIdx > 0 && !this.showAllSettings && !this.showConfiguredSettingsOnly) { break; @@ -552,6 +564,7 @@ export class SettingsEditor2 extends BaseEditor { for (const section of group.sections) { for (const setting of section.settings) { const entry = this.settingToEntry(setting); + if (!this.showConfiguredSettingsOnly || entry.isConfigured) { groupEntries.push(entry); } @@ -603,6 +616,8 @@ export class SettingsEditor2 extends BaseEditor { overriddenScopeList.push('User'); } + const isExpanded = !!this.itemExpanded.get(s.key); + return { id: s.key, key: s.key, @@ -612,7 +627,8 @@ export class SettingsEditor2 extends BaseEditor { description: s.description.join('\n'), enum: s.enum, type: s.type, - templateId: SETTINGS_ENTRY_TEMPLATE_ID + templateId: SETTINGS_ENTRY_TEMPLATE_ID, + isExpanded }; } @@ -623,16 +639,23 @@ export class SettingsEditor2 extends BaseEditor { } } -class SettingItemDelegate implements IDelegate { +class SettingItemDelegate implements IDelegate { + + constructor(private measureContainer: HTMLElement) { - getHeight(entry: IListEntry) { + } + + getHeight(entry: ListEntry) { if (entry.templateId === SETTINGS_GROUP_ENTRY_TEMPLATE_ID) { return 42; } if (entry.templateId === SETTINGS_ENTRY_TEMPLATE_ID) { - // TODO dynamic height - return 68; + if (entry.isExpanded) { + return this.getDynamicHeight(entry); + } else { + return 68; + } } if (entry.templateId === BUTTON_ROW_ENTRY_TEMPLATE) { @@ -642,9 +665,24 @@ class SettingItemDelegate implements IDelegate { return 0; } - getTemplateId(element: IListEntry) { + getTemplateId(element: ListEntry) { return element.templateId; } + + private getDynamicHeight(entry: ISettingItemEntry): number { + return measureSettingItemEntry(entry, this.measureContainer); + } +} + +function measureSettingItemEntry(entry: ISettingItemEntry, measureContainer: HTMLElement): number { + const measureHelper = DOM.append(measureContainer, $('.setting-item-measure-helper.monaco-list-row')); + + const template = SettingItemRenderer.renderTemplate(measureHelper); + SettingItemRenderer.renderElement(entry, 0, template); + + const height = measureHelper.offsetHeight; + measureContainer.removeChild(measureHelper); + return height; } interface IDisposableTemplate { @@ -654,6 +692,7 @@ interface IDisposableTemplate { interface ISettingItemTemplate extends IDisposableTemplate { parent: HTMLElement; + context?: ISettingItemEntry; containerElement: HTMLElement; categoryElement: HTMLElement; labelElement: HTMLElement; @@ -680,6 +719,11 @@ interface ISettingChangeEvent { value: any; // undefined => reset unconfigure } +interface ISettingExpandEvent { + key: string; + expandState: ExpandState; +} + class SettingItemRenderer implements IRenderer { private readonly _onDidChangeSetting: Emitter = new Emitter(); @@ -688,14 +732,22 @@ class SettingItemRenderer implements IRenderer = new Emitter(); public readonly onDidOpenSettings: Event = this._onDidOpenSettings.event; + private readonly _onDidToggleExpandSetting: Emitter = new Emitter(); + public readonly onDidToggleExpandSetting: Event = this._onDidToggleExpandSetting.event; + get templateId(): string { return SETTINGS_ENTRY_TEMPLATE_ID; } constructor( + private measureContainer: HTMLElement, @IContextViewService private contextViewService: IContextViewService, @IThemeService private themeService: IThemeService ) { } renderTemplate(parent: HTMLElement): ISettingItemTemplate { + return SettingItemRenderer.renderTemplate(parent, this); + } + + static renderTemplate(parent: HTMLElement, that?: SettingItemRenderer): ISettingItemTemplate { DOM.addClass(parent, 'setting-item'); const itemContainer = DOM.append(parent, $('.setting-item-container')); @@ -710,9 +762,10 @@ class SettingItemRenderer implements IRenderer { + const entry = template.context; + if (entry && entry.isExpandable) { + const newState = entry.isExpanded ? ExpandState.Collapsed : ExpandState.Expanded; + that._onDidToggleExpandSetting.fire({ key: entry.key, expandState: newState }); + } + })); + } + + return template; } renderElement(entry: ISettingItemEntry, index: number, template: ISettingItemTemplate): void { + return SettingItemRenderer.renderElement(entry, index, template, this); + } + + static renderElement(entry: ISettingItemEntry, index: number, template: ISettingItemTemplate, that?: SettingItemRenderer): void { + template.context = entry; DOM.toggleClass(template.parent, 'odd', index % 2 === 1); + DOM.toggleClass(template.parent, 'is-configured', entry.isConfigured); + DOM.toggleClass(template.parent, 'is-expanded', entry.isExpanded); let titleTooltip = entry.key; if (entry.isConfigured) { @@ -740,19 +812,32 @@ class SettingItemRenderer implements IRenderer 68; + DOM.toggleClass(template.parent, 'is-expandable', entry.isExpandable); + } + + if (that) { + that.renderValue(entry, template); + } const resetButton = new Button(template.valueElement); resetButton.element.title = localize('resetButtonTitle', "Reset"); resetButton.element.classList.add('setting-reset-button'); - attachButtonStyler(resetButton, this.themeService, { - buttonBackground: Color.transparent.toString(), - buttonHoverBackground: Color.transparent.toString() - }); - template.toDispose.push(resetButton.onDidClick(e => { - this._onDidChangeSetting.fire({ key: entry.key, value: undefined }); - })); + + if (that) { + attachButtonStyler(resetButton, that.themeService, { + buttonBackground: Color.transparent.toString(), + buttonHoverBackground: Color.transparent.toString() + }); + } + + if (that) { + template.toDispose.push(resetButton.onDidClick(e => { + that._onDidChangeSetting.fire({ key: entry.key, value: undefined }); + })); + } template.toDispose.push(resetButton); const alsoConfiguredInLabel = localize('alsoConfiguredIn', "Also configured in:"); -- GitLab