changeset 19711:817b521c2b1a draft

(svn r24632) -Feature: Add text filtering to advanced settings.
author frosch <frosch@openttd.org>
date Sat, 27 Oct 2012 15:26:17 +0000
parents 426ee8a3f9cd
children e1b57eff3699
files src/lang/english.txt src/settings_gui.cpp src/stringfilter.cpp src/stringfilter_type.h src/widgets/settings_widget.h
diffstat 5 files changed, 164 insertions(+), 16 deletions(-) [+]
line wrap: on
line diff
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1103,6 +1103,7 @@
 
 # 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)
--- 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 @@
 	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 @@
 	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 @@
 	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 @@
 /**
  * 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 @@
 	}
 }
 
-/** 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 @@
 	}
 }
 
-/** 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 @@
 }
 
 /**
- * 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 @@
 }
 
 /**
- * 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 @@
 	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 @@
  * 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) {
@@ -1246,6 +1263,61 @@
 }
 
 /**
+ * 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.
  *
  * See SettingsPage::Draw() for an explanation about how drawing is performed.
@@ -1274,6 +1346,7 @@
  */
 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 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 @@
 }
 
 /**
- * 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 @@
 /** 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 @@
 	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 @@
 		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 @@
 			this->valuedropdown_entry = NULL;
 		}
 		this->DrawWidgets();
+		this->DrawEditBox(WID_GS_FILTER);
 	}
 
 	virtual void DrawWidget(const Rect &r, int widget) const
@@ -2147,6 +2245,8 @@
 	{
 		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 @@
 		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 @@
 		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),
--- 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 @@
 	}
 }
 
-
+/**
+ * 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);
+}
--- 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 @@
 
 	void ResetState();
 	void AddLine(const char *str);
+	void AddLine(StringID str);
 
 	/**
 	 * Get the matching state of the current item.
--- a/src/widgets/settings_widget.h
+++ b/src/widgets/settings_widget.h
@@ -52,6 +52,7 @@
 
 /** 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.