From 4f45bfd08b013c00600a85abde0d0b19884ec430 Mon Sep 17 00:00:00 2001 From: v-rob Date: Fri, 6 Dec 2019 12:51:10 -0800 Subject: [PATCH] Add scrollbaroptions FormSpec element (#8530) --- doc/lua_api.txt | 28 ++++++++- src/gui/guiFormSpecMenu.cpp | 119 +++++++++++++++++++++++++++--------- src/gui/guiFormSpecMenu.h | 13 +++- src/gui/guiScrollBar.cpp | 27 +++++++- src/gui/guiScrollBar.h | 8 +++ 5 files changed, 159 insertions(+), 36 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 697efcdbc..12ab2b4b5 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2351,16 +2351,40 @@ Elements ### `scrollbar[,;,;;;]` -* Show a scrollbar +* Show a scrollbar using options defined by the previous `scrollbaroptions[]` * There are two ways to use it: 1. handle the changed event (only changed scrollbar is available) 2. read the value on pressing a button (all scrollbars are available) * `orientation`: `vertical`/`horizontal` * Fieldname data is transferred to Lua -* Value this trackbar is set to (`0`-`1000`) +* Value of this trackbar is set to (`0`-`1000`) by default * See also `minetest.explode_scrollbar_event` (main menu: `core.explode_scrollbar_event`). +### `scrollbaroptions[opt1;opt2;...]` +* Sets options for all following `scrollbar[]` elements +* `min=` + * Sets scrollbar minimum value, defaults to `0`. +* `max=` + * Sets scrollbar maximum value, defaults to `1000`. + If the max is equal to the min, the scrollbar will be disabled. +* `smallstep=` + * Sets scrollbar step value when the arrows are clicked or the mouse wheel is + scrolled. + * If this is set to a negative number, the value will be reset to `10`. +* `largestep=` + * Sets scrollbar step value used by page up and page down. + * If this is set to a negative number, the value will be reset to `100`. +* `thumbsize=` + * Sets size of the thumb on the scrollbar. Size is calculated in the number of + units the thumb spans out of the range of the scrollbar values. + * Example: If a scrollbar has a `min` of 1 and a `max` of 100, a thumbsize of 10 + would span a tenth of the scrollbar space. + * If this is set to zero or less, the value will be reset to `1`. +* `arrows=` + * Whether to show the arrow buttons on the scrollbar. `default` hides the arrows + when the scrollbar gets too small, but shows them otherwise. + ### `table[,;,;;,,...,;]` * Show scrollable table using options defined by the previous `tableoptions[]` diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index f2ec758d9..26ca525c8 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "guiFormSpecMenu.h" +#include "guiScrollBar.h" +#include "guiTable.h" #include "constants.h" #include "gamedef.h" #include "client/keycode.h" @@ -123,24 +125,18 @@ GUIFormSpecMenu::~GUIFormSpecMenu() { removeChildren(); - for (auto &table_it : m_tables) { + for (auto &table_it : m_tables) table_it.second->drop(); - } - for (auto &inventorylist_it : m_inventorylists) { + for (auto &inventorylist_it : m_inventorylists) inventorylist_it.e->drop(); - } - for (auto &checkbox_it : m_checkboxes) { + for (auto &checkbox_it : m_checkboxes) checkbox_it.second->drop(); - } - for (auto &scrollbar_it : m_scrollbars) { + for (auto &scrollbar_it : m_scrollbars) scrollbar_it.second->drop(); - } - for (auto &background_it : m_backgrounds) { + for (auto &background_it : m_backgrounds) background_it->drop(); - } - for (auto &tooltip_rect_it : m_tooltip_rects) { + for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); - } delete m_selected_item; delete m_form_src; @@ -614,22 +610,86 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen spec.ftype = f_ScrollBar; spec.send = true; GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect, - is_horizontal, false); + is_horizontal, true); auto style = getStyleForElement("scrollbar", name); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setArrowsVisible(data->scrollbar_options.arrow_visiblity); + + s32 max = data->scrollbar_options.max; + s32 min = data->scrollbar_options.min; + + e->setMax(max); + e->setMin(min); - e->setMax(1000); - e->setMin(0); e->setPos(stoi(parts[4])); - e->setSmallStep(10); - e->setLargeStep(100); + + e->setSmallStep(data->scrollbar_options.small_step); + e->setLargeStep(data->scrollbar_options.large_step); + + s32 scrollbar_size = is_horizontal ? dim.X : dim.Y; + + e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size); m_scrollbars.emplace_back(spec,e); m_fields.push_back(spec); return; } - errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid scrollbar element(" << parts.size() << "): '" << element + << "'" << std::endl; +} + +void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string &element) +{ + std::vector parts = split(element, ';'); + + if (parts.size() == 0) { + warningstream << "Invalid scrollbaroptions element(" << parts.size() << "): '" << + element << "'" << std::endl; + return; + } + + for (const std::string &i : parts) { + std::vector options = split(i, '='); + + if (options.size() != 2) { + warningstream << "Invalid scrollbaroptions option syntax: '" << + element << "'" << std::endl; + continue; // Go to next option + } + + if (options[0] == "max") { + data->scrollbar_options.max = stoi(options[1]); + continue; + } else if (options[0] == "min") { + data->scrollbar_options.min = stoi(options[1]); + continue; + } else if (options[0] == "smallstep") { + int value = stoi(options[1]); + data->scrollbar_options.small_step = value < 0 ? 10 : value; + continue; + } else if (options[0] == "largestep") { + int value = stoi(options[1]); + data->scrollbar_options.large_step = value < 0 ? 100 : value; + continue; + } else if (options[0] == "thumbsize") { + int value = stoi(options[1]); + data->scrollbar_options.thumb_size = value <= 0 ? 1 : value; + continue; + } else if (options[0] == "arrows") { + std::string value = trim(options[1]); + if (value == "hide") + data->scrollbar_options.arrow_visiblity = GUIScrollBar::HIDE; + else if (value == "show") + data->scrollbar_options.arrow_visiblity = GUIScrollBar::SHOW; + else // Auto hide/show + data->scrollbar_options.arrow_visiblity = GUIScrollBar::DEFAULT; + continue; + } + + warningstream << "Invalid scrollbaroptions option(" << options[0] << + "): '" << element << "'" << std::endl; + } } void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) @@ -2591,6 +2651,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "scrollbaroptions") { + parseScrollBarOptions(data, description); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -2633,24 +2698,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // Remove children removeChildren(); - for (auto &table_it : m_tables) { + for (auto &table_it : m_tables) table_it.second->drop(); - } - for (auto &inventorylist_it : m_inventorylists) { + for (auto &inventorylist_it : m_inventorylists) inventorylist_it.e->drop(); - } - for (auto &checkbox_it : m_checkboxes) { + for (auto &checkbox_it : m_checkboxes) checkbox_it.second->drop(); - } - for (auto &scrollbar_it : m_scrollbars) { + for (auto &scrollbar_it : m_scrollbars) scrollbar_it.second->drop(); - } - for (auto &background_it : m_backgrounds) { + for (auto &background_it : m_backgrounds) background_it->drop(); - } - for (auto &tooltip_rect_it : m_tooltip_rects) { + for (auto &tooltip_rect_it : m_tooltip_rects) tooltip_rect_it.first->drop(); - } mydata.size= v2s32(100,100); mydata.screensize = screensize; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 5be639fa5..3a3a74c05 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -379,7 +379,7 @@ protected: video::SColor m_default_tooltip_bgcolor; video::SColor m_default_tooltip_color; - + private: IFormSource *m_form_src; TextDest *m_text_dst; @@ -401,6 +401,16 @@ private: std::string focused_fieldname; GUITable::TableOptions table_options; GUITable::TableColumns table_columns; + + struct { + s32 max = 1000; + s32 min = 0; + s32 small_step = 10; + s32 large_step = 100; + s32 thumb_size = 1; + GUIScrollBar::ArrowVisibility arrow_visiblity = GUIScrollBar::DEFAULT; + } scrollbar_options; + // used to restore table selection/scroll/treeview state std::unordered_map table_dyndata; } parserData; @@ -455,6 +465,7 @@ private: bool parseVersionDirect(const std::string &data); bool parseSizeDirect(parserData* data, const std::string &element); void parseScrollBar(parserData* data, const std::string &element); + void parseScrollBarOptions(parserData *data, const std::string &element); bool parsePositionDirect(parserData *data, const std::string &element); void parsePosition(parserData *data, const std::string &element); bool parseAnchorDirect(parserData *data, const std::string &element); diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp index f7218e733..b04ccb9d5 100644 --- a/src/gui/guiScrollBar.cpp +++ b/src/gui/guiScrollBar.cpp @@ -247,7 +247,7 @@ s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const w = RelativeRect.getHeight() - border_size * 2 - thumb_size; p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset; } - return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range()) + min_pos : 0; + return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range() + 0.5f) + min_pos : 0; } void GUIScrollBar::setPos(const s32 &pos) @@ -272,7 +272,8 @@ void GUIScrollBar::setPos(const s32 &pos) f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range() : 1.0f; - draw_center = s32((f32(scroll_pos) * f) + (f32(thumb_size) * 0.5f)) + border_size; + draw_center = s32((f32(scroll_pos - min_pos) * f) + (f32(thumb_size) * 0.5f)) + + border_size; } void GUIScrollBar::setSmallStep(const s32 &step) @@ -315,6 +316,12 @@ void GUIScrollBar::setPageSize(const s32 &size) setPos(scroll_pos); } +void GUIScrollBar::setArrowsVisible(ArrowVisibility visible) +{ + arrow_visibility = visible; + refreshControls(); +} + s32 GUIScrollBar::getPos() const { return scroll_pos; @@ -419,7 +426,21 @@ void GUIScrollBar::refreshControls() down_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT); } - bool visible = (border_size != 0); + + bool visible; + if (arrow_visibility == DEFAULT) + visible = (border_size != 0); + else if (arrow_visibility == HIDE) { + visible = false; + border_size = 0; + } else { + visible = true; + if (is_horizontal) + border_size = RelativeRect.getHeight(); + else + border_size = RelativeRect.getWidth(); + } + up_button->setVisible(visible); down_button->setVisible(visible); } diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h index 349411fc1..cb8f3cb01 100644 --- a/src/gui/guiScrollBar.h +++ b/src/gui/guiScrollBar.h @@ -23,6 +23,12 @@ public: GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id, core::rect rectangle, bool horizontal, bool auto_scale); + enum ArrowVisibility { + HIDE, + SHOW, + DEFAULT + }; + virtual void draw(); virtual void updateAbsolutePosition(); virtual bool OnEvent(const SEvent &event); @@ -39,6 +45,7 @@ public: void setLargeStep(const s32 &step); void setPos(const s32 &pos); void setPageSize(const s32 &size); + void setArrowsVisible(ArrowVisibility visible); private: void refreshControls(); @@ -47,6 +54,7 @@ private: IGUIButton *up_button; IGUIButton *down_button; + ArrowVisibility arrow_visibility = DEFAULT; bool is_dragging; bool is_horizontal; bool is_auto_scaling;