Skip to content

Commit 62468cb

Browse files
committed
Sync settings tree scroll position to TOC - microsoft#64749
1 parent c1c3e5e commit 62468cb

File tree

2 files changed

+84
-34
lines changed

2 files changed

+84
-34
lines changed

src/vs/workbench/parts/preferences/browser/settingsTree.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer<SettingsT
257257
static readonly CONTROL_SELECTOR = '.' + AbstractSettingRenderer.CONTROL_CLASS;
258258

259259
static readonly SETTING_KEY_ATTR = 'data-key';
260+
static readonly SETTING_ID_ATTR = 'data-id';
260261

261262
private readonly _onDidClickOverrideElement = new Emitter<ISettingOverrideClickEvent>();
262263
readonly onDidClickOverrideElement: Event<ISettingOverrideClickEvent> = this._onDidClickOverrideElement.event;
@@ -385,6 +386,7 @@ export abstract class AbstractSettingRenderer implements ITreeRenderer<SettingsT
385386
DOM.toggleClass(template.containerElement, 'is-configured', element.isConfigured);
386387
DOM.toggleClass(template.containerElement, 'is-expanded', true);
387388
template.containerElement.setAttribute(AbstractSettingRenderer.SETTING_KEY_ATTR, element.setting.key);
389+
template.containerElement.setAttribute(AbstractSettingRenderer.SETTING_ID_ATTR, element.id);
388390

389391
const titleTooltip = setting.key + (element.isConfigured ? ' - Modified' : '');
390392
template.categoryElement.textContent = element.displayCategory && (element.displayCategory + ': ');
@@ -1086,6 +1088,11 @@ export class SettingTreeRenderers {
10861088
const settingElement = this.getSettingDOMElementForDOMElement(element);
10871089
return settingElement && settingElement.getAttribute(AbstractSettingRenderer.SETTING_KEY_ATTR);
10881090
}
1091+
1092+
getIdForDOMElementInSetting(element: HTMLElement): string {
1093+
const settingElement = this.getSettingDOMElementForDOMElement(element);
1094+
return settingElement && settingElement.getAttribute(AbstractSettingRenderer.SETTING_ID_ATTR);
1095+
}
10891096
}
10901097

10911098
function renderValidations(dataElement: SettingsTreeSettingElement, template: ISettingTextItemTemplate, calledOnStartup: boolean, originalAriaLabel: string) {

src/vs/workbench/parts/preferences/electron-browser/settingsEditor2.ts

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { attachSuggestEnabledInputBoxStyler, SuggestEnabledInput } from 'vs/work
3232
import { SettingsTarget, SettingsTargetsWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
3333
import { commonlyUsedData, tocData } from 'vs/workbench/parts/preferences/browser/settingsLayout';
3434
import { AbstractSettingRenderer, ISettingLinkClickEvent, ISettingOverrideClickEvent, resolveExtensionsSettings, resolveSettingsTree, SettingsTree, SettingTreeRenderers } from 'vs/workbench/parts/preferences/browser/settingsTree';
35-
import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel } from 'vs/workbench/parts/preferences/browser/settingsTreeModels';
35+
import { ISettingsEditorViewState, parseQuery, SearchResultIdx, SearchResultModel, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeModel, SettingsTreeSettingElement } from 'vs/workbench/parts/preferences/browser/settingsTreeModels';
3636
import { settingsTextInputBorder } from 'vs/workbench/parts/preferences/browser/settingsWidgets';
3737
import { createTOCIterator, TOCTree, TOCTreeModel } from 'vs/workbench/parts/preferences/browser/tocTree';
3838
import { CONTEXT_SETTINGS_EDITOR, CONTEXT_SETTINGS_SEARCH_FOCUS, CONTEXT_TOC_ROW_FOCUS, IPreferencesSearchService, ISearchProvider, MODIFIED_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU } from 'vs/workbench/parts/preferences/common/preferences';
@@ -124,6 +124,9 @@ export class SettingsEditor2 extends BaseEditor {
124124

125125
private editorMemento: IEditorMemento<ISettingsEditor2State>;
126126

127+
private tocFocusedElement: SettingsTreeGroupElement;
128+
private settingsTreeScrollTop = 0;
129+
127130
constructor(
128131
@ITelemetryService telemetryService: ITelemetryService,
129132
@IConfigurationService private readonly configurationService: IConfigurationService,
@@ -542,21 +545,22 @@ export class SettingsEditor2 extends BaseEditor {
542545
this.viewState));
543546

544547
this._register(this.tocTree.onDidChangeFocus(e => {
545-
this.tocTree.setSelection(e.elements);
546548
const element: SettingsTreeGroupElement = e.elements[0];
549+
if (this.tocFocusedElement === e.elements[0]) {
550+
return;
551+
}
552+
553+
this.tocFocusedElement = element;
554+
this.tocTree.setSelection(e.elements);
547555
if (this.searchResultModel) {
548556
if (this.viewState.filterToCategory !== element) {
549557
this.viewState.filterToCategory = element;
550558
this.renderTree();
551559
this.settingsTree.scrollTop = 0;
552560
}
553-
} else if (element) {
561+
} else if (element && (!e.browserEvent || !(<any>e.browserEvent).fromScroll)) {
554562
this.settingsTree.reveal(element, 0);
555563
}
556-
557-
// else if (element && (!e.payload || !e.payload.fromScroll)) {
558-
// this.settingsTree.reveal(element, 0);
559-
// }
560564
}));
561565

562566
this._register(this.tocTree.onDidFocus(() => {
@@ -606,7 +610,17 @@ export class SettingsEditor2 extends BaseEditor {
606610
this.settingsTree.getHTMLElement().attributes.removeNamedItem('tabindex');
607611

608612
this._register(this.settingsTree.onDidScroll(() => {
609-
this.updateTreeScrollSync();
613+
if (this.settingsTree.scrollTop === this.settingsTreeScrollTop) {
614+
return;
615+
}
616+
617+
this.settingsTreeScrollTop = this.settingsTree.scrollTop;
618+
619+
// setTimeout because calling setChildren on the settingsTree can trigger onDidScroll, so it fires when
620+
// setChildren has called on the settings tree but not the toc tree yet, so their rendered elements are out of sync
621+
setTimeout(() => {
622+
this.updateTreeScrollSync();
623+
}, 0);
610624
}));
611625
}
612626

@@ -642,29 +656,56 @@ export class SettingsEditor2 extends BaseEditor {
642656
return;
643657
}
644658

645-
// this.updateTreePagingByScroll();
646-
647-
// const element = this.tocTreeModel.children[0];
648-
// const elementToSync = this.settingsTree.getFirstVisibleElement();
649-
// const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent :
650-
// elementToSync instanceof SettingsTreeGroupElement ? elementToSync :
651-
// null;
652-
653-
// if (element && this.tocTree.getSelection()[0] !== element) {
654-
// this.tocTree.reveal(element);
655-
// const elementTop = this.tocTree.getRelativeTop(element);
656-
// collapseAll(this.tocTree, element);
657-
// if (elementTop < 0 || elementTop > 1) {
658-
// this.tocTree.reveal(element);
659-
// } else {
660-
// this.tocTree.reveal(element, elementTop);
661-
// }
662-
663-
// this.tocTree.expand(element);
664-
665-
// this.tocTree.setSelection([element]);
666-
// this.tocTree.setFocus(element, { fromScroll: true });
667-
// }
659+
// Hack, see https://github.com/Microsoft/vscode/issues/64749
660+
const settingItems = this.settingsTree.getHTMLElement().querySelectorAll('.setting-item');
661+
const firstEl = settingItems[1] || settingItems[0];
662+
if (!firstEl) {
663+
return;
664+
}
665+
666+
const firstSettingId = this.settingRenderers.getIdForDOMElementInSetting(<HTMLElement>firstEl);
667+
const elementToSync = this.settingsTreeModel.getElementById(firstSettingId);
668+
const element = elementToSync instanceof SettingsTreeSettingElement ? elementToSync.parent :
669+
elementToSync instanceof SettingsTreeGroupElement ? elementToSync :
670+
null;
671+
672+
if (element && this.tocTree.getSelection()[0] !== element) {
673+
const ancestors = this.getAncestors(element);
674+
ancestors.forEach(e => this.tocTree.expand(<SettingsTreeGroupElement>e));
675+
676+
this.tocTree.reveal(element);
677+
const elementTop = this.tocTree.getRelativeTop(element);
678+
this.tocTree.collapseAll();
679+
680+
ancestors.forEach(e => this.tocTree.expand(<SettingsTreeGroupElement>e));
681+
if (elementTop < 0 || elementTop > 1) {
682+
this.tocTree.reveal(element);
683+
} else {
684+
this.tocTree.reveal(element, elementTop);
685+
}
686+
687+
this.tocTree.expand(element);
688+
689+
this.tocTree.setSelection([element]);
690+
691+
const fakeKeyboardEvent = new KeyboardEvent('keydown');
692+
(<any>fakeKeyboardEvent).fromScroll = true;
693+
this.tocTree.setFocus([element], fakeKeyboardEvent);
694+
}
695+
}
696+
697+
private getAncestors(element: SettingsTreeElement): SettingsTreeElement[] {
698+
const ancestors: any[] = [];
699+
700+
while (element.parent) {
701+
if (element.parent.id !== 'root') {
702+
ancestors.push(element.parent);
703+
}
704+
705+
element = element.parent;
706+
}
707+
708+
return ancestors.reverse();
668709
}
669710

670711
private updateChangedSetting(key: string, value: any): Promise<void> {
@@ -843,10 +884,11 @@ export class SettingsEditor2 extends BaseEditor {
843884
} else {
844885
this.settingsTreeModel = this.instantiationService.createInstance(SettingsTreeModel, this.viewState);
845886
this.settingsTreeModel.update(resolvedSettingsRoot);
846-
this.refreshTree();
847-
848887
this.tocTreeModel.settingsTreeRoot = this.settingsTreeModel.root as SettingsTreeGroupElement;
888+
849889
this.refreshTOCTree();
890+
this.refreshTree();
891+
850892
this.tocTree.collapseAll();
851893
}
852894

@@ -980,7 +1022,6 @@ export class SettingsEditor2 extends BaseEditor {
9801022

9811023
this.viewState.filterToCategory = null;
9821024
this.tocTreeModel.currentSearchModel = this.searchResultModel;
983-
this.refreshTOCTree();
9841025
this.onSearchModeToggled();
9851026

9861027
if (this.searchResultModel) {
@@ -995,6 +1036,8 @@ export class SettingsEditor2 extends BaseEditor {
9951036
this.refreshTree();
9961037
this.renderResultCountMessages();
9971038
}
1039+
1040+
this.refreshTOCTree();
9981041
}
9991042

10001043
return Promise.resolve(null);

0 commit comments

Comments
 (0)