diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9a836c503a..9ca78e54d5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -396,6 +396,8 @@ add_files(
screenshot.h
screenshot_type.h
screenshot_pcx.cpp
+ settingentry_gui.cpp
+ settingentry_gui.h
settings.cpp
settings_cmd.h
settings_func.h
diff --git a/src/settingentry_gui.cpp b/src/settingentry_gui.cpp
new file mode 100644
index 0000000000..b4b711f77b
--- /dev/null
+++ b/src/settingentry_gui.cpp
@@ -0,0 +1,954 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file settingentry_gui.cpp Definitions of classes for handling display of individual configuration settings. */
+
+#include "stdafx.h"
+#include "company_base.h"
+#include "company_func.h"
+#include "settingentry_gui.h"
+#include "settings_gui.h"
+#include "settings_internal.h"
+#include "stringfilter_type.h"
+#include "strings_func.h"
+
+#include "table/sprites.h"
+
+#include "safeguards.h"
+
+
+/* == BaseSettingEntry methods == */
+
+/**
+ * Initialization of a setting entry
+ * @param level Page nesting level of this entry
+ */
+void BaseSettingEntry::Init(uint8_t level)
+{
+ this->level = level;
+}
+
+/**
+ * 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 to make the setting really visible.
+ * @param item Entry to search for.
+ * @return true if entry is visible.
+ */
+bool BaseSettingEntry::IsVisible(const BaseSettingEntry *item) const
+{
+ if (this->IsFiltered()) return false;
+ return this == item;
+}
+
+/**
+ * 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 nullptr if it not found (folded or filtered)
+ */
+BaseSettingEntry *BaseSettingEntry::FindEntry(uint row_num, uint *cur_row)
+{
+ if (this->IsFiltered()) return nullptr;
+ if (row_num == *cur_row) return this;
+ (*cur_row)++;
+ return nullptr;
+}
+
+/**
+ * Draw a row in the settings panel.
+ *
+ * The scrollbar uses rows of the page, while the page data structure is a tree of #SettingsPage and #SettingEntry objects.
+ * As a result, the drawing routing traverses the tree from top to bottom, counting rows in \a cur_row until it reaches \a first_row.
+ * Then it enables drawing rows while traversing until \a max_row is reached, at which point drawing is terminated.
+ *
+ * The \a parent_last parameter ensures that the vertical lines at the left are
+ * only drawn when another entry follows, that it prevents output like
+ * \verbatim
+ * |-- setting
+ * |-- (-) - Title
+ * | |-- setting
+ * | |-- setting
+ * \endverbatim
+ * The left-most vertical line is not wanted. It is prevented by setting the
+ * appropriate bit in the \a parent_last parameter.
+ *
+ * @param settings_ptr Pointer to current values of all settings
+ * @param left Left-most position in window/panel to start drawing \a first_row
+ * @param right Right-most x position to draw strings at.
+ * @param y Upper-most position in window/panel to start drawing \a first_row
+ * @param first_row First row number to draw
+ * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
+ * @param selected Selected entry by the user.
+ * @param cur_row Current row number (internal variable)
+ * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
+ * @return Row number of the next row to draw
+ */
+uint BaseSettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
+{
+ if (this->IsFiltered()) return cur_row;
+ if (cur_row >= max_row) return cur_row;
+
+ bool rtl = _current_text_dir == TD_RTL;
+ int offset = (rtl ? -(int)_circle_size.width : (int)_circle_size.width) / 2;
+ int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
+
+ int x = rtl ? right : left;
+ if (cur_row >= first_row) {
+ int colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
+ y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
+
+ /* Draw vertical for parent nesting levels */
+ for (uint lvl = 0; lvl < this->level; lvl++) {
+ if (!HasBit(parent_last, lvl)) GfxDrawLine(x + offset, y, x + offset, y + SETTING_HEIGHT - 1, colour);
+ x += level_width;
+ }
+ /* draw own |- prefix */
+ int halfway_y = y + SETTING_HEIGHT / 2;
+ int bottom_y = flags.Test(SettingEntryFlag::LastField) ? halfway_y : y + SETTING_HEIGHT - 1;
+ GfxDrawLine(x + offset, y, x + offset, bottom_y, colour);
+ /* Small horizontal line from the last vertical line */
+ GfxDrawLine(x + offset, halfway_y, x + level_width - (rtl ? -WidgetDimensions::scaled.hsep_normal : WidgetDimensions::scaled.hsep_normal), halfway_y, colour);
+ x += level_width;
+
+ this->DrawSetting(settings_ptr, rtl ? left : x, rtl ? x : right, y, this == selected);
+ }
+ cur_row++;
+
+ return cur_row;
+}
+
+/* == SettingEntry methods == */
+
+/**
+ * Constructor for a single setting in the 'advanced settings' window
+ * @param name Name of the setting in the setting table
+ */
+SettingEntry::SettingEntry(const char *name)
+{
+ this->name = name;
+ this->setting = nullptr;
+}
+
+/**
+ * Initialization of a setting entry
+ * @param level Page nesting level of this entry
+ */
+void SettingEntry::Init(uint8_t level)
+{
+ BaseSettingEntry::Init(level);
+ this->setting = GetSettingFromName(this->name)->AsIntSetting();
+}
+
+/* Sets the given setting entry to its default value */
+void SettingEntry::ResetAll()
+{
+ SetSettingValue(this->setting, this->setting->GetDefaultValue());
+}
+
+/**
+ * Set the button-depressed flags (#SettingsEntryFlag::LeftDepressed and #SettingsEntryFlag::RightDepressed) to a specified value
+ * @param new_val New value for the button flags
+ * @see SettingEntryFlags
+ */
+void SettingEntry::SetButtons(SettingEntryFlags new_val)
+{
+ assert((new_val & SEF_BUTTONS_MASK) == new_val); // Should not touch any flags outside the buttons
+ this->flags.Set(SettingEntryFlag::LeftDepressed, new_val.Test(SettingEntryFlag::LeftDepressed));
+ this->flags.Set(SettingEntryFlag::RightDepressed, new_val.Test(SettingEntryFlag::RightDepressed));
+}
+
+/** Return number of rows needed to display the (filtered) entry */
+uint SettingEntry::Length() const
+{
+ return this->IsFiltered() ? 0 : 1;
+}
+
+/**
+ * Get the biggest height of the help text(s), if the width is at least \a maxw. Help text gets wrapped if needed.
+ * @param maxw Maximal width of a line help text.
+ * @return Biggest height needed to display any help text of this node (and its descendants).
+ */
+uint SettingEntry::GetMaxHelpHeight(int maxw)
+{
+ return GetStringHeight(this->setting->GetHelp(), maxw);
+}
+
+/**
+ * Checks whether an entry shall be made visible based on the restriction mode.
+ * @param mode The current status of the restriction drop down box.
+ * @return true if the entry shall be visible.
+ */
+bool SettingEntry::IsVisibleByRestrictionMode(RestrictionMode mode) const
+{
+ /* There shall not be any restriction, i.e. all settings shall be visible. */
+ if (mode == RM_ALL) return true;
+
+ const IntSettingDesc *sd = this->setting;
+
+ if (mode == RM_BASIC) return (this->setting->cat & SC_BASIC_LIST) != 0;
+ if (mode == RM_ADVANCED) return (this->setting->cat & SC_ADVANCED_LIST) != 0;
+
+ /* Read the current value. */
+ const void *object = ResolveObject(&GetGameSettings(), sd);
+ int64_t current_value = sd->Read(object);
+ int64_t filter_value;
+
+ if (mode == RM_CHANGED_AGAINST_DEFAULT) {
+ /* This entry shall only be visible, if the value deviates from its default value. */
+
+ /* Read the default value. */
+ filter_value = sd->GetDefaultValue();
+ } else {
+ assert(mode == RM_CHANGED_AGAINST_NEW);
+ /* This entry shall only be visible, if the value deviates from
+ * its value is used when starting a new game. */
+
+ /* Make sure we're not comparing the new game settings against itself. */
+ assert(&GetGameSettings() != &_settings_newgame);
+
+ /* Read the new game's value. */
+ filter_value = sd->Read(ResolveObject(&_settings_newgame, sd));
+ }
+
+ return current_value != filter_value;
+}
+
+/**
+ * Update the filter state.
+ * @param filter Filter
+ * @param force_visible Whether to force all items visible, no matter what (due to filter text; not affected by restriction drop down box).
+ * @return true if item remains visible
+ */
+bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible)
+{
+ this->flags.Reset(SettingEntryFlag::Filtered);
+
+ bool visible = true;
+
+ const IntSettingDesc *sd = this->setting;
+ if (!force_visible && !filter.string.IsEmpty()) {
+ /* Process the search text filter for this item. */
+ filter.string.ResetState();
+
+ SetDParam(0, STR_EMPTY);
+ filter.string.AddLine(sd->GetTitle());
+ filter.string.AddLine(sd->GetHelp());
+
+ visible = filter.string.GetState();
+ }
+
+ if (visible) {
+ if (filter.type != ST_ALL && sd->GetType() != filter.type) {
+ filter.type_hides = true;
+ visible = false;
+ }
+ if (!this->IsVisibleByRestrictionMode(filter.mode)) {
+ while (filter.min_cat < RM_ALL && (filter.min_cat == filter.mode || !this->IsVisibleByRestrictionMode(filter.min_cat))) filter.min_cat++;
+ visible = false;
+ }
+ }
+
+ if (!visible) this->flags.Set(SettingEntryFlag::Filtered);
+ return visible;
+}
+
+const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd)
+{
+ if (sd->flags.Test(SettingFlag::PerCompany)) {
+ if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
+ return &Company::Get(_local_company)->settings;
+ }
+ return &_settings_client.company;
+ }
+ return settings_ptr;
+}
+
+/**
+ * Function to draw setting value (button + text + current value)
+ * @param settings_ptr Pointer to current values of all settings
+ * @param left Left-most position in window/panel to start drawing
+ * @param right Right-most position in window/panel to draw
+ * @param y Upper-most position in window/panel to start drawing
+ * @param highlight Highlight entry.
+ */
+void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const
+{
+ const IntSettingDesc *sd = this->setting;
+ int state = (this->flags & SEF_BUTTONS_MASK).base();
+
+ bool rtl = _current_text_dir == TD_RTL;
+ uint buttons_left = rtl ? right + 1 - SETTING_BUTTON_WIDTH : left;
+ uint text_left = left + (rtl ? 0 : SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide);
+ uint text_right = right - (rtl ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide : 0);
+ uint button_y = y + (SETTING_HEIGHT - SETTING_BUTTON_HEIGHT) / 2;
+
+ /* We do not allow changes of some items when we are a client in a networkgame */
+ bool editable = sd->IsEditable();
+
+ auto [min_val, max_val] = sd->GetRange();
+ int32_t value = sd->Read(ResolveObject(settings_ptr, sd));
+ if (sd->IsBoolSetting()) {
+ /* Draw checkbox for boolean-value either on/off */
+ DrawBoolButton(buttons_left, button_y, value != 0, editable);
+ } else if (sd->flags.Test(SettingFlag::GuiDropdown)) {
+ /* Draw [v] button for settings of an enum-type */
+ DrawDropDownButton(buttons_left, button_y, COLOUR_YELLOW, state != 0, editable);
+ } else {
+ /* Draw [<][>] boxes for settings of an integer-type */
+ DrawArrowButtons(buttons_left, button_y, COLOUR_YELLOW, state,
+ editable && value != (sd->flags.Test(SettingFlag::GuiZeroIsSpecial) ? 0 : min_val), editable && static_cast(value) != max_val);
+ }
+ auto [param1, param2] = sd->GetValueParams(value);
+ DrawString(text_left, text_right, y + (SETTING_HEIGHT - GetCharacterHeight(FS_NORMAL)) / 2, GetString(sd->GetTitle(), STR_CONFIG_SETTING_VALUE, param1, param2), highlight ? TC_WHITE : TC_LIGHT_BLUE);
+}
+
+/* == SettingsContainer methods == */
+
+/**
+ * Initialization of an entire setting page
+ * @param level Nesting level of this page (internal variable, do not provide a value for it when calling)
+ */
+void SettingsContainer::Init(uint8_t level)
+{
+ for (auto &it : this->entries) {
+ it->Init(level);
+ }
+}
+
+/** Resets all settings to their default values */
+void SettingsContainer::ResetAll()
+{
+ for (auto settings_entry : this->entries) {
+ settings_entry->ResetAll();
+ }
+}
+
+/** Recursively close all folds of sub-pages */
+void SettingsContainer::FoldAll()
+{
+ for (auto &it : this->entries) {
+ it->FoldAll();
+ }
+}
+
+/** Recursively open all folds of sub-pages */
+void SettingsContainer::UnFoldAll()
+{
+ for (auto &it : this->entries) {
+ it->UnFoldAll();
+ }
+}
+
+/**
+ * Recursively accumulate the folding state of the 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 SettingsContainer::GetFoldingState(bool &all_folded, bool &all_unfolded) const
+{
+ for (auto &it : this->entries) {
+ it->GetFoldingState(all_folded, all_unfolded);
+ }
+}
+
+/**
+ * Update the filter state.
+ * @param filter Filter
+ * @param force_visible Whether to force all items visible, no matter what
+ * @return true if item remains visible
+ */
+bool SettingsContainer::UpdateFilterState(SettingFilter &filter, bool force_visible)
+{
+ bool visible = false;
+ bool first_visible = true;
+ for (EntryVector::reverse_iterator it = this->entries.rbegin(); it != this->entries.rend(); ++it) {
+ visible |= (*it)->UpdateFilterState(filter, force_visible);
+ (*it)->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 to make the setting really visible.
+ * @param item Entry to search for.
+ * @return true if entry is visible.
+ */
+bool SettingsContainer::IsVisible(const BaseSettingEntry *item) const
+{
+ for (const auto &it : this->entries) {
+ if (it->IsVisible(item)) return true;
+ }
+ return false;
+}
+
+/** Return number of rows needed to display the whole page */
+uint SettingsContainer::Length() const
+{
+ uint length = 0;
+ for (const auto &it : this->entries) {
+ length += it->Length();
+ }
+ return length;
+}
+
+/**
+ * Find the setting entry at row number \a row_num
+ * @param row_num Index of entry to return
+ * @param cur_row Variable used for keeping track of the current row number. Should point to memory initialized to \c 0 when first called.
+ * @return The requested setting entry or \c nullptr if it does not exist
+ */
+BaseSettingEntry *SettingsContainer::FindEntry(uint row_num, uint *cur_row)
+{
+ BaseSettingEntry *pe = nullptr;
+ for (const auto &it : this->entries) {
+ pe = it->FindEntry(row_num, cur_row);
+ if (pe != nullptr) {
+ break;
+ }
+ }
+ return pe;
+}
+
+/**
+ * Get the biggest height of the help texts, if the width is at least \a maxw. Help text gets wrapped if needed.
+ * @param maxw Maximal width of a line help text.
+ * @return Biggest height needed to display any help text of this (sub-)tree.
+ */
+uint SettingsContainer::GetMaxHelpHeight(int maxw)
+{
+ uint biggest = 0;
+ for (const auto &it : this->entries) {
+ biggest = std::max(biggest, it->GetMaxHelpHeight(maxw));
+ }
+ return biggest;
+}
+
+
+/**
+ * Draw a row in the settings panel.
+ *
+ * @param settings_ptr Pointer to current values of all settings
+ * @param left Left-most position in window/panel to start drawing \a first_row
+ * @param right Right-most x position to draw strings at.
+ * @param y Upper-most position in window/panel to start drawing \a first_row
+ * @param first_row First row number to draw
+ * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
+ * @param selected Selected entry by the user.
+ * @param cur_row Current row number (internal variable)
+ * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
+ * @return Row number of the next row to draw
+ */
+uint SettingsContainer::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
+{
+ for (const auto &it : this->entries) {
+ cur_row = it->Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
+ if (cur_row >= max_row) break;
+ }
+ return cur_row;
+}
+
+/* == SettingsPage methods == */
+
+/**
+ * Constructor for a sub-page in the 'advanced settings' window
+ * @param title Title of the sub-page
+ */
+SettingsPage::SettingsPage(StringID title)
+{
+ this->title = title;
+ this->folded = true;
+}
+
+/**
+ * Initialization of an entire setting page
+ * @param level Nesting level of this page (internal variable, do not provide a value for it when calling)
+ */
+void SettingsPage::Init(uint8_t level)
+{
+ BaseSettingEntry::Init(level);
+ SettingsContainer::Init(level + 1);
+}
+
+/** Resets all settings to their default values */
+void SettingsPage::ResetAll()
+{
+ for (auto settings_entry : this->entries) {
+ settings_entry->ResetAll();
+ }
+}
+
+/** Recursively close all (filtered) folds of sub-pages */
+void SettingsPage::FoldAll()
+{
+ if (this->IsFiltered()) return;
+ this->folded = true;
+
+ SettingsContainer::FoldAll();
+}
+
+/** Recursively open all (filtered) folds of sub-pages */
+void SettingsPage::UnFoldAll()
+{
+ if (this->IsFiltered()) return;
+ this->folded = false;
+
+ SettingsContainer::UnFoldAll();
+}
+
+/**
+ * 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 SettingsPage::GetFoldingState(bool &all_folded, bool &all_unfolded) const
+{
+ if (this->IsFiltered()) return;
+
+ if (this->folded) {
+ all_unfolded = false;
+ } else {
+ all_folded = false;
+ }
+
+ SettingsContainer::GetFoldingState(all_folded, all_unfolded);
+}
+
+/**
+ * Update the filter state.
+ * @param filter Filter
+ * @param force_visible Whether to force all items visible, no matter what (due to filter text; not affected by restriction drop down box).
+ * @return true if item remains visible
+ */
+bool SettingsPage::UpdateFilterState(SettingFilter &filter, bool force_visible)
+{
+ if (!force_visible && !filter.string.IsEmpty()) {
+ filter.string.ResetState();
+ filter.string.AddLine(this->title);
+ force_visible = filter.string.GetState();
+ }
+
+ bool visible = SettingsContainer::UpdateFilterState(filter, force_visible);
+ this->flags.Set(SettingEntryFlag::Filtered, !visible);
+ 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 to make the setting really visible.
+ * @param item Entry to search for.
+ * @return true if entry is visible.
+ */
+bool SettingsPage::IsVisible(const BaseSettingEntry *item) const
+{
+ if (this->IsFiltered()) return false;
+ if (this == item) return true;
+ if (this->folded) return false;
+
+ return SettingsContainer::IsVisible(item);
+}
+
+/** Return number of rows needed to display the (filtered) entry */
+uint SettingsPage::Length() const
+{
+ if (this->IsFiltered()) return 0;
+ if (this->folded) return 1; // Only displaying the title
+
+ return 1 + SettingsContainer::Length();
+}
+
+/**
+ * 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 nullptr if it not found (folded or filtered)
+ */
+BaseSettingEntry *SettingsPage::FindEntry(uint row_num, uint *cur_row)
+{
+ if (this->IsFiltered()) return nullptr;
+ if (row_num == *cur_row) return this;
+ (*cur_row)++;
+ if (this->folded) return nullptr;
+
+ return SettingsContainer::FindEntry(row_num, cur_row);
+}
+
+/**
+ * Draw a row in the settings panel.
+ *
+ * @param settings_ptr Pointer to current values of all settings
+ * @param left Left-most position in window/panel to start drawing \a first_row
+ * @param right Right-most x position to draw strings at.
+ * @param y Upper-most position in window/panel to start drawing \a first_row
+ * @param first_row First row number to draw
+ * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
+ * @param selected Selected entry by the user.
+ * @param cur_row Current row number (internal variable)
+ * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
+ * @return Row number of the next row to draw
+ */
+uint SettingsPage::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
+{
+ if (this->IsFiltered()) return cur_row;
+ if (cur_row >= max_row) return cur_row;
+
+ cur_row = BaseSettingEntry::Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
+
+ if (!this->folded) {
+ if (this->flags.Test(SettingEntryFlag::LastField)) {
+ assert(this->level < 8 * sizeof(parent_last));
+ SetBit(parent_last, this->level); // Add own last-field state
+ }
+
+ cur_row = SettingsContainer::Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
+ }
+
+ return cur_row;
+}
+
+/**
+ * Function to draw setting value (button + text + current value)
+ * @param left Left-most position in window/panel to start drawing
+ * @param right Right-most position in window/panel to draw
+ * @param y Upper-most position in window/panel to start drawing
+ */
+void SettingsPage::DrawSetting(GameSettings *, int left, int right, int y, bool) const
+{
+ bool rtl = _current_text_dir == TD_RTL;
+ DrawSprite((this->folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED), PAL_NONE, rtl ? right - _circle_size.width : left, y + (SETTING_HEIGHT - _circle_size.height) / 2);
+ DrawString(rtl ? left : left + _circle_size.width + WidgetDimensions::scaled.hsep_normal, rtl ? right - _circle_size.width - WidgetDimensions::scaled.hsep_normal : right, y + (SETTING_HEIGHT - GetCharacterHeight(FS_NORMAL)) / 2, this->title, TC_ORANGE);
+}
+
+/** Construct settings tree */
+SettingsContainer &GetSettingsTree()
+{
+ static SettingsContainer *main = nullptr;
+
+ if (main == nullptr)
+ {
+ /* Build up the dynamic settings-array only once per OpenTTD session */
+ main = new SettingsContainer();
+
+ SettingsPage *localisation = main->Add(new SettingsPage(STR_CONFIG_SETTING_LOCALISATION));
+ {
+ localisation->Add(new SettingEntry("locale.units_velocity"));
+ localisation->Add(new SettingEntry("locale.units_velocity_nautical"));
+ localisation->Add(new SettingEntry("locale.units_power"));
+ localisation->Add(new SettingEntry("locale.units_weight"));
+ localisation->Add(new SettingEntry("locale.units_volume"));
+ localisation->Add(new SettingEntry("locale.units_force"));
+ localisation->Add(new SettingEntry("locale.units_height"));
+ localisation->Add(new SettingEntry("gui.date_format_in_default_names"));
+ }
+
+ SettingsPage *graphics = main->Add(new SettingsPage(STR_CONFIG_SETTING_GRAPHICS));
+ {
+ graphics->Add(new SettingEntry("gui.zoom_min"));
+ graphics->Add(new SettingEntry("gui.zoom_max"));
+ graphics->Add(new SettingEntry("gui.sprite_zoom_min"));
+ graphics->Add(new SettingEntry("gui.smallmap_land_colour"));
+ graphics->Add(new SettingEntry("gui.linkgraph_colours"));
+ graphics->Add(new SettingEntry("gui.graph_line_thickness"));
+ }
+
+ SettingsPage *sound = main->Add(new SettingsPage(STR_CONFIG_SETTING_SOUND));
+ {
+ sound->Add(new SettingEntry("sound.click_beep"));
+ sound->Add(new SettingEntry("sound.confirm"));
+ sound->Add(new SettingEntry("sound.news_ticker"));
+ sound->Add(new SettingEntry("sound.news_full"));
+ sound->Add(new SettingEntry("sound.new_year"));
+ sound->Add(new SettingEntry("sound.disaster"));
+ sound->Add(new SettingEntry("sound.vehicle"));
+ sound->Add(new SettingEntry("sound.ambient"));
+ }
+
+ SettingsPage *interface = main->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE));
+ {
+ SettingsPage *general = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_GENERAL));
+ {
+ general->Add(new SettingEntry("gui.osk_activation"));
+ general->Add(new SettingEntry("gui.hover_delay_ms"));
+ general->Add(new SettingEntry("gui.errmsg_duration"));
+ general->Add(new SettingEntry("gui.window_snap_radius"));
+ general->Add(new SettingEntry("gui.window_soft_limit"));
+ general->Add(new SettingEntry("gui.right_click_wnd_close"));
+ }
+
+ SettingsPage *viewports = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_VIEWPORTS));
+ {
+ viewports->Add(new SettingEntry("gui.auto_scrolling"));
+ viewports->Add(new SettingEntry("gui.scroll_mode"));
+ viewports->Add(new SettingEntry("gui.smooth_scroll"));
+ /* While the horizontal scrollwheel scrolling is written as general code, only
+ * the cocoa (OSX) driver generates input for it.
+ * Since it's also able to completely disable the scrollwheel will we display it on all platforms anyway */
+ viewports->Add(new SettingEntry("gui.scrollwheel_scrolling"));
+ viewports->Add(new SettingEntry("gui.scrollwheel_multiplier"));
+#ifdef __APPLE__
+ /* We might need to emulate a right mouse button on mac */
+ viewports->Add(new SettingEntry("gui.right_mouse_btn_emulation"));
+#endif
+ viewports->Add(new SettingEntry("gui.population_in_label"));
+ viewports->Add(new SettingEntry("gui.liveries"));
+ viewports->Add(new SettingEntry("construction.train_signal_side"));
+ viewports->Add(new SettingEntry("gui.measure_tooltip"));
+ viewports->Add(new SettingEntry("gui.loading_indicators"));
+ viewports->Add(new SettingEntry("gui.show_track_reservation"));
+ }
+
+ SettingsPage *construction = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION));
+ {
+ construction->Add(new SettingEntry("gui.link_terraform_toolbar"));
+ construction->Add(new SettingEntry("gui.persistent_buildingtools"));
+ construction->Add(new SettingEntry("gui.default_rail_type"));
+ construction->Add(new SettingEntry("gui.semaphore_build_before"));
+ construction->Add(new SettingEntry("gui.signal_gui_mode"));
+ construction->Add(new SettingEntry("gui.cycle_signal_types"));
+ construction->Add(new SettingEntry("gui.drag_signals_fixed_distance"));
+ construction->Add(new SettingEntry("gui.auto_remove_signals"));
+ }
+
+ interface->Add(new SettingEntry("gui.toolbar_pos"));
+ interface->Add(new SettingEntry("gui.statusbar_pos"));
+ interface->Add(new SettingEntry("gui.prefer_teamchat"));
+ interface->Add(new SettingEntry("gui.advanced_vehicle_list"));
+ interface->Add(new SettingEntry("gui.timetable_mode"));
+ interface->Add(new SettingEntry("gui.timetable_arrival_departure"));
+ interface->Add(new SettingEntry("gui.show_newgrf_name"));
+ interface->Add(new SettingEntry("gui.show_cargo_in_vehicle_lists"));
+ }
+
+ SettingsPage *advisors = main->Add(new SettingsPage(STR_CONFIG_SETTING_ADVISORS));
+ {
+ advisors->Add(new SettingEntry("gui.coloured_news_year"));
+ advisors->Add(new SettingEntry("news_display.general"));
+ advisors->Add(new SettingEntry("news_display.new_vehicles"));
+ advisors->Add(new SettingEntry("news_display.accident"));
+ advisors->Add(new SettingEntry("news_display.accident_other"));
+ advisors->Add(new SettingEntry("news_display.company_info"));
+ advisors->Add(new SettingEntry("news_display.acceptance"));
+ advisors->Add(new SettingEntry("news_display.arrival_player"));
+ advisors->Add(new SettingEntry("news_display.arrival_other"));
+ advisors->Add(new SettingEntry("news_display.advice"));
+ advisors->Add(new SettingEntry("gui.order_review_system"));
+ advisors->Add(new SettingEntry("gui.vehicle_income_warn"));
+ advisors->Add(new SettingEntry("gui.lost_vehicle_warn"));
+ advisors->Add(new SettingEntry("gui.old_vehicle_warn"));
+ advisors->Add(new SettingEntry("gui.show_finances"));
+ advisors->Add(new SettingEntry("news_display.economy"));
+ advisors->Add(new SettingEntry("news_display.subsidies"));
+ advisors->Add(new SettingEntry("news_display.open"));
+ advisors->Add(new SettingEntry("news_display.close"));
+ advisors->Add(new SettingEntry("news_display.production_player"));
+ advisors->Add(new SettingEntry("news_display.production_other"));
+ advisors->Add(new SettingEntry("news_display.production_nobody"));
+ }
+
+ SettingsPage *company = main->Add(new SettingsPage(STR_CONFIG_SETTING_COMPANY));
+ {
+ company->Add(new SettingEntry("gui.starting_colour"));
+ company->Add(new SettingEntry("gui.starting_colour_secondary"));
+ company->Add(new SettingEntry("company.engine_renew"));
+ company->Add(new SettingEntry("company.engine_renew_months"));
+ company->Add(new SettingEntry("company.engine_renew_money"));
+ company->Add(new SettingEntry("vehicle.servint_ispercent"));
+ company->Add(new SettingEntry("vehicle.servint_trains"));
+ company->Add(new SettingEntry("vehicle.servint_roadveh"));
+ company->Add(new SettingEntry("vehicle.servint_ships"));
+ company->Add(new SettingEntry("vehicle.servint_aircraft"));
+ }
+
+ SettingsPage *accounting = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCOUNTING));
+ {
+ accounting->Add(new SettingEntry("difficulty.infinite_money"));
+ accounting->Add(new SettingEntry("economy.inflation"));
+ accounting->Add(new SettingEntry("difficulty.initial_interest"));
+ accounting->Add(new SettingEntry("difficulty.max_loan"));
+ accounting->Add(new SettingEntry("difficulty.subsidy_multiplier"));
+ accounting->Add(new SettingEntry("difficulty.subsidy_duration"));
+ accounting->Add(new SettingEntry("economy.feeder_payment_share"));
+ accounting->Add(new SettingEntry("economy.infrastructure_maintenance"));
+ accounting->Add(new SettingEntry("difficulty.vehicle_costs"));
+ accounting->Add(new SettingEntry("difficulty.construction_cost"));
+ }
+
+ SettingsPage *vehicles = main->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES));
+ {
+ SettingsPage *physics = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_PHYSICS));
+ {
+ physics->Add(new SettingEntry("vehicle.train_acceleration_model"));
+ physics->Add(new SettingEntry("vehicle.train_slope_steepness"));
+ physics->Add(new SettingEntry("vehicle.wagon_speed_limits"));
+ physics->Add(new SettingEntry("vehicle.freight_trains"));
+ physics->Add(new SettingEntry("vehicle.roadveh_acceleration_model"));
+ physics->Add(new SettingEntry("vehicle.roadveh_slope_steepness"));
+ physics->Add(new SettingEntry("vehicle.smoke_amount"));
+ physics->Add(new SettingEntry("vehicle.plane_speed"));
+ }
+
+ SettingsPage *routing = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_ROUTING));
+ {
+ routing->Add(new SettingEntry("vehicle.road_side"));
+ routing->Add(new SettingEntry("difficulty.line_reverse_mode"));
+ routing->Add(new SettingEntry("pf.reverse_at_signals"));
+ routing->Add(new SettingEntry("pf.forbid_90_deg"));
+ }
+
+ SettingsPage *orders = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_ORDERS));
+ {
+ orders->Add(new SettingEntry("gui.new_nonstop"));
+ orders->Add(new SettingEntry("gui.quick_goto"));
+ orders->Add(new SettingEntry("gui.stop_location"));
+ }
+ }
+
+ SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS));
+ {
+ limitations->Add(new SettingEntry("construction.command_pause_level"));
+ limitations->Add(new SettingEntry("construction.autoslope"));
+ limitations->Add(new SettingEntry("construction.extra_dynamite"));
+ limitations->Add(new SettingEntry("construction.map_height_limit"));
+ limitations->Add(new SettingEntry("construction.max_bridge_length"));
+ limitations->Add(new SettingEntry("construction.max_bridge_height"));
+ limitations->Add(new SettingEntry("construction.max_tunnel_length"));
+ limitations->Add(new SettingEntry("station.never_expire_airports"));
+ limitations->Add(new SettingEntry("vehicle.never_expire_vehicles"));
+ limitations->Add(new SettingEntry("vehicle.max_trains"));
+ limitations->Add(new SettingEntry("vehicle.max_roadveh"));
+ limitations->Add(new SettingEntry("vehicle.max_aircraft"));
+ limitations->Add(new SettingEntry("vehicle.max_ships"));
+ limitations->Add(new SettingEntry("vehicle.max_train_length"));
+ limitations->Add(new SettingEntry("station.station_spread"));
+ limitations->Add(new SettingEntry("station.distant_join_stations"));
+ limitations->Add(new SettingEntry("station.modified_catchment"));
+ limitations->Add(new SettingEntry("construction.road_stop_on_town_road"));
+ limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road"));
+ limitations->Add(new SettingEntry("construction.crossing_with_competitor"));
+ limitations->Add(new SettingEntry("vehicle.disable_elrails"));
+ limitations->Add(new SettingEntry("order.station_length_loading_penalty"));
+ }
+
+ SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS));
+ {
+ disasters->Add(new SettingEntry("difficulty.disasters"));
+ disasters->Add(new SettingEntry("difficulty.economy"));
+ disasters->Add(new SettingEntry("vehicle.plane_crashes"));
+ disasters->Add(new SettingEntry("difficulty.vehicle_breakdowns"));
+ disasters->Add(new SettingEntry("order.no_servicing_if_no_breakdowns"));
+ disasters->Add(new SettingEntry("order.serviceathelipad"));
+ }
+
+ SettingsPage *genworld = main->Add(new SettingsPage(STR_CONFIG_SETTING_GENWORLD));
+ {
+ genworld->Add(new SettingEntry("game_creation.landscape"));
+ genworld->Add(new SettingEntry("game_creation.land_generator"));
+ genworld->Add(new SettingEntry("difficulty.terrain_type"));
+ genworld->Add(new SettingEntry("game_creation.tgen_smoothness"));
+ genworld->Add(new SettingEntry("game_creation.variety"));
+ genworld->Add(new SettingEntry("game_creation.snow_coverage"));
+ genworld->Add(new SettingEntry("game_creation.snow_line_height"));
+ genworld->Add(new SettingEntry("game_creation.desert_coverage"));
+ genworld->Add(new SettingEntry("game_creation.amount_of_rivers"));
+ }
+
+ SettingsPage *environment = main->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT));
+ {
+ SettingsPage *time = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TIME));
+ {
+ time->Add(new SettingEntry("economy.timekeeping_units"));
+ time->Add(new SettingEntry("economy.minutes_per_calendar_year"));
+ time->Add(new SettingEntry("game_creation.ending_year"));
+ time->Add(new SettingEntry("gui.pause_on_newgame"));
+ time->Add(new SettingEntry("gui.fast_forward_speed_limit"));
+ }
+
+ SettingsPage *authorities = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES));
+ {
+ authorities->Add(new SettingEntry("difficulty.town_council_tolerance"));
+ authorities->Add(new SettingEntry("economy.bribe"));
+ authorities->Add(new SettingEntry("economy.exclusive_rights"));
+ authorities->Add(new SettingEntry("economy.fund_roads"));
+ authorities->Add(new SettingEntry("economy.fund_buildings"));
+ authorities->Add(new SettingEntry("economy.station_noise_level"));
+ }
+
+ SettingsPage *towns = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TOWNS));
+ {
+ towns->Add(new SettingEntry("economy.town_cargo_scale"));
+ towns->Add(new SettingEntry("economy.town_growth_rate"));
+ towns->Add(new SettingEntry("economy.allow_town_roads"));
+ towns->Add(new SettingEntry("economy.allow_town_level_crossings"));
+ towns->Add(new SettingEntry("economy.found_town"));
+ towns->Add(new SettingEntry("economy.place_houses"));
+ towns->Add(new SettingEntry("economy.town_layout"));
+ towns->Add(new SettingEntry("economy.larger_towns"));
+ towns->Add(new SettingEntry("economy.initial_city_size"));
+ towns->Add(new SettingEntry("economy.town_cargogen_mode"));
+ }
+
+ SettingsPage *industries = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES));
+ {
+ industries->Add(new SettingEntry("economy.industry_cargo_scale"));
+ industries->Add(new SettingEntry("difficulty.industry_density"));
+ industries->Add(new SettingEntry("construction.raw_industry_construction"));
+ industries->Add(new SettingEntry("construction.industry_platform"));
+ industries->Add(new SettingEntry("economy.multiple_industry_per_town"));
+ industries->Add(new SettingEntry("game_creation.oil_refinery_limit"));
+ industries->Add(new SettingEntry("economy.type"));
+ industries->Add(new SettingEntry("station.serve_neutral_industries"));
+ }
+
+ SettingsPage *cdist = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST));
+ {
+ cdist->Add(new SettingEntry("linkgraph.recalc_time"));
+ cdist->Add(new SettingEntry("linkgraph.recalc_interval"));
+ cdist->Add(new SettingEntry("linkgraph.distribution_pax"));
+ cdist->Add(new SettingEntry("linkgraph.distribution_mail"));
+ cdist->Add(new SettingEntry("linkgraph.distribution_armoured"));
+ cdist->Add(new SettingEntry("linkgraph.distribution_default"));
+ cdist->Add(new SettingEntry("linkgraph.accuracy"));
+ cdist->Add(new SettingEntry("linkgraph.demand_distance"));
+ cdist->Add(new SettingEntry("linkgraph.demand_size"));
+ cdist->Add(new SettingEntry("linkgraph.short_path_saturation"));
+ }
+
+ SettingsPage *trees = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TREES));
+ {
+ trees->Add(new SettingEntry("game_creation.tree_placer"));
+ trees->Add(new SettingEntry("construction.extra_tree_placement"));
+ }
+ }
+
+ SettingsPage *ai = main->Add(new SettingsPage(STR_CONFIG_SETTING_AI));
+ {
+ SettingsPage *npc = ai->Add(new SettingsPage(STR_CONFIG_SETTING_AI_NPC));
+ {
+ npc->Add(new SettingEntry("script.script_max_opcode_till_suspend"));
+ npc->Add(new SettingEntry("script.script_max_memory_megabytes"));
+ npc->Add(new SettingEntry("difficulty.competitor_speed"));
+ npc->Add(new SettingEntry("ai.ai_in_multiplayer"));
+ npc->Add(new SettingEntry("ai.ai_disable_veh_train"));
+ npc->Add(new SettingEntry("ai.ai_disable_veh_roadveh"));
+ npc->Add(new SettingEntry("ai.ai_disable_veh_aircraft"));
+ npc->Add(new SettingEntry("ai.ai_disable_veh_ship"));
+ }
+
+ ai->Add(new SettingEntry("economy.give_money"));
+ }
+
+ SettingsPage *network = main->Add(new SettingsPage(STR_CONFIG_SETTING_NETWORK));
+ {
+ network->Add(new SettingEntry("network.use_relay_service"));
+ }
+
+ main->Init();
+ }
+ return *main;
+}
diff --git a/src/settingentry_gui.h b/src/settingentry_gui.h
new file mode 100644
index 0000000000..600f2e98e9
--- /dev/null
+++ b/src/settingentry_gui.h
@@ -0,0 +1,173 @@
+/*
+ * This file is part of OpenTTD.
+ * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
+ * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see .
+ */
+
+/** @file settingentry_gui.h Declarations of classes for handling display of individual configuration settings. */
+
+#ifndef SETTINGENTRY_GUI_H
+#define SETTINGENTRY_GUI_H
+
+#include "core/enum_type.hpp"
+#include "settings_internal.h"
+#include "stringfilter_type.h"
+
+extern Dimension _circle_size;
+extern int SETTING_HEIGHT;
+
+/**
+ * Flags for #SettingEntry
+ * @note The #SEF_BUTTONS_MASK matches expectations of the formal parameter 'state' of #DrawArrowButtons
+ */
+enum class SettingEntryFlag : uint8_t {
+ LeftDepressed, ///< Of a numeric setting entry, the left button is depressed
+ RightDepressed, ///< Of a numeric setting entry, the right button is depressed
+ LastField, ///< This entry is the last one in a (sub-)page
+ Filtered, ///< Entry is hidden by the string filter
+};
+using SettingEntryFlags = EnumBitSet;
+
+static constexpr SettingEntryFlags SEF_BUTTONS_MASK = {SettingEntryFlag::LeftDepressed, SettingEntryFlag::RightDepressed}; ///< Mask for button flags
+
+/** How the list of advanced settings is filtered. */
+enum RestrictionMode : uint8_t {
+ RM_BASIC, ///< Display settings associated to the "basic" list.
+ RM_ADVANCED, ///< Display settings associated to the "advanced" list.
+ RM_ALL, ///< List all settings regardless of the default/newgame/... values.
+ RM_CHANGED_AGAINST_DEFAULT, ///< Show only settings which are different compared to default values.
+ RM_CHANGED_AGAINST_NEW, ///< Show only settings which are different compared to the user's new game setting values.
+ RM_END, ///< End for iteration.
+};
+DECLARE_INCREMENT_DECREMENT_OPERATORS(RestrictionMode)
+
+/** Filter for settings list. */
+struct SettingFilter {
+ StringFilter string; ///< Filter string.
+ RestrictionMode min_cat; ///< Minimum category needed to display all filtered strings (#RM_BASIC, #RM_ADVANCED, or #RM_ALL).
+ bool type_hides; ///< Whether the type hides filtered strings.
+ RestrictionMode mode; ///< Filter based on category.
+ SettingType type; ///< Filter based on type.
+};
+
+/** Data structure describing a single setting in a tab */
+struct BaseSettingEntry {
+ SettingEntryFlags flags; ///< Flags of the setting entry. @see SettingEntryFlags
+ uint8_t level; ///< Nesting level of this setting entry
+
+ BaseSettingEntry() : flags(), level(0) {}
+ virtual ~BaseSettingEntry() = default;
+
+ virtual void Init(uint8_t level = 0);
+ virtual void FoldAll() {}
+ virtual void UnFoldAll() {}
+ virtual void ResetAll() = 0;
+
+ /**
+ * Set whether this is the last visible entry of the parent node.
+ * @param last_field Value to set
+ */
+ void SetLastField(bool last_field) { this->flags.Set(SettingEntryFlag::LastField, last_field); }
+
+ virtual uint Length() const = 0;
+ virtual void GetFoldingState([[maybe_unused]] bool &all_folded, [[maybe_unused]] bool &all_unfolded) const {}
+ virtual bool IsVisible(const BaseSettingEntry *item) const;
+ virtual BaseSettingEntry *FindEntry(uint row, uint *cur_row);
+ virtual uint GetMaxHelpHeight([[maybe_unused]] int maxw) { return 0; }
+
+ /**
+ * Check whether an entry is hidden due to filters
+ * @return true if hidden.
+ */
+ bool IsFiltered() const { return this->flags.Test(SettingEntryFlag::Filtered); }
+
+ virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible) = 0;
+
+ virtual uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const;
+
+protected:
+ virtual void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const = 0;
+};
+
+/** Standard setting */
+struct SettingEntry : BaseSettingEntry {
+ const char *name; ///< Name of the setting
+ const IntSettingDesc *setting; ///< Setting description of the setting
+
+ SettingEntry(const char *name);
+
+ void Init(uint8_t level = 0) override;
+ void ResetAll() override;
+ uint Length() const override;
+ uint GetMaxHelpHeight(int maxw) override;
+ bool UpdateFilterState(SettingFilter &filter, bool force_visible) override;
+
+ void SetButtons(SettingEntryFlags new_val);
+
+protected:
+ void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const override;
+
+private:
+ bool IsVisibleByRestrictionMode(RestrictionMode mode) const;
+};
+
+/** Containers for BaseSettingEntry */
+struct SettingsContainer {
+ typedef std::vector EntryVector;
+ EntryVector entries; ///< Settings on this page
+
+ template
+ T *Add(T *item)
+ {
+ this->entries.push_back(item);
+ return item;
+ }
+
+ void Init(uint8_t level = 0);
+ void ResetAll();
+ void FoldAll();
+ void UnFoldAll();
+
+ uint Length() const;
+ void GetFoldingState(bool &all_folded, bool &all_unfolded) const;
+ bool IsVisible(const BaseSettingEntry *item) const;
+ BaseSettingEntry *FindEntry(uint row, uint *cur_row);
+ uint GetMaxHelpHeight(int maxw);
+
+ bool UpdateFilterState(SettingFilter &filter, bool force_visible);
+
+ uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const;
+};
+
+/** Data structure describing one page of settings in the settings window. */
+struct SettingsPage : BaseSettingEntry, SettingsContainer {
+ StringID title; ///< Title of the sub-page
+ bool folded; ///< Sub-page is folded (not visible except for its title)
+
+ SettingsPage(StringID title);
+
+ void Init(uint8_t level = 0) override;
+ void ResetAll() override;
+ void FoldAll() override;
+ void UnFoldAll() override;
+
+ uint Length() const override;
+ void GetFoldingState(bool &all_folded, bool &all_unfolded) const override;
+ bool IsVisible(const BaseSettingEntry *item) const override;
+ BaseSettingEntry *FindEntry(uint row, uint *cur_row) override;
+ uint GetMaxHelpHeight(int maxw) override { return SettingsContainer::GetMaxHelpHeight(maxw); }
+
+ bool UpdateFilterState(SettingFilter &filter, bool force_visible) override;
+
+ uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const override;
+
+protected:
+ void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const override;
+};
+
+SettingsContainer &GetSettingsTree();
+const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd);
+
+#endif /* SETTINGENTRY_GUI_H */
+
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 44c2e0c424..ffb7c81407 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -50,6 +50,7 @@
#include "video/video_driver.hpp"
#include "social_integration.h"
#include "sound_func.h"
+#include "settingentry_gui.h"
#include "safeguards.h"
@@ -76,9 +77,7 @@ static const uint32_t _autosave_dropdown_to_minutes[] = {
120,
};
-static Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window.
-
-static const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd);
+Dimension _circle_size; ///< Dimension of the circle +/- icon. This is here as not all users are within the class of the settings window.
/**
* Get index of the current screen resolution.
@@ -1201,1088 +1200,7 @@ void ShowGameOptions()
new GameOptionsWindow(_game_options_desc);
}
-static int SETTING_HEIGHT = 11; ///< Height of a single setting in the tree view in pixels
-
-/**
- * Flags for #SettingEntry
- * @note The #SEF_BUTTONS_MASK matches expectations of the formal parameter 'state' of #DrawArrowButtons
- */
-enum class SettingEntryFlag : uint8_t {
- LeftDepressed, ///< Of a numeric setting entry, the left button is depressed
- RightDepressed, ///< Of a numeric setting entry, the right button is depressed
- LastField, ///< This entry is the last one in a (sub-)page
- Filtered, ///< Entry is hidden by the string filter
-};
-using SettingEntryFlags = EnumBitSet;
-
-static constexpr SettingEntryFlags SEF_BUTTONS_MASK = {SettingEntryFlag::LeftDepressed, SettingEntryFlag::RightDepressed}; ///< Mask for button flags
-
-/** How the list of advanced settings is filtered. */
-enum RestrictionMode : uint8_t {
- RM_BASIC, ///< Display settings associated to the "basic" list.
- RM_ADVANCED, ///< Display settings associated to the "advanced" list.
- RM_ALL, ///< List all settings regardless of the default/newgame/... values.
- RM_CHANGED_AGAINST_DEFAULT, ///< Show only settings which are different compared to default values.
- RM_CHANGED_AGAINST_NEW, ///< Show only settings which are different compared to the user's new game setting values.
- RM_END, ///< End for iteration.
-};
-DECLARE_INCREMENT_DECREMENT_OPERATORS(RestrictionMode)
-
-/** Filter for settings list. */
-struct SettingFilter {
- StringFilter string; ///< Filter string.
- RestrictionMode min_cat; ///< Minimum category needed to display all filtered strings (#RM_BASIC, #RM_ADVANCED, or #RM_ALL).
- bool type_hides; ///< Whether the type hides filtered strings.
- RestrictionMode mode; ///< Filter based on category.
- SettingType type; ///< Filter based on type.
-};
-
-/** Data structure describing a single setting in a tab */
-struct BaseSettingEntry {
- SettingEntryFlags flags; ///< Flags of the setting entry. @see SettingEntryFlags
- uint8_t level; ///< Nesting level of this setting entry
-
- BaseSettingEntry() : flags(), level(0) {}
- virtual ~BaseSettingEntry() = default;
-
- virtual void Init(uint8_t level = 0);
- virtual void FoldAll() {}
- virtual void UnFoldAll() {}
- virtual void ResetAll() = 0;
-
- /**
- * Set whether this is the last visible entry of the parent node.
- * @param last_field Value to set
- */
- void SetLastField(bool last_field) { this->flags.Set(SettingEntryFlag::LastField, last_field); }
-
- virtual uint Length() const = 0;
- virtual void GetFoldingState([[maybe_unused]] bool &all_folded, [[maybe_unused]] bool &all_unfolded) const {}
- virtual bool IsVisible(const BaseSettingEntry *item) const;
- virtual BaseSettingEntry *FindEntry(uint row, uint *cur_row);
- virtual uint GetMaxHelpHeight([[maybe_unused]] int maxw) { return 0; }
-
- /**
- * Check whether an entry is hidden due to filters
- * @return true if hidden.
- */
- bool IsFiltered() const { return this->flags.Test(SettingEntryFlag::Filtered); }
-
- virtual bool UpdateFilterState(SettingFilter &filter, bool force_visible) = 0;
-
- virtual uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const;
-
-protected:
- virtual void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const = 0;
-};
-
-/** Standard setting */
-struct SettingEntry : BaseSettingEntry {
- const char *name; ///< Name of the setting
- const IntSettingDesc *setting; ///< Setting description of the setting
-
- SettingEntry(const char *name);
-
- void Init(uint8_t level = 0) override;
- void ResetAll() override;
- uint Length() const override;
- uint GetMaxHelpHeight(int maxw) override;
- bool UpdateFilterState(SettingFilter &filter, bool force_visible) override;
-
- void SetButtons(SettingEntryFlags new_val);
-
-protected:
- void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const override;
-
-private:
- bool IsVisibleByRestrictionMode(RestrictionMode mode) const;
-};
-
-/** Containers for BaseSettingEntry */
-struct SettingsContainer {
- typedef std::vector EntryVector;
- EntryVector entries; ///< Settings on this page
-
- template
- T *Add(T *item)
- {
- this->entries.push_back(item);
- return item;
- }
-
- void Init(uint8_t level = 0);
- void ResetAll();
- void FoldAll();
- void UnFoldAll();
-
- uint Length() const;
- void GetFoldingState(bool &all_folded, bool &all_unfolded) const;
- bool IsVisible(const BaseSettingEntry *item) const;
- BaseSettingEntry *FindEntry(uint row, uint *cur_row);
- uint GetMaxHelpHeight(int maxw);
-
- bool UpdateFilterState(SettingFilter &filter, bool force_visible);
-
- uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const;
-};
-
-/** Data structure describing one page of settings in the settings window. */
-struct SettingsPage : BaseSettingEntry, SettingsContainer {
- StringID title; ///< Title of the sub-page
- bool folded; ///< Sub-page is folded (not visible except for its title)
-
- SettingsPage(StringID title);
-
- void Init(uint8_t level = 0) override;
- void ResetAll() override;
- void FoldAll() override;
- void UnFoldAll() override;
-
- uint Length() const override;
- void GetFoldingState(bool &all_folded, bool &all_unfolded) const override;
- bool IsVisible(const BaseSettingEntry *item) const override;
- BaseSettingEntry *FindEntry(uint row, uint *cur_row) override;
- uint GetMaxHelpHeight(int maxw) override { return SettingsContainer::GetMaxHelpHeight(maxw); }
-
- bool UpdateFilterState(SettingFilter &filter, bool force_visible) override;
-
- uint Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row = 0, uint parent_last = 0) const override;
-
-protected:
- void DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const override;
-};
-
-/* == BaseSettingEntry methods == */
-
-/**
- * Initialization of a setting entry
- * @param level Page nesting level of this entry
- */
-void BaseSettingEntry::Init(uint8_t level)
-{
- this->level = level;
-}
-
-/**
- * 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 to make the setting really visible.
- * @param item Entry to search for.
- * @return true if entry is visible.
- */
-bool BaseSettingEntry::IsVisible(const BaseSettingEntry *item) const
-{
- if (this->IsFiltered()) return false;
- return this == item;
-}
-
-/**
- * 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 nullptr if it not found (folded or filtered)
- */
-BaseSettingEntry *BaseSettingEntry::FindEntry(uint row_num, uint *cur_row)
-{
- if (this->IsFiltered()) return nullptr;
- if (row_num == *cur_row) return this;
- (*cur_row)++;
- return nullptr;
-}
-
-/**
- * Draw a row in the settings panel.
- *
- * The scrollbar uses rows of the page, while the page data structure is a tree of #SettingsPage and #SettingEntry objects.
- * As a result, the drawing routing traverses the tree from top to bottom, counting rows in \a cur_row until it reaches \a first_row.
- * Then it enables drawing rows while traversing until \a max_row is reached, at which point drawing is terminated.
- *
- * The \a parent_last parameter ensures that the vertical lines at the left are
- * only drawn when another entry follows, that it prevents output like
- * \verbatim
- * |-- setting
- * |-- (-) - Title
- * | |-- setting
- * | |-- setting
- * \endverbatim
- * The left-most vertical line is not wanted. It is prevented by setting the
- * appropriate bit in the \a parent_last parameter.
- *
- * @param settings_ptr Pointer to current values of all settings
- * @param left Left-most position in window/panel to start drawing \a first_row
- * @param right Right-most x position to draw strings at.
- * @param y Upper-most position in window/panel to start drawing \a first_row
- * @param first_row First row number to draw
- * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
- * @param selected Selected entry by the user.
- * @param cur_row Current row number (internal variable)
- * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
- * @return Row number of the next row to draw
- */
-uint BaseSettingEntry::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
-{
- if (this->IsFiltered()) return cur_row;
- if (cur_row >= max_row) return cur_row;
-
- bool rtl = _current_text_dir == TD_RTL;
- int offset = (rtl ? -(int)_circle_size.width : (int)_circle_size.width) / 2;
- int level_width = rtl ? -WidgetDimensions::scaled.hsep_indent : WidgetDimensions::scaled.hsep_indent;
-
- int x = rtl ? right : left;
- if (cur_row >= first_row) {
- int colour = GetColourGradient(COLOUR_ORANGE, SHADE_NORMAL);
- y += (cur_row - first_row) * SETTING_HEIGHT; // Compute correct y start position
-
- /* Draw vertical for parent nesting levels */
- for (uint lvl = 0; lvl < this->level; lvl++) {
- if (!HasBit(parent_last, lvl)) GfxDrawLine(x + offset, y, x + offset, y + SETTING_HEIGHT - 1, colour);
- x += level_width;
- }
- /* draw own |- prefix */
- int halfway_y = y + SETTING_HEIGHT / 2;
- int bottom_y = flags.Test(SettingEntryFlag::LastField) ? halfway_y : y + SETTING_HEIGHT - 1;
- GfxDrawLine(x + offset, y, x + offset, bottom_y, colour);
- /* Small horizontal line from the last vertical line */
- GfxDrawLine(x + offset, halfway_y, x + level_width - (rtl ? -WidgetDimensions::scaled.hsep_normal : WidgetDimensions::scaled.hsep_normal), halfway_y, colour);
- x += level_width;
-
- this->DrawSetting(settings_ptr, rtl ? left : x, rtl ? x : right, y, this == selected);
- }
- cur_row++;
-
- return cur_row;
-}
-
-/* == SettingEntry methods == */
-
-/**
- * Constructor for a single setting in the 'advanced settings' window
- * @param name Name of the setting in the setting table
- */
-SettingEntry::SettingEntry(const char *name)
-{
- this->name = name;
- this->setting = nullptr;
-}
-
-/**
- * Initialization of a setting entry
- * @param level Page nesting level of this entry
- */
-void SettingEntry::Init(uint8_t level)
-{
- BaseSettingEntry::Init(level);
- this->setting = GetSettingFromName(this->name)->AsIntSetting();
-}
-
-/* Sets the given setting entry to its default value */
-void SettingEntry::ResetAll()
-{
- SetSettingValue(this->setting, this->setting->GetDefaultValue());
-}
-
-/**
- * Set the button-depressed flags (#SettingsEntryFlag::LeftDepressed and #SettingsEntryFlag::RightDepressed) to a specified value
- * @param new_val New value for the button flags
- * @see SettingEntryFlags
- */
-void SettingEntry::SetButtons(SettingEntryFlags new_val)
-{
- assert((new_val & SEF_BUTTONS_MASK) == new_val); // Should not touch any flags outside the buttons
- this->flags.Set(SettingEntryFlag::LeftDepressed, new_val.Test(SettingEntryFlag::LeftDepressed));
- this->flags.Set(SettingEntryFlag::RightDepressed, new_val.Test(SettingEntryFlag::RightDepressed));
-}
-
-/** Return number of rows needed to display the (filtered) entry */
-uint SettingEntry::Length() const
-{
- return this->IsFiltered() ? 0 : 1;
-}
-
-/**
- * Get the biggest height of the help text(s), if the width is at least \a maxw. Help text gets wrapped if needed.
- * @param maxw Maximal width of a line help text.
- * @return Biggest height needed to display any help text of this node (and its descendants).
- */
-uint SettingEntry::GetMaxHelpHeight(int maxw)
-{
- return GetStringHeight(this->setting->GetHelp(), maxw);
-}
-
-/**
- * Checks whether an entry shall be made visible based on the restriction mode.
- * @param mode The current status of the restriction drop down box.
- * @return true if the entry shall be visible.
- */
-bool SettingEntry::IsVisibleByRestrictionMode(RestrictionMode mode) const
-{
- /* There shall not be any restriction, i.e. all settings shall be visible. */
- if (mode == RM_ALL) return true;
-
- const IntSettingDesc *sd = this->setting;
-
- if (mode == RM_BASIC) return (this->setting->cat & SC_BASIC_LIST) != 0;
- if (mode == RM_ADVANCED) return (this->setting->cat & SC_ADVANCED_LIST) != 0;
-
- /* Read the current value. */
- const void *object = ResolveObject(&GetGameSettings(), sd);
- int64_t current_value = sd->Read(object);
- int64_t filter_value;
-
- if (mode == RM_CHANGED_AGAINST_DEFAULT) {
- /* This entry shall only be visible, if the value deviates from its default value. */
-
- /* Read the default value. */
- filter_value = sd->GetDefaultValue();
- } else {
- assert(mode == RM_CHANGED_AGAINST_NEW);
- /* This entry shall only be visible, if the value deviates from
- * its value is used when starting a new game. */
-
- /* Make sure we're not comparing the new game settings against itself. */
- assert(&GetGameSettings() != &_settings_newgame);
-
- /* Read the new game's value. */
- filter_value = sd->Read(ResolveObject(&_settings_newgame, sd));
- }
-
- return current_value != filter_value;
-}
-
-/**
- * Update the filter state.
- * @param filter Filter
- * @param force_visible Whether to force all items visible, no matter what (due to filter text; not affected by restriction drop down box).
- * @return true if item remains visible
- */
-bool SettingEntry::UpdateFilterState(SettingFilter &filter, bool force_visible)
-{
- this->flags.Reset(SettingEntryFlag::Filtered);
-
- bool visible = true;
-
- const IntSettingDesc *sd = this->setting;
- if (!force_visible && !filter.string.IsEmpty()) {
- /* Process the search text filter for this item. */
- filter.string.ResetState();
-
- SetDParam(0, STR_EMPTY);
- filter.string.AddLine(sd->GetTitle());
- filter.string.AddLine(sd->GetHelp());
-
- visible = filter.string.GetState();
- }
-
- if (visible) {
- if (filter.type != ST_ALL && sd->GetType() != filter.type) {
- filter.type_hides = true;
- visible = false;
- }
- if (!this->IsVisibleByRestrictionMode(filter.mode)) {
- while (filter.min_cat < RM_ALL && (filter.min_cat == filter.mode || !this->IsVisibleByRestrictionMode(filter.min_cat))) filter.min_cat++;
- visible = false;
- }
- }
-
- if (!visible) this->flags.Set(SettingEntryFlag::Filtered);
- return visible;
-}
-
-static const void *ResolveObject(const GameSettings *settings_ptr, const IntSettingDesc *sd)
-{
- if (sd->flags.Test(SettingFlag::PerCompany)) {
- if (Company::IsValidID(_local_company) && _game_mode != GM_MENU) {
- return &Company::Get(_local_company)->settings;
- }
- return &_settings_client.company;
- }
- return settings_ptr;
-}
-
-/**
- * Function to draw setting value (button + text + current value)
- * @param settings_ptr Pointer to current values of all settings
- * @param left Left-most position in window/panel to start drawing
- * @param right Right-most position in window/panel to draw
- * @param y Upper-most position in window/panel to start drawing
- * @param highlight Highlight entry.
- */
-void SettingEntry::DrawSetting(GameSettings *settings_ptr, int left, int right, int y, bool highlight) const
-{
- const IntSettingDesc *sd = this->setting;
- int state = (this->flags & SEF_BUTTONS_MASK).base();
-
- bool rtl = _current_text_dir == TD_RTL;
- uint buttons_left = rtl ? right + 1 - SETTING_BUTTON_WIDTH : left;
- uint text_left = left + (rtl ? 0 : SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide);
- uint text_right = right - (rtl ? SETTING_BUTTON_WIDTH + WidgetDimensions::scaled.hsep_wide : 0);
- uint button_y = y + (SETTING_HEIGHT - SETTING_BUTTON_HEIGHT) / 2;
-
- /* We do not allow changes of some items when we are a client in a networkgame */
- bool editable = sd->IsEditable();
-
- auto [min_val, max_val] = sd->GetRange();
- int32_t value = sd->Read(ResolveObject(settings_ptr, sd));
- if (sd->IsBoolSetting()) {
- /* Draw checkbox for boolean-value either on/off */
- DrawBoolButton(buttons_left, button_y, value != 0, editable);
- } else if (sd->flags.Test(SettingFlag::GuiDropdown)) {
- /* Draw [v] button for settings of an enum-type */
- DrawDropDownButton(buttons_left, button_y, COLOUR_YELLOW, state != 0, editable);
- } else {
- /* Draw [<][>] boxes for settings of an integer-type */
- DrawArrowButtons(buttons_left, button_y, COLOUR_YELLOW, state,
- editable && value != (sd->flags.Test(SettingFlag::GuiZeroIsSpecial) ? 0 : min_val), editable && static_cast(value) != max_val);
- }
- auto [param1, param2] = sd->GetValueParams(value);
- DrawString(text_left, text_right, y + (SETTING_HEIGHT - GetCharacterHeight(FS_NORMAL)) / 2, GetString(sd->GetTitle(), STR_CONFIG_SETTING_VALUE, param1, param2), highlight ? TC_WHITE : TC_LIGHT_BLUE);
-}
-
-/* == SettingsContainer methods == */
-
-/**
- * Initialization of an entire setting page
- * @param level Nesting level of this page (internal variable, do not provide a value for it when calling)
- */
-void SettingsContainer::Init(uint8_t level)
-{
- for (auto &it : this->entries) {
- it->Init(level);
- }
-}
-
-/** Resets all settings to their default values */
-void SettingsContainer::ResetAll()
-{
- for (auto settings_entry : this->entries) {
- settings_entry->ResetAll();
- }
-}
-
-/** Recursively close all folds of sub-pages */
-void SettingsContainer::FoldAll()
-{
- for (auto &it : this->entries) {
- it->FoldAll();
- }
-}
-
-/** Recursively open all folds of sub-pages */
-void SettingsContainer::UnFoldAll()
-{
- for (auto &it : this->entries) {
- it->UnFoldAll();
- }
-}
-
-/**
- * Recursively accumulate the folding state of the 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 SettingsContainer::GetFoldingState(bool &all_folded, bool &all_unfolded) const
-{
- for (auto &it : this->entries) {
- it->GetFoldingState(all_folded, all_unfolded);
- }
-}
-
-/**
- * Update the filter state.
- * @param filter Filter
- * @param force_visible Whether to force all items visible, no matter what
- * @return true if item remains visible
- */
-bool SettingsContainer::UpdateFilterState(SettingFilter &filter, bool force_visible)
-{
- bool visible = false;
- bool first_visible = true;
- for (EntryVector::reverse_iterator it = this->entries.rbegin(); it != this->entries.rend(); ++it) {
- visible |= (*it)->UpdateFilterState(filter, force_visible);
- (*it)->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 to make the setting really visible.
- * @param item Entry to search for.
- * @return true if entry is visible.
- */
-bool SettingsContainer::IsVisible(const BaseSettingEntry *item) const
-{
- for (const auto &it : this->entries) {
- if (it->IsVisible(item)) return true;
- }
- return false;
-}
-
-/** Return number of rows needed to display the whole page */
-uint SettingsContainer::Length() const
-{
- uint length = 0;
- for (const auto &it : this->entries) {
- length += it->Length();
- }
- return length;
-}
-
-/**
- * Find the setting entry at row number \a row_num
- * @param row_num Index of entry to return
- * @param cur_row Variable used for keeping track of the current row number. Should point to memory initialized to \c 0 when first called.
- * @return The requested setting entry or \c nullptr if it does not exist
- */
-BaseSettingEntry *SettingsContainer::FindEntry(uint row_num, uint *cur_row)
-{
- BaseSettingEntry *pe = nullptr;
- for (const auto &it : this->entries) {
- pe = it->FindEntry(row_num, cur_row);
- if (pe != nullptr) {
- break;
- }
- }
- return pe;
-}
-
-/**
- * Get the biggest height of the help texts, if the width is at least \a maxw. Help text gets wrapped if needed.
- * @param maxw Maximal width of a line help text.
- * @return Biggest height needed to display any help text of this (sub-)tree.
- */
-uint SettingsContainer::GetMaxHelpHeight(int maxw)
-{
- uint biggest = 0;
- for (const auto &it : this->entries) {
- biggest = std::max(biggest, it->GetMaxHelpHeight(maxw));
- }
- return biggest;
-}
-
-
-/**
- * Draw a row in the settings panel.
- *
- * @param settings_ptr Pointer to current values of all settings
- * @param left Left-most position in window/panel to start drawing \a first_row
- * @param right Right-most x position to draw strings at.
- * @param y Upper-most position in window/panel to start drawing \a first_row
- * @param first_row First row number to draw
- * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
- * @param selected Selected entry by the user.
- * @param cur_row Current row number (internal variable)
- * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
- * @return Row number of the next row to draw
- */
-uint SettingsContainer::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
-{
- for (const auto &it : this->entries) {
- cur_row = it->Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
- if (cur_row >= max_row) break;
- }
- return cur_row;
-}
-
-/* == SettingsPage methods == */
-
-/**
- * Constructor for a sub-page in the 'advanced settings' window
- * @param title Title of the sub-page
- */
-SettingsPage::SettingsPage(StringID title)
-{
- this->title = title;
- this->folded = true;
-}
-
-/**
- * Initialization of an entire setting page
- * @param level Nesting level of this page (internal variable, do not provide a value for it when calling)
- */
-void SettingsPage::Init(uint8_t level)
-{
- BaseSettingEntry::Init(level);
- SettingsContainer::Init(level + 1);
-}
-
-/** Resets all settings to their default values */
-void SettingsPage::ResetAll()
-{
- for (auto settings_entry : this->entries) {
- settings_entry->ResetAll();
- }
-}
-
-/** Recursively close all (filtered) folds of sub-pages */
-void SettingsPage::FoldAll()
-{
- if (this->IsFiltered()) return;
- this->folded = true;
-
- SettingsContainer::FoldAll();
-}
-
-/** Recursively open all (filtered) folds of sub-pages */
-void SettingsPage::UnFoldAll()
-{
- if (this->IsFiltered()) return;
- this->folded = false;
-
- SettingsContainer::UnFoldAll();
-}
-
-/**
- * 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 SettingsPage::GetFoldingState(bool &all_folded, bool &all_unfolded) const
-{
- if (this->IsFiltered()) return;
-
- if (this->folded) {
- all_unfolded = false;
- } else {
- all_folded = false;
- }
-
- SettingsContainer::GetFoldingState(all_folded, all_unfolded);
-}
-
-/**
- * Update the filter state.
- * @param filter Filter
- * @param force_visible Whether to force all items visible, no matter what (due to filter text; not affected by restriction drop down box).
- * @return true if item remains visible
- */
-bool SettingsPage::UpdateFilterState(SettingFilter &filter, bool force_visible)
-{
- if (!force_visible && !filter.string.IsEmpty()) {
- filter.string.ResetState();
- filter.string.AddLine(this->title);
- force_visible = filter.string.GetState();
- }
-
- bool visible = SettingsContainer::UpdateFilterState(filter, force_visible);
- this->flags.Set(SettingEntryFlag::Filtered, !visible);
- 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 to make the setting really visible.
- * @param item Entry to search for.
- * @return true if entry is visible.
- */
-bool SettingsPage::IsVisible(const BaseSettingEntry *item) const
-{
- if (this->IsFiltered()) return false;
- if (this == item) return true;
- if (this->folded) return false;
-
- return SettingsContainer::IsVisible(item);
-}
-
-/** Return number of rows needed to display the (filtered) entry */
-uint SettingsPage::Length() const
-{
- if (this->IsFiltered()) return 0;
- if (this->folded) return 1; // Only displaying the title
-
- return 1 + SettingsContainer::Length();
-}
-
-/**
- * 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 nullptr if it not found (folded or filtered)
- */
-BaseSettingEntry *SettingsPage::FindEntry(uint row_num, uint *cur_row)
-{
- if (this->IsFiltered()) return nullptr;
- if (row_num == *cur_row) return this;
- (*cur_row)++;
- if (this->folded) return nullptr;
-
- return SettingsContainer::FindEntry(row_num, cur_row);
-}
-
-/**
- * Draw a row in the settings panel.
- *
- * @param settings_ptr Pointer to current values of all settings
- * @param left Left-most position in window/panel to start drawing \a first_row
- * @param right Right-most x position to draw strings at.
- * @param y Upper-most position in window/panel to start drawing \a first_row
- * @param first_row First row number to draw
- * @param max_row Row-number to stop drawing (the row-number of the row below the last row to draw)
- * @param selected Selected entry by the user.
- * @param cur_row Current row number (internal variable)
- * @param parent_last Last-field booleans of parent page level (page level \e i sets bit \e i to 1 if it is its last field)
- * @return Row number of the next row to draw
- */
-uint SettingsPage::Draw(GameSettings *settings_ptr, int left, int right, int y, uint first_row, uint max_row, BaseSettingEntry *selected, uint cur_row, uint parent_last) const
-{
- if (this->IsFiltered()) return cur_row;
- if (cur_row >= max_row) return cur_row;
-
- cur_row = BaseSettingEntry::Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
-
- if (!this->folded) {
- if (this->flags.Test(SettingEntryFlag::LastField)) {
- assert(this->level < 8 * sizeof(parent_last));
- SetBit(parent_last, this->level); // Add own last-field state
- }
-
- cur_row = SettingsContainer::Draw(settings_ptr, left, right, y, first_row, max_row, selected, cur_row, parent_last);
- }
-
- return cur_row;
-}
-
-/**
- * Function to draw setting value (button + text + current value)
- * @param left Left-most position in window/panel to start drawing
- * @param right Right-most position in window/panel to draw
- * @param y Upper-most position in window/panel to start drawing
- */
-void SettingsPage::DrawSetting(GameSettings *, int left, int right, int y, bool) const
-{
- bool rtl = _current_text_dir == TD_RTL;
- DrawSprite((this->folded ? SPR_CIRCLE_FOLDED : SPR_CIRCLE_UNFOLDED), PAL_NONE, rtl ? right - _circle_size.width : left, y + (SETTING_HEIGHT - _circle_size.height) / 2);
- DrawString(rtl ? left : left + _circle_size.width + WidgetDimensions::scaled.hsep_normal, rtl ? right - _circle_size.width - WidgetDimensions::scaled.hsep_normal : right, y + (SETTING_HEIGHT - GetCharacterHeight(FS_NORMAL)) / 2, this->title, TC_ORANGE);
-}
-
-/** Construct settings tree */
-static SettingsContainer &GetSettingsTree()
-{
- static SettingsContainer *main = nullptr;
-
- if (main == nullptr)
- {
- /* Build up the dynamic settings-array only once per OpenTTD session */
- main = new SettingsContainer();
-
- SettingsPage *localisation = main->Add(new SettingsPage(STR_CONFIG_SETTING_LOCALISATION));
- {
- localisation->Add(new SettingEntry("locale.units_velocity"));
- localisation->Add(new SettingEntry("locale.units_velocity_nautical"));
- localisation->Add(new SettingEntry("locale.units_power"));
- localisation->Add(new SettingEntry("locale.units_weight"));
- localisation->Add(new SettingEntry("locale.units_volume"));
- localisation->Add(new SettingEntry("locale.units_force"));
- localisation->Add(new SettingEntry("locale.units_height"));
- localisation->Add(new SettingEntry("gui.date_format_in_default_names"));
- }
-
- SettingsPage *graphics = main->Add(new SettingsPage(STR_CONFIG_SETTING_GRAPHICS));
- {
- graphics->Add(new SettingEntry("gui.zoom_min"));
- graphics->Add(new SettingEntry("gui.zoom_max"));
- graphics->Add(new SettingEntry("gui.sprite_zoom_min"));
- graphics->Add(new SettingEntry("gui.smallmap_land_colour"));
- graphics->Add(new SettingEntry("gui.linkgraph_colours"));
- graphics->Add(new SettingEntry("gui.graph_line_thickness"));
- }
-
- SettingsPage *sound = main->Add(new SettingsPage(STR_CONFIG_SETTING_SOUND));
- {
- sound->Add(new SettingEntry("sound.click_beep"));
- sound->Add(new SettingEntry("sound.confirm"));
- sound->Add(new SettingEntry("sound.news_ticker"));
- sound->Add(new SettingEntry("sound.news_full"));
- sound->Add(new SettingEntry("sound.new_year"));
- sound->Add(new SettingEntry("sound.disaster"));
- sound->Add(new SettingEntry("sound.vehicle"));
- sound->Add(new SettingEntry("sound.ambient"));
- }
-
- SettingsPage *interface = main->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE));
- {
- SettingsPage *general = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_GENERAL));
- {
- general->Add(new SettingEntry("gui.osk_activation"));
- general->Add(new SettingEntry("gui.hover_delay_ms"));
- general->Add(new SettingEntry("gui.errmsg_duration"));
- general->Add(new SettingEntry("gui.window_snap_radius"));
- general->Add(new SettingEntry("gui.window_soft_limit"));
- general->Add(new SettingEntry("gui.right_click_wnd_close"));
- }
-
- SettingsPage *viewports = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_VIEWPORTS));
- {
- viewports->Add(new SettingEntry("gui.auto_scrolling"));
- viewports->Add(new SettingEntry("gui.scroll_mode"));
- viewports->Add(new SettingEntry("gui.smooth_scroll"));
- /* While the horizontal scrollwheel scrolling is written as general code, only
- * the cocoa (OSX) driver generates input for it.
- * Since it's also able to completely disable the scrollwheel will we display it on all platforms anyway */
- viewports->Add(new SettingEntry("gui.scrollwheel_scrolling"));
- viewports->Add(new SettingEntry("gui.scrollwheel_multiplier"));
-#ifdef __APPLE__
- /* We might need to emulate a right mouse button on mac */
- viewports->Add(new SettingEntry("gui.right_mouse_btn_emulation"));
-#endif
- viewports->Add(new SettingEntry("gui.population_in_label"));
- viewports->Add(new SettingEntry("gui.liveries"));
- viewports->Add(new SettingEntry("construction.train_signal_side"));
- viewports->Add(new SettingEntry("gui.measure_tooltip"));
- viewports->Add(new SettingEntry("gui.loading_indicators"));
- viewports->Add(new SettingEntry("gui.show_track_reservation"));
- }
-
- SettingsPage *construction = interface->Add(new SettingsPage(STR_CONFIG_SETTING_INTERFACE_CONSTRUCTION));
- {
- construction->Add(new SettingEntry("gui.link_terraform_toolbar"));
- construction->Add(new SettingEntry("gui.persistent_buildingtools"));
- construction->Add(new SettingEntry("gui.default_rail_type"));
- construction->Add(new SettingEntry("gui.semaphore_build_before"));
- construction->Add(new SettingEntry("gui.signal_gui_mode"));
- construction->Add(new SettingEntry("gui.cycle_signal_types"));
- construction->Add(new SettingEntry("gui.drag_signals_fixed_distance"));
- construction->Add(new SettingEntry("gui.auto_remove_signals"));
- }
-
- interface->Add(new SettingEntry("gui.toolbar_pos"));
- interface->Add(new SettingEntry("gui.statusbar_pos"));
- interface->Add(new SettingEntry("gui.prefer_teamchat"));
- interface->Add(new SettingEntry("gui.advanced_vehicle_list"));
- interface->Add(new SettingEntry("gui.timetable_mode"));
- interface->Add(new SettingEntry("gui.timetable_arrival_departure"));
- interface->Add(new SettingEntry("gui.show_newgrf_name"));
- interface->Add(new SettingEntry("gui.show_cargo_in_vehicle_lists"));
- }
-
- SettingsPage *advisors = main->Add(new SettingsPage(STR_CONFIG_SETTING_ADVISORS));
- {
- advisors->Add(new SettingEntry("gui.coloured_news_year"));
- advisors->Add(new SettingEntry("news_display.general"));
- advisors->Add(new SettingEntry("news_display.new_vehicles"));
- advisors->Add(new SettingEntry("news_display.accident"));
- advisors->Add(new SettingEntry("news_display.accident_other"));
- advisors->Add(new SettingEntry("news_display.company_info"));
- advisors->Add(new SettingEntry("news_display.acceptance"));
- advisors->Add(new SettingEntry("news_display.arrival_player"));
- advisors->Add(new SettingEntry("news_display.arrival_other"));
- advisors->Add(new SettingEntry("news_display.advice"));
- advisors->Add(new SettingEntry("gui.order_review_system"));
- advisors->Add(new SettingEntry("gui.vehicle_income_warn"));
- advisors->Add(new SettingEntry("gui.lost_vehicle_warn"));
- advisors->Add(new SettingEntry("gui.old_vehicle_warn"));
- advisors->Add(new SettingEntry("gui.show_finances"));
- advisors->Add(new SettingEntry("news_display.economy"));
- advisors->Add(new SettingEntry("news_display.subsidies"));
- advisors->Add(new SettingEntry("news_display.open"));
- advisors->Add(new SettingEntry("news_display.close"));
- advisors->Add(new SettingEntry("news_display.production_player"));
- advisors->Add(new SettingEntry("news_display.production_other"));
- advisors->Add(new SettingEntry("news_display.production_nobody"));
- }
-
- SettingsPage *company = main->Add(new SettingsPage(STR_CONFIG_SETTING_COMPANY));
- {
- company->Add(new SettingEntry("gui.starting_colour"));
- company->Add(new SettingEntry("gui.starting_colour_secondary"));
- company->Add(new SettingEntry("company.engine_renew"));
- company->Add(new SettingEntry("company.engine_renew_months"));
- company->Add(new SettingEntry("company.engine_renew_money"));
- company->Add(new SettingEntry("vehicle.servint_ispercent"));
- company->Add(new SettingEntry("vehicle.servint_trains"));
- company->Add(new SettingEntry("vehicle.servint_roadveh"));
- company->Add(new SettingEntry("vehicle.servint_ships"));
- company->Add(new SettingEntry("vehicle.servint_aircraft"));
- }
-
- SettingsPage *accounting = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCOUNTING));
- {
- accounting->Add(new SettingEntry("difficulty.infinite_money"));
- accounting->Add(new SettingEntry("economy.inflation"));
- accounting->Add(new SettingEntry("difficulty.initial_interest"));
- accounting->Add(new SettingEntry("difficulty.max_loan"));
- accounting->Add(new SettingEntry("difficulty.subsidy_multiplier"));
- accounting->Add(new SettingEntry("difficulty.subsidy_duration"));
- accounting->Add(new SettingEntry("economy.feeder_payment_share"));
- accounting->Add(new SettingEntry("economy.infrastructure_maintenance"));
- accounting->Add(new SettingEntry("difficulty.vehicle_costs"));
- accounting->Add(new SettingEntry("difficulty.construction_cost"));
- }
-
- SettingsPage *vehicles = main->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES));
- {
- SettingsPage *physics = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_PHYSICS));
- {
- physics->Add(new SettingEntry("vehicle.train_acceleration_model"));
- physics->Add(new SettingEntry("vehicle.train_slope_steepness"));
- physics->Add(new SettingEntry("vehicle.wagon_speed_limits"));
- physics->Add(new SettingEntry("vehicle.freight_trains"));
- physics->Add(new SettingEntry("vehicle.roadveh_acceleration_model"));
- physics->Add(new SettingEntry("vehicle.roadveh_slope_steepness"));
- physics->Add(new SettingEntry("vehicle.smoke_amount"));
- physics->Add(new SettingEntry("vehicle.plane_speed"));
- }
-
- SettingsPage *routing = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_ROUTING));
- {
- routing->Add(new SettingEntry("vehicle.road_side"));
- routing->Add(new SettingEntry("difficulty.line_reverse_mode"));
- routing->Add(new SettingEntry("pf.reverse_at_signals"));
- routing->Add(new SettingEntry("pf.forbid_90_deg"));
- }
-
- SettingsPage *orders = vehicles->Add(new SettingsPage(STR_CONFIG_SETTING_VEHICLES_ORDERS));
- {
- orders->Add(new SettingEntry("gui.new_nonstop"));
- orders->Add(new SettingEntry("gui.quick_goto"));
- orders->Add(new SettingEntry("gui.stop_location"));
- }
- }
-
- SettingsPage *limitations = main->Add(new SettingsPage(STR_CONFIG_SETTING_LIMITATIONS));
- {
- limitations->Add(new SettingEntry("construction.command_pause_level"));
- limitations->Add(new SettingEntry("construction.autoslope"));
- limitations->Add(new SettingEntry("construction.extra_dynamite"));
- limitations->Add(new SettingEntry("construction.map_height_limit"));
- limitations->Add(new SettingEntry("construction.max_bridge_length"));
- limitations->Add(new SettingEntry("construction.max_bridge_height"));
- limitations->Add(new SettingEntry("construction.max_tunnel_length"));
- limitations->Add(new SettingEntry("station.never_expire_airports"));
- limitations->Add(new SettingEntry("vehicle.never_expire_vehicles"));
- limitations->Add(new SettingEntry("vehicle.max_trains"));
- limitations->Add(new SettingEntry("vehicle.max_roadveh"));
- limitations->Add(new SettingEntry("vehicle.max_aircraft"));
- limitations->Add(new SettingEntry("vehicle.max_ships"));
- limitations->Add(new SettingEntry("vehicle.max_train_length"));
- limitations->Add(new SettingEntry("station.station_spread"));
- limitations->Add(new SettingEntry("station.distant_join_stations"));
- limitations->Add(new SettingEntry("station.modified_catchment"));
- limitations->Add(new SettingEntry("construction.road_stop_on_town_road"));
- limitations->Add(new SettingEntry("construction.road_stop_on_competitor_road"));
- limitations->Add(new SettingEntry("construction.crossing_with_competitor"));
- limitations->Add(new SettingEntry("vehicle.disable_elrails"));
- limitations->Add(new SettingEntry("order.station_length_loading_penalty"));
- }
-
- SettingsPage *disasters = main->Add(new SettingsPage(STR_CONFIG_SETTING_ACCIDENTS));
- {
- disasters->Add(new SettingEntry("difficulty.disasters"));
- disasters->Add(new SettingEntry("difficulty.economy"));
- disasters->Add(new SettingEntry("vehicle.plane_crashes"));
- disasters->Add(new SettingEntry("difficulty.vehicle_breakdowns"));
- disasters->Add(new SettingEntry("order.no_servicing_if_no_breakdowns"));
- disasters->Add(new SettingEntry("order.serviceathelipad"));
- }
-
- SettingsPage *genworld = main->Add(new SettingsPage(STR_CONFIG_SETTING_GENWORLD));
- {
- genworld->Add(new SettingEntry("game_creation.landscape"));
- genworld->Add(new SettingEntry("game_creation.land_generator"));
- genworld->Add(new SettingEntry("difficulty.terrain_type"));
- genworld->Add(new SettingEntry("game_creation.tgen_smoothness"));
- genworld->Add(new SettingEntry("game_creation.variety"));
- genworld->Add(new SettingEntry("game_creation.snow_coverage"));
- genworld->Add(new SettingEntry("game_creation.snow_line_height"));
- genworld->Add(new SettingEntry("game_creation.desert_coverage"));
- genworld->Add(new SettingEntry("game_creation.amount_of_rivers"));
- }
-
- SettingsPage *environment = main->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT));
- {
- SettingsPage *time = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TIME));
- {
- time->Add(new SettingEntry("economy.timekeeping_units"));
- time->Add(new SettingEntry("economy.minutes_per_calendar_year"));
- time->Add(new SettingEntry("game_creation.ending_year"));
- time->Add(new SettingEntry("gui.pause_on_newgame"));
- time->Add(new SettingEntry("gui.fast_forward_speed_limit"));
- }
-
- SettingsPage *authorities = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_AUTHORITIES));
- {
- authorities->Add(new SettingEntry("difficulty.town_council_tolerance"));
- authorities->Add(new SettingEntry("economy.bribe"));
- authorities->Add(new SettingEntry("economy.exclusive_rights"));
- authorities->Add(new SettingEntry("economy.fund_roads"));
- authorities->Add(new SettingEntry("economy.fund_buildings"));
- authorities->Add(new SettingEntry("economy.station_noise_level"));
- }
-
- SettingsPage *towns = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TOWNS));
- {
- towns->Add(new SettingEntry("economy.town_cargo_scale"));
- towns->Add(new SettingEntry("economy.town_growth_rate"));
- towns->Add(new SettingEntry("economy.allow_town_roads"));
- towns->Add(new SettingEntry("economy.allow_town_level_crossings"));
- towns->Add(new SettingEntry("economy.found_town"));
- towns->Add(new SettingEntry("economy.place_houses"));
- towns->Add(new SettingEntry("economy.town_layout"));
- towns->Add(new SettingEntry("economy.larger_towns"));
- towns->Add(new SettingEntry("economy.initial_city_size"));
- towns->Add(new SettingEntry("economy.town_cargogen_mode"));
- }
-
- SettingsPage *industries = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_INDUSTRIES));
- {
- industries->Add(new SettingEntry("economy.industry_cargo_scale"));
- industries->Add(new SettingEntry("difficulty.industry_density"));
- industries->Add(new SettingEntry("construction.raw_industry_construction"));
- industries->Add(new SettingEntry("construction.industry_platform"));
- industries->Add(new SettingEntry("economy.multiple_industry_per_town"));
- industries->Add(new SettingEntry("game_creation.oil_refinery_limit"));
- industries->Add(new SettingEntry("economy.type"));
- industries->Add(new SettingEntry("station.serve_neutral_industries"));
- }
-
- SettingsPage *cdist = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_CARGODIST));
- {
- cdist->Add(new SettingEntry("linkgraph.recalc_time"));
- cdist->Add(new SettingEntry("linkgraph.recalc_interval"));
- cdist->Add(new SettingEntry("linkgraph.distribution_pax"));
- cdist->Add(new SettingEntry("linkgraph.distribution_mail"));
- cdist->Add(new SettingEntry("linkgraph.distribution_armoured"));
- cdist->Add(new SettingEntry("linkgraph.distribution_default"));
- cdist->Add(new SettingEntry("linkgraph.accuracy"));
- cdist->Add(new SettingEntry("linkgraph.demand_distance"));
- cdist->Add(new SettingEntry("linkgraph.demand_size"));
- cdist->Add(new SettingEntry("linkgraph.short_path_saturation"));
- }
-
- SettingsPage *trees = environment->Add(new SettingsPage(STR_CONFIG_SETTING_ENVIRONMENT_TREES));
- {
- trees->Add(new SettingEntry("game_creation.tree_placer"));
- trees->Add(new SettingEntry("construction.extra_tree_placement"));
- }
- }
-
- SettingsPage *ai = main->Add(new SettingsPage(STR_CONFIG_SETTING_AI));
- {
- SettingsPage *npc = ai->Add(new SettingsPage(STR_CONFIG_SETTING_AI_NPC));
- {
- npc->Add(new SettingEntry("script.script_max_opcode_till_suspend"));
- npc->Add(new SettingEntry("script.script_max_memory_megabytes"));
- npc->Add(new SettingEntry("difficulty.competitor_speed"));
- npc->Add(new SettingEntry("ai.ai_in_multiplayer"));
- npc->Add(new SettingEntry("ai.ai_disable_veh_train"));
- npc->Add(new SettingEntry("ai.ai_disable_veh_roadveh"));
- npc->Add(new SettingEntry("ai.ai_disable_veh_aircraft"));
- npc->Add(new SettingEntry("ai.ai_disable_veh_ship"));
- }
-
- ai->Add(new SettingEntry("economy.give_money"));
- }
-
- SettingsPage *network = main->Add(new SettingsPage(STR_CONFIG_SETTING_NETWORK));
- {
- network->Add(new SettingEntry("network.use_relay_service"));
- }
-
- main->Init();
- }
- return *main;
-}
+int SETTING_HEIGHT = 11; ///< Height of a single setting in the tree view in pixels
static const StringID _game_settings_restrict_dropdown[] = {
STR_CONFIG_SETTING_RESTRICT_BASIC, // RM_BASIC