From 2d550a7579841adc0d0fe6664bee0375363e6066 Mon Sep 17 00:00:00 2001 From: frosch Date: Sat, 27 Oct 2012 15:26:17 +0000 Subject: (svn r24632) -Feature: Add text filtering to advanced settings. --- src/lang/english.txt | 1 + src/settings_gui.cpp | 159 ++++++++++++++++++++++++++++++++++++++---- src/stringfilter.cpp | 17 ++++- src/stringfilter_type.h | 2 + src/widgets/settings_widget.h | 1 + 5 files changed, 164 insertions(+), 16 deletions(-) diff --git a/src/lang/english.txt b/src/lang/english.txt index 3245f9548..ab06135ae 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -1103,6 +1103,7 @@ STR_WARNING_DIFFICULTY_TO_CUSTOM :{WHITE}This act # Advanced settings window STR_CONFIG_SETTING_CAPTION :{WHITE}Advanced Settings +STR_CONFIG_SETTING_FILTER_TITLE :{BLACK}Filter string: STR_CONFIG_SETTING_EXPAND_ALL :{BLACK}Expand all STR_CONFIG_SETTING_COLLAPSE_ALL :{BLACK}Collapse all STR_CONFIG_SETTING_NO_EXPLANATION_AVAILABLE_HELPTEXT :(no explanation available) diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp index 7739ff6df..28c17c639 100644 --- a/src/settings_gui.cpp +++ b/src/settings_gui.cpp @@ -35,7 +35,8 @@ #include "blitter/factory.hpp" #include "language.h" #include "textfile_gui.h" - +#include "stringfilter_type.h" +#include "querystring_gui.h" static const StringID _units_dropdown[] = { @@ -968,6 +969,7 @@ enum SettingEntryFlags { SEF_BUTTONS_MASK = (SEF_LEFT_DEPRESSED | SEF_RIGHT_DEPRESSED), ///< Bit-mask for button flags SEF_LAST_FIELD = 0x04, ///< This entry is the last one in a (sub-)page + SEF_FILTERED = 0x08, ///< Entry is hidden by the string filter /* Entry kind */ SEF_SETTING_KIND = 0x10, ///< Entry kind: Entry is a setting @@ -1003,17 +1005,26 @@ struct SettingEntry { SettingEntry(const char *nm); SettingEntry(SettingsPage *sub, StringID title); - void Init(byte level, bool last_field); + void Init(byte level); void FoldAll(); void UnFoldAll(); void SetButtons(byte new_val); + /** + * Set whether this is the last visible entry of the parent node. + * @param last_field Value to set + */ + void SetLastField(bool last_field) { if (last_field) SETBITS(this->flags, SEF_LAST_FIELD); else CLRBITS(this->flags, SEF_LAST_FIELD); } + uint Length() const; void GetFoldingState(bool &all_folded, bool &all_unfolded) const; bool IsVisible(const SettingEntry *item) const; SettingEntry *FindEntry(uint row, uint *cur_row); uint GetMaxHelpHeight(int maxw); + bool IsFiltered() const; + bool UpdateFilterState(StringFilter &filter, bool force_visible); + uint Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, uint cur_row, uint parent_last, SettingEntry *selected); /** @@ -1047,6 +1058,8 @@ struct SettingsPage { SettingEntry *FindEntry(uint row, uint *cur_row) const; uint GetMaxHelpHeight(int maxw); + bool UpdateFilterState(StringFilter &filter, bool force_visible); + uint Draw(GameSettings *settings_ptr, int base_x, int base_y, int max_x, uint first_row, uint max_row, SettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const; }; @@ -1083,12 +1096,10 @@ SettingEntry::SettingEntry(SettingsPage *sub, StringID title) /** * Initialization of a setting entry * @param level Page nesting level of this entry - * @param last_field Boolean indicating this entry is the last at the (sub-)page */ -void SettingEntry::Init(byte level, bool last_field) +void SettingEntry::Init(byte level) { this->level = level; - if (last_field) this->flags |= SEF_LAST_FIELD; switch (this->flags & SEF_KIND_MASK) { case SEF_SETTING_KIND: @@ -1102,9 +1113,10 @@ void SettingEntry::Init(byte level, bool last_field) } } -/** Recursively close all folds of sub-pages */ +/** Recursively close all (filtered) folds of sub-pages */ void SettingEntry::FoldAll() { + if (this->IsFiltered()) return; switch (this->flags & SEF_KIND_MASK) { case SEF_SETTING_KIND: break; @@ -1118,9 +1130,10 @@ void SettingEntry::FoldAll() } } -/** Recursively open all folds of sub-pages */ +/** Recursively open all (filtered) folds of sub-pages */ void SettingEntry::UnFoldAll() { + if (this->IsFiltered()) return; switch (this->flags & SEF_KIND_MASK) { case SEF_SETTING_KIND: break; @@ -1135,12 +1148,13 @@ void SettingEntry::UnFoldAll() } /** - * Recursively accumulate the folding state of the tree. + * Recursively accumulate the folding state of the (filtered) tree. * @param[in,out] all_folded Set to false, if one entry is not folded. * @param[in,out] all_unfolded Set to false, if one entry is folded. */ void SettingEntry::GetFoldingState(bool &all_folded, bool &all_unfolded) const { + if (this->IsFiltered()) return; switch (this->flags & SEF_KIND_MASK) { case SEF_SETTING_KIND: break; @@ -1159,13 +1173,14 @@ void SettingEntry::GetFoldingState(bool &all_folded, bool &all_unfolded) const } /** - * Check whether an entry is visible and not folded away. + * Check whether an entry is visible and not folded or filtered away. * Note: This does not consider the scrolling range; it might still require scrolling ot make the setting really visible. * @param item Entry to search for. * @return true if entry is visible. */ bool SettingEntry::IsVisible(const SettingEntry *item) const { + if (this->IsFiltered()) return false; if (this == item) return true; switch (this->flags & SEF_KIND_MASK) { @@ -1190,9 +1205,10 @@ void SettingEntry::SetButtons(byte new_val) this->flags = (this->flags & ~SEF_BUTTONS_MASK) | new_val; } -/** Return numbers of rows needed to display the entry */ +/** Return numbers of rows needed to display the (filtered) entry */ uint SettingEntry::Length() const { + if (this->IsFiltered()) return 0; switch (this->flags & SEF_KIND_MASK) { case SEF_SETTING_KIND: return 1; @@ -1208,10 +1224,11 @@ uint SettingEntry::Length() const * Find setting entry at row \a row_num * @param row_num Index of entry to return * @param cur_row Current row number - * @return The requested setting entry or \c NULL if it not found + * @return The requested setting entry or \c NULL if it not found (folded or filtered) */ SettingEntry *SettingEntry::FindEntry(uint row_num, uint *cur_row) { + if (this->IsFiltered()) return NULL; if (row_num == *cur_row) return this; switch (this->flags & SEF_KIND_MASK) { @@ -1245,6 +1262,61 @@ uint SettingEntry::GetMaxHelpHeight(int maxw) } } +/** + * Check whether an entry is hidden due to filters + * @return true if hidden. + */ +bool SettingEntry::IsFiltered() const +{ + return this->flags & SEF_FILTERED; +} + +/** + * Update the filter state. + * @param filter String filter + * @param force_visible Whether to force all items visible, no matter what + * @return true if item remains visible + */ +bool SettingEntry::UpdateFilterState(StringFilter &filter, bool force_visible) +{ + CLRBITS(this->flags, SEF_FILTERED); + + bool visible = true; + switch (this->flags & SEF_KIND_MASK) { + case SEF_SETTING_KIND: { + if (force_visible || filter.IsEmpty()) break; + + filter.ResetState(); + + const SettingDesc *sd = this->d.entry.setting; + const SettingDescBase *sdb = &sd->desc; + + SetDParam(0, STR_EMPTY); + filter.AddLine(sdb->str); + + filter.AddLine(this->GetHelpText()); + + visible = filter.GetState(); + break; + } + case SEF_SUBTREE_KIND: { + if (!force_visible && !filter.IsEmpty()) { + filter.ResetState(); + filter.AddLine(this->d.sub.title); + force_visible = filter.GetState(); + } + visible = this->d.sub.page->UpdateFilterState(filter, force_visible); + break; + } + default: NOT_REACHED(); + } + + if (!visible) SETBITS(this->flags, SEF_FILTERED); + return visible; +} + + + /** * Draw a row in the settings panel. * @@ -1274,6 +1346,7 @@ uint SettingEntry::GetMaxHelpHeight(int maxw) */ uint SettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int base_y, uint first_row, uint max_row, uint cur_row, uint parent_last, SettingEntry *selected) { + if (this->IsFiltered()) return cur_row; if (cur_row >= max_row) return cur_row; bool rtl = _current_text_dir == TD_RTL; @@ -1419,7 +1492,7 @@ void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, void SettingsPage::Init(byte level) { for (uint field = 0; field < this->num; field++) { - this->entries[field].Init(level, field + 1 == num); + this->entries[field].Init(level); } } @@ -1452,7 +1525,26 @@ void SettingsPage::GetFoldingState(bool &all_folded, bool &all_unfolded) const } /** - * Check whether an entry is visible and not folded away. + * Update the filter state. + * @param filter String filter + * @param force_visible Whether to force all items visible, no matter what + * @return true if item remains visible + */ +bool SettingsPage::UpdateFilterState(StringFilter &filter, bool force_visible) +{ + bool visible = force_visible; + bool first_visible = true; + for (int field = this->num - 1; field >= 0; field--) { + visible |= this->entries[field].UpdateFilterState(filter, force_visible); + this->entries[field].SetLastField(first_visible); + if (visible && first_visible) first_visible = false; + } + return visible; +} + + +/** + * Check whether an entry is visible and not folded or filtered away. * Note: This does not consider the scrolling range; it might still require scrolling ot make the setting really visible. * @param item Entry to search for. * @return true if entry is visible. @@ -1789,7 +1881,7 @@ static SettingEntry _settings_main[] = { /** Main page, holding all advanced settings */ static SettingsPage _settings_main_page = {_settings_main, lengthof(_settings_main)}; -struct GameSettingsWindow : Window { +struct GameSettingsWindow : QueryStringBaseWindow { static const int SETTINGTREE_LEFT_OFFSET = 5; ///< Position of left edge of setting values static const int SETTINGTREE_RIGHT_OFFSET = 5; ///< Position of right edge of setting values static const int SETTINGTREE_TOP_OFFSET = 5; ///< Position of top edge of setting values @@ -1803,9 +1895,11 @@ struct GameSettingsWindow : Window { SettingEntry *valuedropdown_entry; ///< If non-NULL, pointer to the value for which a dropdown window is currently opened. bool closing_dropdown; ///< True, if the dropdown list is currently closing. + StringFilter string_filter; ///< Text filter for settings. + Scrollbar *vscroll; - GameSettingsWindow(const WindowDesc *desc) : Window() + GameSettingsWindow(const WindowDesc *desc) : QueryStringBaseWindow(50) { static bool first_time = true; @@ -1829,6 +1923,9 @@ struct GameSettingsWindow : Window { this->vscroll = this->GetScrollbar(WID_GS_SCROLLBAR); this->FinishInitNested(desc, WN_GAME_OPTIONS_GAME_SETTINGS); + this->text.Initialize(this->edit_str_buf, this->edit_str_size); + this->SetFocusedWidget(WID_GS_FILTER); + this->InvalidateData(); } @@ -1871,6 +1968,7 @@ struct GameSettingsWindow : Window { this->valuedropdown_entry = NULL; } this->DrawWidgets(); + this->DrawEditBox(WID_GS_FILTER); } virtual void DrawWidget(const Rect &r, int widget) const @@ -2147,6 +2245,8 @@ struct GameSettingsWindow : Window { { if (!gui_scope) return; + _settings_main_page.UpdateFilterState(string_filter, false); + this->vscroll->SetCount(_settings_main_page.Length()); if (this->last_clicked != NULL && !_settings_main_page.IsVisible(this->last_clicked)) { @@ -2160,6 +2260,27 @@ struct GameSettingsWindow : Window { this->SetWidgetDisabledState(WID_GS_COLLAPSE_ALL, all_folded); } + virtual void OnMouseLoop() + { + this->HandleEditBox(WID_GS_FILTER); + } + + virtual EventState OnKeyPress(uint16 key, uint16 keycode) + { + /* Handle editbox input */ + EventState state = ES_NOT_HANDLED; + if (this->HandleEditBoxKey(WID_GS_FILTER, key, keycode, state) == HEBR_EDITING) { + this->OnOSKInput(WID_GS_FILTER); + } + return state; + } + + virtual void OnOSKInput(int wid) + { + string_filter.SetFilterTerm(this->edit_str_buf); + this->InvalidateData(); + } + virtual void OnResize() { this->vscroll->SetCapacityFromWidget(this, WID_GS_OPTIONSPANEL, SETTINGTREE_TOP_OFFSET + SETTINGTREE_BOTTOM_OFFSET); @@ -2173,6 +2294,14 @@ static const NWidgetPart _nested_settings_selection_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_MAUVE), NWidget(WWT_CAPTION, COLOUR_MAUVE), SetDataTip(STR_CONFIG_SETTING_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), EndContainer(), + NWidget(WWT_PANEL, COLOUR_MAUVE), + NWidget(NWID_HORIZONTAL), SetPadding(WD_TEXTPANEL_TOP, 0, WD_TEXTPANEL_BOTTOM, 0), + SetPIP(WD_FRAMETEXT_LEFT, WD_FRAMETEXT_RIGHT, WD_FRAMETEXT_RIGHT), + NWidget(WWT_TEXT, COLOUR_MAUVE), SetFill(0, 1), SetDataTip(STR_CONFIG_SETTING_FILTER_TITLE, STR_NULL), + NWidget(WWT_EDITBOX, COLOUR_MAUVE, WID_GS_FILTER), SetFill(1, 0), SetMinimalSize(50, 12), SetResize(1, 0), + SetDataTip(STR_LIST_FILTER_OSKTITLE, STR_LIST_FILTER_TOOLTIP), + EndContainer(), + EndContainer(), NWidget(NWID_HORIZONTAL), NWidget(WWT_PANEL, COLOUR_MAUVE, WID_GS_OPTIONSPANEL), SetMinimalSize(400, 174), SetScrollbar(WID_GS_SCROLLBAR), EndContainer(), NWidget(NWID_VERTICAL), diff --git a/src/stringfilter.cpp b/src/stringfilter.cpp index 616fd9854..e021c57b5 100644 --- a/src/stringfilter.cpp +++ b/src/stringfilter.cpp @@ -11,7 +11,9 @@ #include "stdafx.h" #include "string_func.h" +#include "strings_func.h" #include "stringfilter_type.h" +#include "gfx_func.h" static const WChar STATE_WHITESPACE = ' '; static const WChar STATE_WORD = 'w'; @@ -117,4 +119,17 @@ void StringFilter::AddLine(const char *str) } } - +/** + * Pass another text line from the current item to the filter. + * + * You can call this multiple times for a single item, if the filter shall apply to multiple things. + * Before processing the next item you have to call ResetState(). + * + * @param str Another line from the item. + */ +void StringFilter::AddLine(StringID str) +{ + char buffer[DRAW_STRING_BUFFER]; + GetString(buffer, str, lastof(buffer)); + AddLine(buffer); +} diff --git a/src/stringfilter_type.h b/src/stringfilter_type.h index 7995ffdf6..f78b133cb 100644 --- a/src/stringfilter_type.h +++ b/src/stringfilter_type.h @@ -13,6 +13,7 @@ #define STRINGFILTER_TYPE_H #include "core/smallvec_type.hpp" +#include "strings_type.h" /** * String filter and state. @@ -61,6 +62,7 @@ public: void ResetState(); void AddLine(const char *str); + void AddLine(StringID str); /** * Get the matching state of the current item. diff --git a/src/widgets/settings_widget.h b/src/widgets/settings_widget.h index 2a9173b49..aa1f09a10 100644 --- a/src/widgets/settings_widget.h +++ b/src/widgets/settings_widget.h @@ -52,6 +52,7 @@ enum GameDifficultyWidgets { /** Widgets of the #GameSettingsWindow class. */ enum GameSettingsWidgets { + WID_GS_FILTER, ///< Text filter. WID_GS_OPTIONSPANEL, ///< Panel widget containing the option lists. WID_GS_SCROLLBAR, ///< Scrollbar. WID_GS_HELP_TEXT, ///< Information area to display help text of the selected option. -- cgit v1.2.3-70-g09d2