diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 5616313787e778582769ebd05b244a8f5073a141..0eb524c52472032fcd6fcbf5629082e4a0e92f05 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -673,7 +673,7 @@ export class TreeView extends HeightMap { } public getLastVisibleElement(): any { - const item = this.itemAtIndex(this.indexAt(this.lastRenderTop + this.lastRenderHeight)); + const item = this.itemAtIndex(this.indexAt(this.lastRenderTop + this.lastRenderHeight - 1)); return item && item.model.getElement(); } diff --git a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts index e39c293743a759eb7df76eac9fcd181604c4313a..d891a63aabca18ea257d40bce5c227a642d72f8f 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsEditor2.ts @@ -39,8 +39,8 @@ import { SuggestEnabledInput } from 'vs/workbench/parts/codeEditor/browser/sugge import { PreferencesEditor } from 'vs/workbench/parts/preferences/browser/preferencesEditor'; import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets'; import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { resolveExtensionsSettings, resolveSettingsTree, SettingsRenderer, SettingsTree } from 'vs/workbench/parts/preferences/browser/settingsTree'; -import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement, countSettingGroupChildrenWithPredicate } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { resolveExtensionsSettings, resolveSettingsTree, SettingsRenderer, SettingsTree, SimplePagedDataSource, SettingsDataSource } from 'vs/workbench/parts/preferences/browser/settingsTree'; +import { ISettingsEditorViewState, MODIFIED_SETTING_TAG, ONLINE_SERVICES_SETTING_TAG, SearchResultIdx, SearchResultModel, SettingsTreeGroupElement, SettingsTreeModel, countSettingGroupChildrenWithPredicate, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { TOCRenderer, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree'; import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider } from 'vs/workbench/parts/preferences/common/preferences'; import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; @@ -71,6 +71,7 @@ export class SettingsEditor2 extends BaseEditor { private settingsTreeContainer: HTMLElement; private settingsTree: Tree; private settingsTreeRenderer: SettingsRenderer; + private settingsTreeDataSource: SimplePagedDataSource; private tocTreeModel: TOCTreeModel; private settingsTreeModel: SettingsTreeModel; private noResultsMessage: HTMLElement; @@ -426,14 +427,19 @@ export class SettingsEditor2 extends BaseEditor { })); this._register(this.tocTree.onDidChangeFocus(e => { - const element = e.focus; + const element: SettingsTreeGroupElement = e.focus; if (this.searchResultModel) { this.viewState.filterToCategory = element; this.renderTree(); } if (element && (!e.payload || !e.payload.fromScroll)) { - this.settingsTree.reveal(element, 0); + let refreshP = TPromise.wrap(null); + if (this.settingsTreeDataSource.pageTo(element.index)) { + refreshP = this.renderTree(); + } + + refreshP.then(() => this.settingsTree.reveal(element, 0)); } })); @@ -463,11 +469,15 @@ export class SettingsEditor2 extends BaseEditor { this.settingsTree.reveal(element); })); + this.settingsTreeDataSource = this.instantiationService.createInstance(SimplePagedDataSource, + this.instantiationService.createInstance(SettingsDataSource, this.viewState)); + this.settingsTree = this._register(this.instantiationService.createInstance(SettingsTree, this.settingsTreeContainer, this.viewState, { - renderer: this.settingsTreeRenderer + renderer: this.settingsTreeRenderer, + dataSource: this.settingsTreeDataSource })); this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex'); @@ -516,6 +526,8 @@ export class SettingsEditor2 extends BaseEditor { return; } + this.updateTreePagingByScroll(); + const elementToSync = this.settingsTree.getFirstVisibleElement(); const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent : elementToSync instanceof SettingsTreeGroupElement ? elementToSync : @@ -536,6 +548,13 @@ export class SettingsEditor2 extends BaseEditor { } } + private updateTreePagingByScroll(): void { + const lastVisibleElement = this.settingsTree.getLastVisibleElement(); + if (lastVisibleElement && this.settingsTreeDataSource.pageTo(lastVisibleElement.index)) { + this.renderTree(); + } + } + private updateChangedSetting(key: string, value: any): TPromise { // ConfigurationService displays the error if this fails. // Force a render afterwards because onDidConfigurationUpdate doesn't fire if the update doesn't result in an effective setting value change diff --git a/src/vs/workbench/parts/preferences/browser/settingsTree.ts b/src/vs/workbench/parts/preferences/browser/settingsTree.ts index 30418278f38577884db2738e914c405c6e3f4771..e1816a4ca4eb4ab4ae98f1067becd150a52e199f 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTree.ts @@ -39,7 +39,7 @@ import { editorBackground, errorForeground, focusBorder, foreground, inputValida import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ITOCEntry } from 'vs/workbench/parts/preferences/browser/settingsLayout'; -import { ISettingsEditorViewState, isExcludeSetting, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement, settingKeyToDisplayFormat } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; +import { ISettingsEditorViewState, isExcludeSetting, SettingsTreeElement, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, settingKeyToDisplayFormat, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels'; import { ExcludeSettingWidget, IExcludeDataItem, settingsHeaderForeground, settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectListBorder, settingsSelectForeground, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/parts/preferences/browser/settingsWidgets'; import { ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; @@ -206,6 +206,58 @@ export class SettingsDataSource implements IDataSource { } } +export class SimplePagedDataSource implements IDataSource { + private static readonly SETTINGS_PER_PAGE = 30; + + private loadedToIndex: number; + + constructor(private realDataSource: IDataSource) { + this.loadedToIndex = SimplePagedDataSource.SETTINGS_PER_PAGE * 2; + } + + pageTo(index: number): boolean { + if (index > this.loadedToIndex - SimplePagedDataSource.SETTINGS_PER_PAGE) { + this.loadedToIndex = (Math.ceil(index / SimplePagedDataSource.SETTINGS_PER_PAGE) + 1) * SimplePagedDataSource.SETTINGS_PER_PAGE; + return true; + } else { + return false; + } + } + + getId(tree: ITree, element: any): string { + return this.realDataSource.getId(tree, element); + } + + hasChildren(tree: ITree, element: any): boolean { + return this.realDataSource.hasChildren(tree, element); + } + + getChildren(tree: ITree, element: SettingsTreeGroupElement): TPromise { + return this.realDataSource.getChildren(tree, element).then(realChildren => { + return this._getChildren(realChildren); + }); + } + + _getChildren(realChildren: SettingsTreeElement[]): any[] { + const lastChild = realChildren[realChildren.length - 1]; + if (lastChild && lastChild.index > this.loadedToIndex) { + return realChildren.filter(child => { + return child.index < this.loadedToIndex; + }); + } else { + return realChildren; + } + } + + getParent(tree: ITree, element: any): TPromise { + return this.realDataSource.getParent(tree, element); + } + + shouldAutoexpand(tree: ITree, element: any): boolean { + return this.realDataSource.shouldAutoexpand(tree, element); + } +} + interface IDisposableTemplate { toDispose: IDisposable[]; } @@ -1374,7 +1426,6 @@ export class SettingsTree extends NonExpandableOrSelectableTree { const controller = instantiationService.createInstance(SettingsTreeController); const fullConfiguration = { - dataSource: instantiationService.createInstance(SettingsDataSource, viewState), controller, accessibilityProvider: instantiationService.createInstance(SettingsAccessibilityProvider), filter: instantiationService.createInstance(SettingsTreeFilter, viewState), diff --git a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts index cbbc0a6672ac981cbecc40dda8581cfaf2efafba..9626898d4705c10a8879c93ef2047df9ed7910ea 100644 --- a/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/parts/preferences/browser/settingsTreeModels.ts @@ -25,6 +25,11 @@ export interface ISettingsEditorViewState { export abstract class SettingsTreeElement { id: string; parent: SettingsTreeGroupElement; + + /** + * Index assigned in display order, used for paging. + */ + index: number; } export class SettingsTreeGroupElement extends SettingsTreeElement { @@ -42,8 +47,8 @@ export class SettingsTreeNewExtensionsElement extends SettingsTreeElement { export class SettingsTreeSettingElement extends SettingsTreeElement { setting: ISetting; - _displayCategory: string; - _displayLabel: string; + private _displayCategory: string; + private _displayLabel: string; /** * scopeValue || defaultValue, for rendering convenience. @@ -70,8 +75,9 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { description: string; valueType: 'enum' | 'string' | 'integer' | 'number' | 'boolean' | 'exclude' | 'complex' | 'nullable-integer' | 'nullable-number'; - constructor(setting: ISetting, parent: SettingsTreeGroupElement, inspectResult: IInspectResult) { + constructor(setting: ISetting, parent: SettingsTreeGroupElement, index: number, inspectResult: IInspectResult) { super(); + this.index = index; this.setting = setting; this.parent = parent; this.id = sanitizeId(parent.id + '_' + setting.key); @@ -193,7 +199,7 @@ export function countSettingGroupChildrenWithPredicate(tree: SettingsTreeGroupEl export class SettingsTreeModel { protected _root: SettingsTreeGroupElement; - private _treeElementsById = new Map(); + protected _treeElementsById = new Map(); private _treeElementsBySettingName = new Map(); private _tocRoot: ITOCEntry; @@ -243,6 +249,8 @@ export class SettingsTreeModel { private createSettingsTreeGroupElement(tocEntry: ITOCEntry, parent?: SettingsTreeGroupElement): SettingsTreeGroupElement { const element = new SettingsTreeGroupElement(); + const index = this._treeElementsById.size; + element.index = index; element.id = tocEntry.id; element.label = tocEntry.label; element.parent = parent; @@ -273,8 +281,9 @@ export class SettingsTreeModel { } private createSettingsTreeSettingElement(setting: ISetting, parent: SettingsTreeGroupElement): SettingsTreeSettingElement { + const index = this._treeElementsById.size; const inspectResult = inspectSetting(setting.key, this._viewState.settingsTarget, this._configurationService); - const element = new SettingsTreeSettingElement(setting, parent, inspectResult); + const element = new SettingsTreeSettingElement(setting, parent, index, inspectResult); this._treeElementsById.set(element.id, element); const nameElements = this._treeElementsBySettingName.get(setting.key) || []; @@ -449,8 +458,11 @@ export class SearchResultModel extends SettingsTreeModel { if (this.newExtensionSearchResults && this.newExtensionSearchResults.filterMatches.length) { const newExtElement = new SettingsTreeNewExtensionsElement(); + newExtElement.index = this._treeElementsById.size; newExtElement.parent = this._root; newExtElement.id = 'newExtensions'; + this._treeElementsById.set(newExtElement.id, newExtElement); + const resultExtensionIds = this.newExtensionSearchResults.filterMatches .map(result => (result.setting)) .filter(setting => setting.extensionName && setting.extensionPublisher)