From 13f533d4902b3e18a1ab73540555ae979bf139d7 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 8 Oct 2024 21:45:27 +0200 Subject: [PATCH] scrollcontainer: Add automatic scrollbar calculation (#14623) New parameter 'content padding'. When specified, the scrollbar max value is calculated automatically. This aims to reduce manual calculation functions. --- builtin/mainmenu/settings/dlg_settings.lua | 21 ++-------- doc/lua_api.md | 10 ++++- games/devtest/mods/testformspec/formspec.lua | 15 ++++++- src/gui/guiFormSpecMenu.cpp | 9 +++- src/gui/guiScrollBar.h | 1 + src/gui/guiScrollContainer.cpp | 44 ++++++++++++++++++++ src/gui/guiScrollContainer.h | 14 ++++--- src/network/networkprotocol.cpp | 2 +- 8 files changed, 87 insertions(+), 29 deletions(-) diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index 4842b2e1a..3da80e877 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -443,19 +443,6 @@ local function build_page_components(page) end ---- Creates a scrollbaroptions for a scroll_container --- --- @param visible_l the length of the scroll_container and scrollbar --- @param total_l length of the scrollable area --- @param scroll_factor as passed to scroll_container -local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor) - assert(total_l >= visible_l) - local max = total_l - visible_l - local thumb_size = (visible_l / total_l) * max - return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor) -end - - local formspec_show_hack = false @@ -517,8 +504,8 @@ local function get_formspec(dialogdata) "tooltip[search;", fgettext("Search"), "]", "tooltip[search_clear;", fgettext("Clear"), "]", "container_end[]", - "scroll_container[0.25,1.25;", tostring(left_pane_width), ",", - tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]", + ("scroll_container[0.25,1.25;%f,%f;leftscroll;vertical;0.1;0]"):format( + left_pane_width, tabsize.height - 1.5), "style_type[button;border=false;bgcolor=#3333]", "style_type[button:hover;border=false;bgcolor=#6663]", } @@ -548,7 +535,6 @@ local function get_formspec(dialogdata) fs[#fs + 1] = "scroll_container_end[]" if y >= tabsize.height - 1.25 then - fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height - 1.5, y, 0.1) fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format( left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0) end @@ -560,7 +546,7 @@ local function get_formspec(dialogdata) end local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25 - fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1]"):format( + fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1;0.25]"):format( tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height) y = 0.25 @@ -616,7 +602,6 @@ local function get_formspec(dialogdata) fs[#fs + 1] = "scroll_container_end[]" if y >= tabsize.height then - fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height, y + 0.375, 0.1) fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format( tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0) end diff --git a/doc/lua_api.md b/doc/lua_api.md index 9cc25172e..92c19545f 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2747,6 +2747,8 @@ Version History * Formspec version 7 (5.8.0): * style[]: Add focused state for buttons * Add field_enter_after_edit[] (experimental) +* Formspec version 8 (5.10.0) + * scroll_container[]: content padding parameter Elements -------- @@ -2830,7 +2832,7 @@ Elements * End of a container, following elements are no longer relative to this container. -### `scroll_container[,;,;;;]` +### `scroll_container[,;,;;;;]` * Start of a scroll_container block. All contained elements will ... * take the scroll_container coordinate as position origin, @@ -2839,6 +2841,12 @@ Elements * be clipped to the rectangle defined by `X`, `Y`, `W` and `H`. * `orientation`: possible values are `vertical` and `horizontal`. * `scroll factor`: optional, defaults to `0.1`. +* `content padding`: (optional), in formspec coordinate units + * If specified, the scrollbar properties `max` and `thumbsize` are calculated automatically + based on the content size plus `content padding` at the end of the container. `min` is set to 0. + * Negative `scroll factor` is not supported. + * When active, `scrollbaroptions[]` has no effect on the affected properties. + * Defaults to empty value (= disabled). * Nesting is possible. * Some elements might work a little different if they are in a scroll_container. * Note: If you want the scroll_container to actually work, you also need to add a diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 8d0b759f5..f8f17798b 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -299,7 +299,18 @@ local scroll_fs = "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height) "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]".. "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]".. - "dropdown[0,6;2;hmdrpdwnnn;Outside,of,container;1]" + "dropdown[0,6;2;hmdrpdwnnn;Outside,of,container;1]".. + "scroll_container[0,8;10,4;scrbar420;vertical;0.1;2]".. + "button[0.5,0.5;10,1;;Container with padding=2]".. + "list[current_player;main;0,5;8,4;]".. + "scroll_container_end[]".. + "scrollbar[10.1,8;0.5,4;vertical;scrbar420;0]".. + -- Buttons for scale comparison + "button[11,8;1,1;;0]".. + "button[11,9;1,1;;1]".. + "button[11,10;1,1;;2]".. + "button[11,11;1,1;;3]".. + "button[11,12;1,1;;4]" --style_type[label;textcolor=green] --label[0,0;Green] @@ -462,7 +473,7 @@ mouse control = true] ]], -- Scroll containers - "formspec_version[3]size[12,13]" .. + "formspec_version[7]size[12,13]" .. scroll_fs, -- Sound diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 40a445a0c..c9366f83f 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -356,7 +356,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data, const std::string &) void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element) { std::vector parts; - if (!precheckElement("scroll_container start", element, 4, 5, parts)) + if (!precheckElement("scroll_container start", element, 4, 6, parts)) return; std::vector v_pos = split(parts[0], ','); @@ -367,6 +367,12 @@ void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string & if (parts.size() >= 5 && !parts[4].empty()) scroll_factor = stof(parts[4]); + std::optional content_padding_px; + if (parts.size() >= 6 && !parts[5].empty()) { + std::vector v_size = { parts[5], parts[5] }; + content_padding_px = getRealCoordinateGeometry(v_size)[orientation == "vertical" ? 1 : 0]; + } + MY_CHECKPOS("scroll_container", 0); MY_CHECKGEOM("scroll_container", 1); @@ -405,6 +411,7 @@ void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string & GUIScrollContainer *mover = new GUIScrollContainer(Environment, clipper, spec_mover.fid, rect_mover, orientation, scroll_factor); + mover->setContentPadding(content_padding_px); data->current_parent = mover; diff --git a/src/gui/guiScrollBar.h b/src/gui/guiScrollBar.h index 05e195aed..af3bc4652 100644 --- a/src/gui/guiScrollBar.h +++ b/src/gui/guiScrollBar.h @@ -45,6 +45,7 @@ public: s32 getSmallStep() const { return small_step; } s32 getPos() const; s32 getTargetPos() const; + bool isHorizontal() const { return is_horizontal; } void setMax(const s32 &max); void setMin(const s32 &min); diff --git a/src/gui/guiScrollContainer.cpp b/src/gui/guiScrollContainer.cpp index 2d71f3453..13ba5c35f 100644 --- a/src/gui/guiScrollContainer.cpp +++ b/src/gui/guiScrollContainer.cpp @@ -67,6 +67,50 @@ void GUIScrollContainer::draw() } } +void GUIScrollContainer::setScrollBar(GUIScrollBar *scrollbar) +{ + m_scrollbar = scrollbar; + + if (m_scrollbar && m_content_padding_px.has_value() && m_scrollfactor != 0.0f) { + // Set the scrollbar max value based on the content size. + + // Get content size based on elements + core::rect size; + for (gui::IGUIElement *e : Children) { + core::rect abs_rect = e->getAbsolutePosition(); + size.addInternalPoint(abs_rect.LowerRightCorner); + } + + s32 visible_content_px = ( + m_orientation == VERTICAL + ? AbsoluteClippingRect.getHeight() + : AbsoluteClippingRect.getWidth() + ); + + s32 total_content_px = *m_content_padding_px + ( + m_orientation == VERTICAL + ? (size.LowerRightCorner.Y - AbsoluteClippingRect.UpperLeftCorner.Y) + : (size.LowerRightCorner.X - AbsoluteClippingRect.UpperLeftCorner.X) + ); + + s32 hidden_content_px = std::max(0, total_content_px - visible_content_px); + m_scrollbar->setMin(0); + m_scrollbar->setMax(std::ceil(hidden_content_px / std::fabs(m_scrollfactor))); + + // Note: generally, the scrollbar has the same size as the scroll container. + // However, in case it isn't, proportional adjustments are needed. + s32 scrollbar_px = ( + m_scrollbar->isHorizontal() + ? m_scrollbar->getRelativePosition().getWidth() + : m_scrollbar->getRelativePosition().getHeight() + ); + + m_scrollbar->setPageSize((total_content_px * scrollbar_px) / visible_content_px); + } + + updateScrolling(); +} + void GUIScrollContainer::updateScrolling() { s32 pos = m_scrollbar->getPos(); diff --git a/src/gui/guiScrollContainer.h b/src/gui/guiScrollContainer.h index 9e3ec6e93..d6871a53e 100644 --- a/src/gui/guiScrollContainer.h +++ b/src/gui/guiScrollContainer.h @@ -34,17 +34,18 @@ public: virtual void draw() override; + inline void setContentPadding(std::optional padding) + { + m_content_padding_px = padding; + } + inline void onScrollEvent(gui::IGUIElement *caller) { if (caller == m_scrollbar) updateScrolling(); } - inline void setScrollBar(GUIScrollBar *scrollbar) - { - m_scrollbar = scrollbar; - updateScrolling(); - } + void setScrollBar(GUIScrollBar *scrollbar); private: enum OrientationEnum @@ -56,7 +57,8 @@ private: GUIScrollBar *m_scrollbar; OrientationEnum m_orientation; - f32 m_scrollfactor; + f32 m_scrollfactor; //< scrollbar pos * scrollfactor = scroll offset in pixels + std::optional m_content_padding_px; //< in pixels void updateScrolling(); }; diff --git a/src/network/networkprotocol.cpp b/src/network/networkprotocol.cpp index 350b4d734..38b958d24 100644 --- a/src/network/networkprotocol.cpp +++ b/src/network/networkprotocol.cpp @@ -63,4 +63,4 @@ const u16 LATEST_PROTOCOL_VERSION = 46; // See also formspec [Version History] in doc/lua_api.md -const u16 FORMSPEC_API_VERSION = 7; +const u16 FORMSPEC_API_VERSION = 8;