Skip to content

Commit 13f533d

Browse files
authored
scrollcontainer: Add automatic scrollbar calculation (luanti-org#14623)
New parameter 'content padding'. When specified, the scrollbar max value is calculated automatically. This aims to reduce manual calculation functions.
1 parent 291c3ad commit 13f533d

File tree

8 files changed

+87
-29
lines changed

8 files changed

+87
-29
lines changed

builtin/mainmenu/settings/dlg_settings.lua

+3-18
Original file line numberDiff line numberDiff line change
@@ -443,19 +443,6 @@ local function build_page_components(page)
443443
end
444444

445445

446-
--- Creates a scrollbaroptions for a scroll_container
447-
--
448-
-- @param visible_l the length of the scroll_container and scrollbar
449-
-- @param total_l length of the scrollable area
450-
-- @param scroll_factor as passed to scroll_container
451-
local function make_scrollbaroptions_for_scroll_container(visible_l, total_l, scroll_factor)
452-
assert(total_l >= visible_l)
453-
local max = total_l - visible_l
454-
local thumb_size = (visible_l / total_l) * max
455-
return ("scrollbaroptions[min=0;max=%f;thumbsize=%f]"):format(max / scroll_factor, thumb_size / scroll_factor)
456-
end
457-
458-
459446
local formspec_show_hack = false
460447

461448

@@ -517,8 +504,8 @@ local function get_formspec(dialogdata)
517504
"tooltip[search;", fgettext("Search"), "]",
518505
"tooltip[search_clear;", fgettext("Clear"), "]",
519506
"container_end[]",
520-
"scroll_container[0.25,1.25;", tostring(left_pane_width), ",",
521-
tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]",
507+
("scroll_container[0.25,1.25;%f,%f;leftscroll;vertical;0.1;0]"):format(
508+
left_pane_width, tabsize.height - 1.5),
522509
"style_type[button;border=false;bgcolor=#3333]",
523510
"style_type[button:hover;border=false;bgcolor=#6663]",
524511
}
@@ -548,7 +535,6 @@ local function get_formspec(dialogdata)
548535
fs[#fs + 1] = "scroll_container_end[]"
549536

550537
if y >= tabsize.height - 1.25 then
551-
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height - 1.5, y, 0.1)
552538
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
553539
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
554540
end
@@ -560,7 +546,7 @@ local function get_formspec(dialogdata)
560546
end
561547

562548
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
563-
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1]"):format(
549+
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1;0.25]"):format(
564550
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
565551

566552
y = 0.25
@@ -616,7 +602,6 @@ local function get_formspec(dialogdata)
616602
fs[#fs + 1] = "scroll_container_end[]"
617603

618604
if y >= tabsize.height then
619-
fs[#fs + 1] = make_scrollbaroptions_for_scroll_container(tabsize.height, y + 0.375, 0.1)
620605
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
621606
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
622607
end

doc/lua_api.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -2747,6 +2747,8 @@ Version History
27472747
* Formspec version 7 (5.8.0):
27482748
* style[]: Add focused state for buttons
27492749
* Add field_enter_after_edit[] (experimental)
2750+
* Formspec version 8 (5.10.0)
2751+
* scroll_container[]: content padding parameter
27502752

27512753
Elements
27522754
--------
@@ -2830,7 +2832,7 @@ Elements
28302832
* End of a container, following elements are no longer relative to this
28312833
container.
28322834

2833-
### `scroll_container[<X>,<Y>;<W>,<H>;<scrollbar name>;<orientation>;<scroll factor>]`
2835+
### `scroll_container[<X>,<Y>;<W>,<H>;<scrollbar name>;<orientation>;<scroll factor>;<content padding>]`
28342836

28352837
* Start of a scroll_container block. All contained elements will ...
28362838
* take the scroll_container coordinate as position origin,
@@ -2839,6 +2841,12 @@ Elements
28392841
* be clipped to the rectangle defined by `X`, `Y`, `W` and `H`.
28402842
* `orientation`: possible values are `vertical` and `horizontal`.
28412843
* `scroll factor`: optional, defaults to `0.1`.
2844+
* `content padding`: (optional), in formspec coordinate units
2845+
* If specified, the scrollbar properties `max` and `thumbsize` are calculated automatically
2846+
based on the content size plus `content padding` at the end of the container. `min` is set to 0.
2847+
* Negative `scroll factor` is not supported.
2848+
* When active, `scrollbaroptions[]` has no effect on the affected properties.
2849+
* Defaults to empty value (= disabled).
28422850
* Nesting is possible.
28432851
* Some elements might work a little different if they are in a scroll_container.
28442852
* Note: If you want the scroll_container to actually work, you also need to add a

games/devtest/mods/testformspec/formspec.lua

+13-2
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,18 @@ local scroll_fs =
299299
"scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height)
300300
"scrollbar[7.5,0;0.3,4;vertical;scrbar;0]"..
301301
"scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]"..
302-
"dropdown[0,6;2;hmdrpdwnnn;Outside,of,container;1]"
302+
"dropdown[0,6;2;hmdrpdwnnn;Outside,of,container;1]"..
303+
"scroll_container[0,8;10,4;scrbar420;vertical;0.1;2]"..
304+
"button[0.5,0.5;10,1;;Container with padding=2]"..
305+
"list[current_player;main;0,5;8,4;]"..
306+
"scroll_container_end[]"..
307+
"scrollbar[10.1,8;0.5,4;vertical;scrbar420;0]"..
308+
-- Buttons for scale comparison
309+
"button[11,8;1,1;;0]"..
310+
"button[11,9;1,1;;1]"..
311+
"button[11,10;1,1;;2]"..
312+
"button[11,11;1,1;;3]"..
313+
"button[11,12;1,1;;4]"
303314

304315
--style_type[label;textcolor=green]
305316
--label[0,0;Green]
@@ -462,7 +473,7 @@ mouse control = true]
462473
]],
463474

464475
-- Scroll containers
465-
"formspec_version[3]size[12,13]" ..
476+
"formspec_version[7]size[12,13]" ..
466477
scroll_fs,
467478

468479
-- Sound

src/gui/guiFormSpecMenu.cpp

+8-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data, const std::string &)
356356
void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element)
357357
{
358358
std::vector<std::string> parts;
359-
if (!precheckElement("scroll_container start", element, 4, 5, parts))
359+
if (!precheckElement("scroll_container start", element, 4, 6, parts))
360360
return;
361361

362362
std::vector<std::string> v_pos = split(parts[0], ',');
@@ -367,6 +367,12 @@ void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &
367367
if (parts.size() >= 5 && !parts[4].empty())
368368
scroll_factor = stof(parts[4]);
369369

370+
std::optional<s32> content_padding_px;
371+
if (parts.size() >= 6 && !parts[5].empty()) {
372+
std::vector<std::string> v_size = { parts[5], parts[5] };
373+
content_padding_px = getRealCoordinateGeometry(v_size)[orientation == "vertical" ? 1 : 0];
374+
}
375+
370376
MY_CHECKPOS("scroll_container", 0);
371377
MY_CHECKGEOM("scroll_container", 1);
372378

@@ -405,6 +411,7 @@ void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &
405411

406412
GUIScrollContainer *mover = new GUIScrollContainer(Environment,
407413
clipper, spec_mover.fid, rect_mover, orientation, scroll_factor);
414+
mover->setContentPadding(content_padding_px);
408415

409416
data->current_parent = mover;
410417

src/gui/guiScrollBar.h

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class GUIScrollBar : public IGUIElement
4545
s32 getSmallStep() const { return small_step; }
4646
s32 getPos() const;
4747
s32 getTargetPos() const;
48+
bool isHorizontal() const { return is_horizontal; }
4849

4950
void setMax(const s32 &max);
5051
void setMin(const s32 &min);

src/gui/guiScrollContainer.cpp

+44
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,50 @@ void GUIScrollContainer::draw()
6767
}
6868
}
6969

70+
void GUIScrollContainer::setScrollBar(GUIScrollBar *scrollbar)
71+
{
72+
m_scrollbar = scrollbar;
73+
74+
if (m_scrollbar && m_content_padding_px.has_value() && m_scrollfactor != 0.0f) {
75+
// Set the scrollbar max value based on the content size.
76+
77+
// Get content size based on elements
78+
core::rect<s32> size;
79+
for (gui::IGUIElement *e : Children) {
80+
core::rect<s32> abs_rect = e->getAbsolutePosition();
81+
size.addInternalPoint(abs_rect.LowerRightCorner);
82+
}
83+
84+
s32 visible_content_px = (
85+
m_orientation == VERTICAL
86+
? AbsoluteClippingRect.getHeight()
87+
: AbsoluteClippingRect.getWidth()
88+
);
89+
90+
s32 total_content_px = *m_content_padding_px + (
91+
m_orientation == VERTICAL
92+
? (size.LowerRightCorner.Y - AbsoluteClippingRect.UpperLeftCorner.Y)
93+
: (size.LowerRightCorner.X - AbsoluteClippingRect.UpperLeftCorner.X)
94+
);
95+
96+
s32 hidden_content_px = std::max<s32>(0, total_content_px - visible_content_px);
97+
m_scrollbar->setMin(0);
98+
m_scrollbar->setMax(std::ceil(hidden_content_px / std::fabs(m_scrollfactor)));
99+
100+
// Note: generally, the scrollbar has the same size as the scroll container.
101+
// However, in case it isn't, proportional adjustments are needed.
102+
s32 scrollbar_px = (
103+
m_scrollbar->isHorizontal()
104+
? m_scrollbar->getRelativePosition().getWidth()
105+
: m_scrollbar->getRelativePosition().getHeight()
106+
);
107+
108+
m_scrollbar->setPageSize((total_content_px * scrollbar_px) / visible_content_px);
109+
}
110+
111+
updateScrolling();
112+
}
113+
70114
void GUIScrollContainer::updateScrolling()
71115
{
72116
s32 pos = m_scrollbar->getPos();

src/gui/guiScrollContainer.h

+8-6
Original file line numberDiff line numberDiff line change
@@ -34,17 +34,18 @@ class GUIScrollContainer : public gui::IGUIElement
3434

3535
virtual void draw() override;
3636

37+
inline void setContentPadding(std::optional<s32> padding)
38+
{
39+
m_content_padding_px = padding;
40+
}
41+
3742
inline void onScrollEvent(gui::IGUIElement *caller)
3843
{
3944
if (caller == m_scrollbar)
4045
updateScrolling();
4146
}
4247

43-
inline void setScrollBar(GUIScrollBar *scrollbar)
44-
{
45-
m_scrollbar = scrollbar;
46-
updateScrolling();
47-
}
48+
void setScrollBar(GUIScrollBar *scrollbar);
4849

4950
private:
5051
enum OrientationEnum
@@ -56,7 +57,8 @@ class GUIScrollContainer : public gui::IGUIElement
5657

5758
GUIScrollBar *m_scrollbar;
5859
OrientationEnum m_orientation;
59-
f32 m_scrollfactor;
60+
f32 m_scrollfactor; //< scrollbar pos * scrollfactor = scroll offset in pixels
61+
std::optional<s32> m_content_padding_px; //< in pixels
6062

6163
void updateScrolling();
6264
};

src/network/networkprotocol.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,4 @@
6363
const u16 LATEST_PROTOCOL_VERSION = 46;
6464

6565
// See also formspec [Version History] in doc/lua_api.md
66-
const u16 FORMSPEC_API_VERSION = 7;
66+
const u16 FORMSPEC_API_VERSION = 8;

0 commit comments

Comments
 (0)