From 84bcaf10308c72f1eb7f2c4685346a9d5b63ebb8 Mon Sep 17 00:00:00 2001 From: vince-fugnitto Date: Tue, 9 Mar 2021 09:40:59 -0500 Subject: [PATCH] preferences: restore preference state The following commit restores the `preferences-ui` state when reloading the application. The state includes: - restoring the search term. - restoring the currently selected workspace scope. - restoring the editor location (through the `firstVisibleChildID`. Signed-off-by: vince-fugnitto --- .../views/preference-editor-widget.tsx | 19 +++++++++- .../views/preference-scope-tabbar-widget.tsx | 18 ++++++++-- .../views/preference-searchbar-widget.tsx | 36 +++++++++++++++++-- .../src/browser/views/preference-widget.tsx | 30 +++++++++++++--- 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/packages/preferences/src/browser/views/preference-editor-widget.tsx b/packages/preferences/src/browser/views/preference-editor-widget.tsx index 6a3c4bd741bbc..f6b1c43788728 100644 --- a/packages/preferences/src/browser/views/preference-editor-widget.tsx +++ b/packages/preferences/src/browser/views/preference-editor-widget.tsx @@ -27,6 +27,7 @@ import { PreferenceItem, TreeNode, ExpandableTreeNode, + StatefulWidget, } from '@theia/core/lib/browser'; import { Message, } from '@theia/core/lib/browser/widgets/widget'; import { SinglePreferenceDisplayFactory } from './components/single-preference-display-factory'; @@ -36,8 +37,12 @@ import { Emitter } from '@theia/core'; const HEADER_CLASS = 'settings-section-category-title'; const SUBHEADER_CLASS = 'settings-section-subcategory-title'; +export interface PreferencesEditorState { + firstVisibleChildID: string, +} + @injectable() -export class PreferencesEditorWidget extends ReactWidget { +export class PreferencesEditorWidget extends ReactWidget implements StatefulWidget { static readonly ID = 'settings.editor'; static readonly LABEL = 'Settings Editor'; @@ -218,4 +223,16 @@ export class PreferencesEditorWidget extends ReactWidget { } } } + + storeState(): PreferencesEditorState { + return { + firstVisibleChildID: this.firstVisibleChildID, + }; + } + + restoreState(oldState: PreferencesEditorState): void { + this.firstVisibleChildID = oldState.firstVisibleChildID; + this.handleDisplayChange(); + } + } diff --git a/packages/preferences/src/browser/views/preference-scope-tabbar-widget.tsx b/packages/preferences/src/browser/views/preference-scope-tabbar-widget.tsx index c5abb191c3818..223be84a10df1 100644 --- a/packages/preferences/src/browser/views/preference-scope-tabbar-widget.tsx +++ b/packages/preferences/src/browser/views/preference-scope-tabbar-widget.tsx @@ -16,7 +16,7 @@ import { inject, injectable, postConstruct } from 'inversify'; import { TabBar, Widget, Title } from '@phosphor/widgets'; -import { PreferenceScope, Message, ContextMenuRenderer, LabelProvider } from '@theia/core/lib/browser'; +import { PreferenceScope, Message, ContextMenuRenderer, LabelProvider, StatefulWidget } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service'; import URI from '@theia/core/lib/common/uri'; import { FileStat } from '@theia/filesystem/lib/common/files'; @@ -41,8 +41,12 @@ const SINGLE_FOLDER_TAB_CLASSNAME = `${PREFERENCE_TAB_CLASSNAME} ${GENERAL_FOLDE const UNSELECTED_FOLDER_DROPDOWN_CLASSNAME = `${PREFERENCE_TAB_CLASSNAME} ${GENERAL_FOLDER_TAB_CLASSNAME} ${FOLDER_DROPDOWN_CLASSNAME}`; const SELECTED_FOLDER_DROPDOWN_CLASSNAME = `${PREFERENCE_TAB_CLASSNAME} ${GENERAL_FOLDER_TAB_CLASSNAME} ${LABELED_FOLDER_TAB_CLASSNAME} ${FOLDER_DROPDOWN_CLASSNAME}`; +export interface PreferencesScopeTabBarState { + scopeDetails: Preference.SelectedScopeDetails; +} + @injectable() -export class PreferencesScopeTabBar extends TabBar { +export class PreferencesScopeTabBar extends TabBar implements StatefulWidget { static ID = 'preferences-scope-tab-bar'; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @@ -248,4 +252,14 @@ export class PreferencesScopeTabBar extends TabBar { protected emitNewScope(): void { this.onScopeChangedEmitter.fire(this.currentSelection); } + + storeState(): PreferencesScopeTabBarState { + return { + scopeDetails: this.currentScope + }; + } + + restoreState(oldState: PreferencesScopeTabBarState): void { + this.setNewScopeSelection(oldState.scopeDetails); + } } diff --git a/packages/preferences/src/browser/views/preference-searchbar-widget.tsx b/packages/preferences/src/browser/views/preference-searchbar-widget.tsx index 19a94020756ac..b696b4d4c1198 100644 --- a/packages/preferences/src/browser/views/preference-searchbar-widget.tsx +++ b/packages/preferences/src/browser/views/preference-searchbar-widget.tsx @@ -15,13 +15,17 @@ ********************************************************************************/ import { injectable, postConstruct } from 'inversify'; -import { ReactWidget } from '@theia/core/lib/browser'; +import { ReactWidget, StatefulWidget } from '@theia/core/lib/browser'; import * as React from 'react'; import { debounce } from 'lodash'; import { Disposable, Emitter } from '@theia/core'; +export interface PreferencesSearchbarState { + searchTerm: string; +} + @injectable() -export class PreferencesSearchbarWidget extends ReactWidget { +export class PreferencesSearchbarWidget extends ReactWidget implements StatefulWidget { static readonly ID = 'settings.header'; static readonly LABEL = 'Settings Header'; static readonly SEARCHBAR_ID = 'preference-searchbar'; @@ -110,6 +114,21 @@ export class PreferencesSearchbarWidget extends ReactWidget { return !!this.searchbarRef.current?.value; } + protected getSearchTerm(): string { + const search = document.getElementById(PreferencesSearchbarWidget.SEARCHBAR_ID) as HTMLInputElement; + return search?.value; + } + + protected updateSearchTerm(searchTerm: string): void { + const search = document.getElementById(PreferencesSearchbarWidget.SEARCHBAR_ID) as HTMLInputElement; + if (!search) { + return; + } + search.value = searchTerm; + this.search(search.value); + this.update(); + } + render(): React.ReactNode { const optionContainer = this.renderOptionContainer(); return ( @@ -138,4 +157,17 @@ export class PreferencesSearchbarWidget extends ReactWidget { this.resultsCount = count; this.update(); } + + storeState(): PreferencesSearchbarState { + return { + searchTerm: this.getSearchTerm() + }; + } + + restoreState(oldState: PreferencesSearchbarState): void { + const searchInputExists = this.onDidChangeVisibility(() => { + this.updateSearchTerm(oldState.searchTerm || ''); + searchInputExists.dispose(); + }); + } } diff --git a/packages/preferences/src/browser/views/preference-widget.tsx b/packages/preferences/src/browser/views/preference-widget.tsx index b4f2907802e92..3621dbaf675ab 100644 --- a/packages/preferences/src/browser/views/preference-widget.tsx +++ b/packages/preferences/src/browser/views/preference-widget.tsx @@ -15,17 +15,23 @@ ********************************************************************************/ import { postConstruct, injectable, inject } from 'inversify'; -import { Panel, Widget, Message, } from '@theia/core/lib/browser'; -import { PreferencesEditorWidget } from './preference-editor-widget'; +import { Panel, Widget, Message, StatefulWidget, } from '@theia/core/lib/browser'; +import { PreferencesEditorState, PreferencesEditorWidget } from './preference-editor-widget'; import { PreferencesTreeWidget } from './preference-tree-widget'; -import { PreferencesSearchbarWidget } from './preference-searchbar-widget'; -import { PreferencesScopeTabBar } from './preference-scope-tabbar-widget'; +import { PreferencesSearchbarState, PreferencesSearchbarWidget } from './preference-searchbar-widget'; +import { PreferencesScopeTabBar, PreferencesScopeTabBarState } from './preference-scope-tabbar-widget'; import { Preference } from '../util/preference-types'; const SHADOW_CLASSNAME = 'with-shadow'; +interface PreferencesWidgetState { + scopeTabBarState: PreferencesScopeTabBarState, + editorState: PreferencesEditorState, + searchbarWidgetState: PreferencesSearchbarState, +} + @injectable() -export class PreferencesWidget extends Panel { +export class PreferencesWidget extends Panel implements StatefulWidget { /** * The widget `id`. */ @@ -89,4 +95,18 @@ export class PreferencesWidget extends Panel { this.update(); } + + storeState(): PreferencesWidgetState { + return { + scopeTabBarState: this.tabBarWidget.storeState(), + editorState: this.editorWidget.storeState(), + searchbarWidgetState: this.searchbarWidget.storeState(), + }; + } + + restoreState(state: PreferencesWidgetState): void { + this.tabBarWidget.restoreState(state.scopeTabBarState); + this.editorWidget.restoreState(state.editorState); + this.searchbarWidget.restoreState(state.searchbarWidgetState); + } }