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.
This commit is contained in:
SmallJoker
2024-10-08 21:45:27 +02:00
committed by GitHub
parent 291c3ad0c1
commit 13f533d490
8 changed files with 87 additions and 29 deletions

View File

@ -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

View File

@ -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[<X>,<Y>;<W>,<H>;<scrollbar name>;<orientation>;<scroll factor>]`
### `scroll_container[<X>,<Y>;<W>,<H>;<scrollbar name>;<orientation>;<scroll factor>;<content padding>]`
* 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

View File

@ -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

View File

@ -356,7 +356,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data, const std::string &)
void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element)
{
std::vector<std::string> parts;
if (!precheckElement("scroll_container start", element, 4, 5, parts))
if (!precheckElement("scroll_container start", element, 4, 6, parts))
return;
std::vector<std::string> 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<s32> content_padding_px;
if (parts.size() >= 6 && !parts[5].empty()) {
std::vector<std::string> 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;

View File

@ -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);

View File

@ -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<s32> size;
for (gui::IGUIElement *e : Children) {
core::rect<s32> 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<s32>(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();

View File

@ -34,17 +34,18 @@ public:
virtual void draw() override;
inline void setContentPadding(std::optional<s32> 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<s32> m_content_padding_px; //< in pixels
void updateScrolling();
};

View File

@ -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;