Add scrollbaroptions FormSpec element (#8530)
authorv-rob <robinsonvincent89@gmail.com>
Fri, 6 Dec 2019 20:51:10 +0000 (12:51 -0800)
committerrubenwardy <rw@rubenwardy.com>
Fri, 6 Dec 2019 20:51:10 +0000 (20:51 +0000)
doc/lua_api.txt
src/gui/guiFormSpecMenu.cpp
src/gui/guiFormSpecMenu.h
src/gui/guiScrollBar.cpp
src/gui/guiScrollBar.h

index 697efcdbc1624093b01ec4f66a92dea2bd0845ad..12ab2b4b5ad684ac4b9adc54e55100ac1beb24ac 100644 (file)
@@ -2351,16 +2351,40 @@ Elements
 
 ### `scrollbar[<X>,<Y>;<W>,<H>;<orientation>;<name>;<value>]`
 
-* 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=<int>`
+    * Sets scrollbar minimum value, defaults to `0`.
+* `max=<int>`
+    * Sets scrollbar maximum value, defaults to `1000`.
+      If the max is equal to the min, the scrollbar will be disabled.
+* `smallstep=<int>`
+    * 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=<int>`
+    * 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=<int>`
+    * 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=<show/hide/default>`
+    * Whether to show the arrow buttons on the scrollbar. `default` hides the arrows
+      when the scrollbar gets too small, but shows them otherwise.
+
 ### `table[<X>,<Y>;<W>,<H>;<name>;<cell 1>,<cell 2>,...,<cell n>;<selected idx>]`
 
 * Show scrollable table using options defined by the previous `tableoptions[]`
index f2ec758d95ee369960d73b17b98e871addfe4e6a..26ca525c83d791ea6d30e490a2b3eba77a66ca49 100644 (file)
@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <limits>
 #include <sstream>
 #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<std::string> 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<std::string> 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;
index 5be639fa5fd645383e76dfb9926624f7829bf2d2..3a3a74c05ae4ad56357f231ab57c797ce5534954 100644 (file)
@@ -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<std::string, GUITable::DynamicData> 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);
index f7218e733e532d741ef88f496630710fe70f12fd..b04ccb9d50496361e3ff02ef44738da56d774404 100644 (file)
@@ -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);
 }
index 349411fc12dee8082df56c9c8c574390eeb82720..cb8f3cb01bcf1f1861a14cc9296c27e017ca8153 100644 (file)
@@ -23,6 +23,12 @@ public:
        GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
                        core::rect<s32> 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;