mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
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:
parent
291c3ad0c1
commit
13f533d490
8 changed files with 87 additions and 29 deletions
|
@ -443,19 +443,6 @@ local function build_page_components(page)
|
||||||
end
|
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
|
local formspec_show_hack = false
|
||||||
|
|
||||||
|
|
||||||
|
@ -517,8 +504,8 @@ local function get_formspec(dialogdata)
|
||||||
"tooltip[search;", fgettext("Search"), "]",
|
"tooltip[search;", fgettext("Search"), "]",
|
||||||
"tooltip[search_clear;", fgettext("Clear"), "]",
|
"tooltip[search_clear;", fgettext("Clear"), "]",
|
||||||
"container_end[]",
|
"container_end[]",
|
||||||
"scroll_container[0.25,1.25;", tostring(left_pane_width), ",",
|
("scroll_container[0.25,1.25;%f,%f;leftscroll;vertical;0.1;0]"):format(
|
||||||
tostring(tabsize.height - 1.5), ";leftscroll;vertical;0.1]",
|
left_pane_width, tabsize.height - 1.5),
|
||||||
"style_type[button;border=false;bgcolor=#3333]",
|
"style_type[button;border=false;bgcolor=#3333]",
|
||||||
"style_type[button:hover;border=false;bgcolor=#6663]",
|
"style_type[button:hover;border=false;bgcolor=#6663]",
|
||||||
}
|
}
|
||||||
|
@ -548,7 +535,6 @@ local function get_formspec(dialogdata)
|
||||||
fs[#fs + 1] = "scroll_container_end[]"
|
fs[#fs + 1] = "scroll_container_end[]"
|
||||||
|
|
||||||
if y >= tabsize.height - 1.25 then
|
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(
|
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)
|
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
|
||||||
end
|
end
|
||||||
|
@ -560,7 +546,7 @@ local function get_formspec(dialogdata)
|
||||||
end
|
end
|
||||||
|
|
||||||
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
|
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)
|
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
|
||||||
|
|
||||||
y = 0.25
|
y = 0.25
|
||||||
|
@ -616,7 +602,6 @@ local function get_formspec(dialogdata)
|
||||||
fs[#fs + 1] = "scroll_container_end[]"
|
fs[#fs + 1] = "scroll_container_end[]"
|
||||||
|
|
||||||
if y >= tabsize.height then
|
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(
|
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
|
||||||
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
|
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -2747,6 +2747,8 @@ Version History
|
||||||
* Formspec version 7 (5.8.0):
|
* Formspec version 7 (5.8.0):
|
||||||
* style[]: Add focused state for buttons
|
* style[]: Add focused state for buttons
|
||||||
* Add field_enter_after_edit[] (experimental)
|
* Add field_enter_after_edit[] (experimental)
|
||||||
|
* Formspec version 8 (5.10.0)
|
||||||
|
* scroll_container[]: content padding parameter
|
||||||
|
|
||||||
Elements
|
Elements
|
||||||
--------
|
--------
|
||||||
|
@ -2830,7 +2832,7 @@ Elements
|
||||||
* End of a container, following elements are no longer relative to this
|
* End of a container, following elements are no longer relative to this
|
||||||
container.
|
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 ...
|
* Start of a scroll_container block. All contained elements will ...
|
||||||
* take the scroll_container coordinate as position origin,
|
* 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`.
|
* be clipped to the rectangle defined by `X`, `Y`, `W` and `H`.
|
||||||
* `orientation`: possible values are `vertical` and `horizontal`.
|
* `orientation`: possible values are `vertical` and `horizontal`.
|
||||||
* `scroll factor`: optional, defaults to `0.1`.
|
* `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.
|
* Nesting is possible.
|
||||||
* Some elements might work a little different if they are in a scroll_container.
|
* 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
|
* Note: If you want the scroll_container to actually work, you also need to add a
|
||||||
|
|
|
@ -299,7 +299,18 @@ local scroll_fs =
|
||||||
"scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height)
|
"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[7.5,0;0.3,4;vertical;scrbar;0]"..
|
||||||
"scrollbar[8,0;0.3,4;vertical;scrbarhmmm;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]
|
--style_type[label;textcolor=green]
|
||||||
--label[0,0;Green]
|
--label[0,0;Green]
|
||||||
|
@ -462,7 +473,7 @@ mouse control = true]
|
||||||
]],
|
]],
|
||||||
|
|
||||||
-- Scroll containers
|
-- Scroll containers
|
||||||
"formspec_version[3]size[12,13]" ..
|
"formspec_version[7]size[12,13]" ..
|
||||||
scroll_fs,
|
scroll_fs,
|
||||||
|
|
||||||
-- Sound
|
-- Sound
|
||||||
|
|
|
@ -356,7 +356,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data, const std::string &)
|
||||||
void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element)
|
void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element)
|
||||||
{
|
{
|
||||||
std::vector<std::string> parts;
|
std::vector<std::string> parts;
|
||||||
if (!precheckElement("scroll_container start", element, 4, 5, parts))
|
if (!precheckElement("scroll_container start", element, 4, 6, parts))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<std::string> v_pos = split(parts[0], ',');
|
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())
|
if (parts.size() >= 5 && !parts[4].empty())
|
||||||
scroll_factor = stof(parts[4]);
|
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_CHECKPOS("scroll_container", 0);
|
||||||
MY_CHECKGEOM("scroll_container", 1);
|
MY_CHECKGEOM("scroll_container", 1);
|
||||||
|
|
||||||
|
@ -405,6 +411,7 @@ void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &
|
||||||
|
|
||||||
GUIScrollContainer *mover = new GUIScrollContainer(Environment,
|
GUIScrollContainer *mover = new GUIScrollContainer(Environment,
|
||||||
clipper, spec_mover.fid, rect_mover, orientation, scroll_factor);
|
clipper, spec_mover.fid, rect_mover, orientation, scroll_factor);
|
||||||
|
mover->setContentPadding(content_padding_px);
|
||||||
|
|
||||||
data->current_parent = mover;
|
data->current_parent = mover;
|
||||||
|
|
||||||
|
|
|
@ -45,6 +45,7 @@ public:
|
||||||
s32 getSmallStep() const { return small_step; }
|
s32 getSmallStep() const { return small_step; }
|
||||||
s32 getPos() const;
|
s32 getPos() const;
|
||||||
s32 getTargetPos() const;
|
s32 getTargetPos() const;
|
||||||
|
bool isHorizontal() const { return is_horizontal; }
|
||||||
|
|
||||||
void setMax(const s32 &max);
|
void setMax(const s32 &max);
|
||||||
void setMin(const s32 &min);
|
void setMin(const s32 &min);
|
||||||
|
|
|
@ -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()
|
void GUIScrollContainer::updateScrolling()
|
||||||
{
|
{
|
||||||
s32 pos = m_scrollbar->getPos();
|
s32 pos = m_scrollbar->getPos();
|
||||||
|
|
|
@ -34,17 +34,18 @@ public:
|
||||||
|
|
||||||
virtual void draw() override;
|
virtual void draw() override;
|
||||||
|
|
||||||
|
inline void setContentPadding(std::optional<s32> padding)
|
||||||
|
{
|
||||||
|
m_content_padding_px = padding;
|
||||||
|
}
|
||||||
|
|
||||||
inline void onScrollEvent(gui::IGUIElement *caller)
|
inline void onScrollEvent(gui::IGUIElement *caller)
|
||||||
{
|
{
|
||||||
if (caller == m_scrollbar)
|
if (caller == m_scrollbar)
|
||||||
updateScrolling();
|
updateScrolling();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setScrollBar(GUIScrollBar *scrollbar)
|
void setScrollBar(GUIScrollBar *scrollbar);
|
||||||
{
|
|
||||||
m_scrollbar = scrollbar;
|
|
||||||
updateScrolling();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum OrientationEnum
|
enum OrientationEnum
|
||||||
|
@ -56,7 +57,8 @@ private:
|
||||||
|
|
||||||
GUIScrollBar *m_scrollbar;
|
GUIScrollBar *m_scrollbar;
|
||||||
OrientationEnum m_orientation;
|
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();
|
void updateScrolling();
|
||||||
};
|
};
|
||||||
|
|
|
@ -63,4 +63,4 @@
|
||||||
const u16 LATEST_PROTOCOL_VERSION = 46;
|
const u16 LATEST_PROTOCOL_VERSION = 46;
|
||||||
|
|
||||||
// See also formspec [Version History] in doc/lua_api.md
|
// See also formspec [Version History] in doc/lua_api.md
|
||||||
const u16 FORMSPEC_API_VERSION = 7;
|
const u16 FORMSPEC_API_VERSION = 8;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue