From 4f053d820d29a752145cb9b9c3ff23909f22fe88 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 24 Jul 2018 21:27:49 -0700 Subject: [PATCH] Settings exclude control - replace fancypants List with basic dom manipulation --- .../browser/media/settingsWidgets.css | 36 +-- .../preferences/browser/settingsWidgets.ts | 257 ++++++------------ 2 files changed, 105 insertions(+), 188 deletions(-) diff --git a/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css b/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css index c57b7fa82ed..eb6dfcb6401 100644 --- a/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css +++ b/src/vs/workbench/parts/preferences/browser/media/settingsWidgets.css @@ -33,35 +33,39 @@ margin-top: 1px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row:hover .monaco-action-bar, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.focused .monaco-action-bar { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row { + position: relative; +} + +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover .monaco-action-bar, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row.focused .monaco-action-bar { display: block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .action-label { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .action-label { width: 16px; height: 16px; padding: 2px; margin-right: 2px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { margin-right: 4px; } -.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { +.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { background: url(edit.svg) center center no-repeat; } -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-edit { +.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-edit { background: url(edit_inverse.svg) center center no-repeat; } -.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-remove { +.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove { background: url(action-remove.svg) center center no-repeat; } -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row .monaco-action-bar .setting-excludeAction-remove { +.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row .monaco-action-bar .setting-excludeAction-remove { background: url(action-remove-dark.svg) center center no-repeat; } @@ -82,30 +86,30 @@ display: inline-block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem { display: flex; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem .setting-exclude-patternInput, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newExcludeItem .setting-exclude-siblingInput { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem .setting-exclude-patternInput, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newExcludeItem .setting-exclude-siblingInput { display: none; flex: 1; max-width: 200px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPattern .setting-exclude-patternInput { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPattern .setting-exclude-patternInput { display: inline-block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput { margin-right: 5px; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput, -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list-row.setting-exclude-newPatternWithSibling .setting-exclude-siblingInput { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-patternInput, +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-new-row.setting-exclude-newPatternWithSibling .setting-exclude-siblingInput { display: inline-block; } -.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .monaco-list { +.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-widget { margin-bottom: 10px; } diff --git a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts index bbb79af5539..675e598ac1c 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsWidgets.ts @@ -8,7 +8,6 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Button } from 'vs/base/browser/ui/button/button'; import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox'; -import { IRenderer, IVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IAction } from 'vs/base/common/actions'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode } from 'vs/base/common/keyCodes'; @@ -16,9 +15,7 @@ import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import 'vs/css!./media/settingsWidgets'; import { localize } from 'vs/nls'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { WorkbenchList } from 'vs/platform/list/browser/listService'; -import { foreground, inputBackground, inputBorder, inputForeground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; +import { foreground, inputBackground, inputBorder, inputForeground, listHoverBackground, registerColor, selectBackground, selectBorder, selectForeground, textLinkForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; @@ -77,7 +74,12 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { const foregroundColor = theme.getColor(foreground); if (foregroundColor) { - collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; };`); + collector.addRule(`.settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget .action-label { color: ${foregroundColor}; }`); + } + + const listHoverBackgroundColor = theme.getColor(listHoverBackground); + if (listHoverBackgroundColor) { + collector.addRule(`.settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-exclude .setting-exclude-row:hover { background-color: ${listHoverBackgroundColor}; }`); } }); @@ -88,8 +90,8 @@ enum AddItemMode { } export class ExcludeSettingListModel { - private _dataItems: IExcludeItem[]; - private _newItem: AddItemMode; + private _dataItems: IExcludeItem[] = []; + private _newItem = AddItemMode.None; get items(): IExcludeItem[] { const items = [ @@ -133,7 +135,8 @@ interface IExcludeChangeEvent { } export class ExcludeSettingWidget extends Disposable { - private list: WorkbenchList; + private listElement: HTMLElement; + private renderedDisposables: IDisposable[] = []; private model = new ExcludeSettingListModel(); @@ -143,23 +146,11 @@ export class ExcludeSettingWidget extends Disposable { constructor( container: HTMLElement, @IThemeService private themeService: IThemeService, - @IInstantiationService private instantiationService: IInstantiationService + @IContextViewService private contextViewService: IContextViewService ) { super(); - const dataRenderer = new ExcludeDataItemRenderer(); - this._register(dataRenderer.onDidRemoveExclude(key => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined }))); - this._register(dataRenderer.onEditExclude(key => { - // this.model - })); - - const newItemRenderer = this.instantiationService.createInstance(NewExcludeRenderer); - const delegate = new ExcludeSettingListDelegate(); - this.list = this.instantiationService.createInstance(WorkbenchList, container, delegate, [newItemRenderer, dataRenderer], { - identityProvider: element => element.id, - multipleSelectionSupport: false - }) as WorkbenchList; - this._register(this.list); + this.listElement = DOM.append(container, $('.setting-exclude-widget')); const addPatternButton = this._register(new Button(container)); addPatternButton.label = localize('addPattern', "Add Pattern"); @@ -169,6 +160,8 @@ export class ExcludeSettingWidget extends Disposable { this.model.setAddItemMode(AddItemMode.Pattern); this.update(); })); + + this.update(); } setValue(excludeValue: any): void { @@ -177,66 +170,15 @@ export class ExcludeSettingWidget extends Disposable { } private update(): void { - this.list.splice(0, this.list.length, this.model.items); - - const listHeight = 22 * this.model.items.length; - this.list.layout(listHeight); - this.list.getHTMLElement().style.height = listHeight + 'px'; - } -} - -interface IExcludeDataItem { - id: string; - pattern: string; - sibling?: string; -} - -interface INewExcludeItem { - id: string; - mode: AddItemMode; -} - -type IExcludeItem = IExcludeDataItem | INewExcludeItem; - -function isExcludeDataItem(excludeItem: IExcludeItem): excludeItem is IExcludeDataItem { - return !!(excludeItem).pattern; -} + DOM.clearNode(this.listElement); + this.renderedDisposables = dispose(this.renderedDisposables); -interface IExcludeDataItemTemplate { - container: HTMLElement; - - actionBar: ActionBar; - patternElement: HTMLElement; - siblingElement: HTMLElement; - toDispose: IDisposable[]; -} + this.model.items + .map(item => this.renderItem(item)) + .forEach(itemElement => this.listElement.appendChild(itemElement)); -class ExcludeDataItemRenderer implements IRenderer { - static readonly templateId: string = 'excludeDataItem'; - - private readonly _onDidRemoveExclude: Emitter = new Emitter(); - public readonly onDidRemoveExclude: Event = this._onDidRemoveExclude.event; - - private readonly _onEditExclude: Emitter = new Emitter(); - public readonly onEditExclude: Event = this._onEditExclude.event; - - get templateId(): string { - return ExcludeDataItemRenderer.templateId; - } - - renderTemplate(container: HTMLElement): IExcludeDataItemTemplate { - const toDispose = []; - - const actionBar = new ActionBar(container); - toDispose.push(actionBar); - - return { - container, - patternElement: DOM.append(container, $('.setting-exclude-pattern')), - siblingElement: DOM.append(container, $('.setting-exclude-sibling')), - toDispose, - actionBar - }; + const listHeight = 22 * this.model.items.length; + this.listElement.style.height = listHeight + 'px'; } private createDeleteAction(key: string): IAction { @@ -245,7 +187,7 @@ class ExcludeDataItemRenderer implements IRenderer this._onDidRemoveExclude.fire(key) + run: () => this._onDidChangeExclude.fire({ originalPattern: key, pattern: undefined }) }; } @@ -255,140 +197,111 @@ class ExcludeDataItemRenderer implements IRenderer this._onEditExclude.fire(key) + run: () => { } }; } - renderElement(element: IExcludeDataItem, index: number, templateData: IExcludeDataItemTemplate): void { - templateData.patternElement.textContent = element.pattern; - templateData.siblingElement.textContent = element.sibling && ('when: ' + element.sibling); - - templateData.actionBar.clear(); - templateData.actionBar.push([ - this.createEditAction(element.pattern), - this.createDeleteAction(element.pattern) - ], { icon: true, label: false }); - - templateData.container.title = element.sibling ? - localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", element.pattern, element.sibling) : - localize('excludePatternHintLabel', "Exclude files matching `{0}`", element.pattern); - } - - disposeElement(element: IExcludeDataItem, index: number, templateData: IExcludeDataItemTemplate): void { + private renderItem(item: IExcludeItem): HTMLElement { + return isExcludeDataItem(item) ? + this.renderDataItem(item) : + this.renderNewItem(item); } - disposeTemplate(templateData: IExcludeDataItemTemplate): void { - dispose(templateData.toDispose); - } -} + private renderDataItem(item: IExcludeDataItem): HTMLElement { + const rowElement = $('.setting-exclude-row'); + const actionBar = new ActionBar(rowElement); + this.renderedDisposables.push(actionBar); -interface INewExcludeItemTemplate { - container: HTMLElement; + const patternElement = DOM.append(rowElement, $('.setting-exclude-pattern')); + const siblingElement = DOM.append(rowElement, $('.setting-exclude-sibling')); + patternElement.textContent = item.pattern; + siblingElement.textContent = item.sibling && ('when: ' + item.sibling); - patternInput: InputBox; - siblingInput: InputBox; - toDispose: IDisposable[]; -} - -interface INewExcludeItemEvent { - pattern: string; - sibling?: string; -} - -class NewExcludeRenderer implements IRenderer { - static readonly templateId: string = 'newExcludeItem'; + actionBar.push([ + this.createEditAction(item.pattern), + this.createDeleteAction(item.pattern) + ], { icon: true, label: false }); - private readonly _onNewExcludeItem: Emitter = new Emitter(); - public readonly onNewExcludeItem: Event = this._onNewExcludeItem.event; + rowElement.title = item.sibling ? + localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", item.pattern, item.sibling) : + localize('excludePatternHintLabel', "Exclude files matching `{0}`", item.pattern); - constructor( - @IContextViewService private contextViewService: IContextViewService, - @IThemeService private themeService: IThemeService - ) { - } - - get templateId(): string { - return NewExcludeRenderer.templateId; + return rowElement; } - renderTemplate(container: HTMLElement): INewExcludeItemTemplate { - const toDispose = []; + private renderNewItem(item: INewExcludeItem): HTMLElement { + const rowElement = $('.setting-exclude-new-row'); const onKeydown = (e: StandardKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { - this._onNewExcludeItem.fire({ + this._onDidChangeExclude.fire({ + originalPattern: undefined, pattern: patternInput.value, - sibling: siblingInput.value + // sibling: siblingInput.value }); } }; - const patternInput = new InputBox(container, this.contextViewService, { + const patternInput = new InputBox(rowElement, this.contextViewService, { placeholder: localize('excludePatternInputPlaceholder', "Exclude Pattern...") }); patternInput.element.classList.add('setting-exclude-patternInput'); - toDispose.push(attachInputBoxStyler(patternInput, this.themeService, { + this.renderedDisposables.push(attachInputBoxStyler(patternInput, this.themeService, { inputBackground: settingsTextInputBackground, inputForeground: settingsTextInputForeground, inputBorder: settingsTextInputBorder })); - toDispose.push(patternInput); - toDispose.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + this.renderedDisposables.push(patternInput); + this.renderedDisposables.push(DOM.addStandardDisposableListener(patternInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); - const siblingInput = new InputBox(container, this.contextViewService, { + const siblingInput = new InputBox(rowElement, this.contextViewService, { placeholder: localize('excludeSiblingInputPlaceholder', "When Pattern Is Present...") }); siblingInput.element.classList.add('setting-exclude-siblingInput'); - toDispose.push(siblingInput); - toDispose.push(attachInputBoxStyler(siblingInput, this.themeService, { + this.renderedDisposables.push(siblingInput); + this.renderedDisposables.push(attachInputBoxStyler(siblingInput, this.themeService, { inputBackground: settingsTextInputBackground, inputForeground: settingsTextInputForeground, inputBorder: settingsTextInputBorder })); - toDispose.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + this.renderedDisposables.push(DOM.addStandardDisposableListener(siblingInput.inputElement, DOM.EventType.KEY_DOWN, onKeydown)); + + rowElement.classList.add('setting-exclude-newExcludeItem'); + + rowElement.classList.remove('setting-exclude-newPattern'); + rowElement.classList.remove('setting-exclude-newPatternWithSibling'); + if (item.mode === AddItemMode.Pattern) { + rowElement.classList.add('setting-exclude-newPattern'); + patternInput.focus(); + patternInput.select(); + } else if (item.mode === AddItemMode.PatternWithSibling) { + rowElement.classList.add('setting-exclude-newPatternWithSibling'); + } - return { - container, - patternInput, - siblingInput, - toDispose - }; + return rowElement; } - renderElement(element: INewExcludeItem, index: number, templateData: INewExcludeItemTemplate): void { - templateData.container.classList.add('setting-exclude-newExcludeItem'); - - templateData.container.classList.remove('setting-exclude-newPattern'); - templateData.container.classList.remove('setting-exclude-newPatternWithSibling'); - if (element.mode === AddItemMode.Pattern) { - templateData.container.classList.add('setting-exclude-newPattern'); - templateData.patternInput.focus(); - templateData.patternInput.select(); - } else if (element.mode === AddItemMode.PatternWithSibling) { - templateData.container.classList.add('setting-exclude-newPatternWithSibling'); - } + dispose() { + super.dispose(); + this.renderedDisposables = dispose(this.renderedDisposables); } +} - disposeElement(element: INewExcludeItem, index: number, templateData: INewExcludeItemTemplate): void { - } +interface IExcludeDataItem { + id: string; + pattern: string; + sibling?: string; +} - disposeTemplate(templateData: INewExcludeItemTemplate): void { - dispose(templateData.toDispose); - } +interface INewExcludeItem { + id: string; + mode: AddItemMode; } -class ExcludeSettingListDelegate implements IVirtualDelegate { - getHeight(element: IExcludeItem): number { - return 22; - } +type IExcludeItem = IExcludeDataItem | INewExcludeItem; - getTemplateId(element: IExcludeItem): string { - if (isExcludeDataItem(element)) { - return ExcludeDataItemRenderer.templateId; - } else { - return NewExcludeRenderer.templateId; - } - } +function isExcludeDataItem(excludeItem: IExcludeItem): excludeItem is IExcludeDataItem { + return !!(excludeItem).pattern; } // class EditExcludeItemAction extends Action { -- GitLab