Skip to content

Commit 173a74e

Browse files
committed
Add Simple Content Alignment Feature in ScrollContainer
1 parent 5950fca commit 173a74e

File tree

3 files changed

+165
-2
lines changed

3 files changed

+165
-2
lines changed

doc/classes/ScrollContainer.xml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
<member name="follow_focus" type="bool" setter="set_follow_focus" getter="is_following_focus" default="false">
4747
If [code]true[/code], the ScrollContainer will automatically scroll to focused children (including indirect children) to make sure they are fully visible.
4848
</member>
49+
<member name="horizontal_content_align" type="int" setter="set_horizontal_content_align" getter="get_horizontal_content_align" enum="ScrollContainer.ContentHAlign" default="0">
50+
Controls Horizontal Placement of Contents.
51+
[b]Left[/b] ([constant CONTENT_H_ALIGN_LEFT]) = Horizontally Align Contents in Left [i](Default)[/i]
52+
[b]Center[/b] ([constant CONTENT_H_ALIGN_CENTER]) = Horizontally Align Contents in Center
53+
[b]Right[/b] ([constant CONTENT_H_ALIGN_RIGHT]) = Horizontally Align Contents in Right
54+
</member>
4955
<member name="horizontal_scroll_mode" type="int" setter="set_horizontal_scroll_mode" getter="get_horizontal_scroll_mode" enum="ScrollContainer.ScrollMode" default="1">
5056
Controls whether horizontal scrollbar can be used and when it should be visible.
5157
</member>
@@ -74,6 +80,12 @@
7480
<member name="scroll_vertical_custom_step" type="float" setter="set_vertical_custom_step" getter="get_vertical_custom_step" default="-1.0">
7581
Overrides the [member ScrollBar.custom_step] used when clicking the internal scroll bar's vertical increment and decrement buttons or when using arrow keys when the [ScrollBar] is focused.
7682
</member>
83+
<member name="vertical_content_align" type="int" setter="set_vertical_content_align" getter="get_vertical_content_align" enum="ScrollContainer.ContentVAlign" default="0">
84+
Controls Vertical Placement of Contents.
85+
[b]Top[/b] ([constant CONTENT_V_ALIGN_TOP]) = Vertically Align Contents at Top [i](Default)[/i]
86+
[b]Center[/b] ([constant CONTENT_V_ALIGN_CENTER]) = Vertically Align Contents in Center
87+
[b]Bottom[/b] ([constant CONTENT_V_ALIGN_BOTTOM]) = Vertically Align Contents at Bottom
88+
</member>
7789
<member name="vertical_scroll_mode" type="int" setter="set_vertical_scroll_mode" getter="get_vertical_scroll_mode" enum="ScrollContainer.ScrollMode" default="1">
7890
Controls whether vertical scrollbar can be used and when it should be visible.
7991
</member>
@@ -108,6 +120,24 @@
108120
<constant name="SCROLL_MODE_RESERVE" value="4" enum="ScrollMode">
109121
Combines [constant SCROLL_MODE_AUTO] and [constant SCROLL_MODE_SHOW_ALWAYS]. The scrollbar is only visible if necessary, but the content size is adjusted as if it was always visible. It's useful for ensuring that content size stays the same regardless if the scrollbar is visible.
110122
</constant>
123+
<constant name="CONTENT_H_ALIGN_LEFT" value="0" enum="ContentHAlign">
124+
Left horizontal alignment of contents.
125+
</constant>
126+
<constant name="CONTENT_H_ALIGN_CENTER" value="1" enum="ContentHAlign">
127+
Center horizontal alignment of contents.
128+
</constant>
129+
<constant name="CONTENT_H_ALIGN_RIGHT" value="2" enum="ContentHAlign">
130+
Right horizontal alignment of contents.
131+
</constant>
132+
<constant name="CONTENT_V_ALIGN_TOP" value="0" enum="ContentVAlign">
133+
Top vertical alignment of contents.
134+
</constant>
135+
<constant name="CONTENT_V_ALIGN_CENTER" value="1" enum="ContentVAlign">
136+
Center vertical alignment of contents.
137+
</constant>
138+
<constant name="CONTENT_V_ALIGN_BOTTOM" value="2" enum="ContentVAlign">
139+
Bottom vertical alignment of contents.
140+
</constant>
111141
</constants>
112142
<theme_items>
113143
<theme_item name="focus" data_type="style" type="StyleBox">

scene/gui/scroll_container.cpp

Lines changed: 110 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,45 @@ void ScrollContainer::_reposition_children() {
360360
size.x -= v_scroll->get_minimum_size().x;
361361
}
362362

363+
float viewport_w = size.x;
364+
float viewport_h = size.y;
365+
366+
float total_content_w = 0.0f;
367+
float total_content_h = 0.0f;
368+
for (int ci = 0; ci < get_child_count(); ci++) {
369+
Control *cc = as_sortable_control(get_child(ci));
370+
if (!cc || cc == h_scroll || cc == v_scroll || cc == focus_panel) {
371+
continue;
372+
}
373+
Size2 ms = cc->get_combined_minimum_size();
374+
float child_w = ms.x;
375+
float child_h = ms.y;
376+
if (cc->get_h_size_flags().has_flag(SIZE_EXPAND)) {
377+
child_w = MAX(viewport_w, ms.x);
378+
}
379+
if (cc->get_v_size_flags().has_flag(SIZE_EXPAND)) {
380+
child_h = MAX(viewport_h, ms.y);
381+
}
382+
total_content_w = MAX(total_content_w, child_w);
383+
total_content_h += child_h;
384+
}
385+
386+
float max_scroll_x = total_content_w > viewport_w ? (total_content_w - viewport_w) : 0.0f;
387+
float max_scroll_y = total_content_h > viewport_h ? (total_content_h - viewport_h) : 0.0f;
388+
float scroll_x = h_scroll ? CLAMP(h_scroll->get_value(), 0.0f, max_scroll_x) : 0.0f;
389+
float scroll_y = v_scroll ? CLAMP(v_scroll->get_value(), 0.0f, max_scroll_y) : 0.0f;
390+
391+
float group_v_offset = 0.0f;
392+
if (max_scroll_y <= 0.0f && total_content_h < viewport_h) {
393+
float extra = viewport_h - total_content_h;
394+
if (vertical_content_align == CONTENT_V_ALIGN_CENTER) {
395+
group_v_offset = Math::floor(extra * 0.5f);
396+
} else if (vertical_content_align == CONTENT_V_ALIGN_BOTTOM) {
397+
group_v_offset = Math::floor(extra);
398+
}
399+
}
400+
ofs.y += group_v_offset;
401+
363402
for (int i = 0; i < get_child_count(); i++) {
364403
Control *c = as_sortable_control(get_child(i));
365404
if (!c) {
@@ -370,18 +409,44 @@ void ScrollContainer::_reposition_children() {
370409
}
371410
Size2 minsize = c->get_combined_minimum_size();
372411

373-
Rect2 r = Rect2(-Size2(get_h_scroll(), get_v_scroll()), minsize);
412+
Rect2 r = Rect2(ofs, minsize);
374413
if (c->get_h_size_flags().has_flag(SIZE_EXPAND)) {
375414
r.size.width = MAX(size.width, minsize.width);
376415
}
377416
if (c->get_v_size_flags().has_flag(SIZE_EXPAND)) {
378417
r.size.height = MAX(size.height, minsize.height);
379418
}
380-
r.position += ofs;
381419
if (rtl && reserve_vscroll) {
382420
r.position.x += v_scroll->get_minimum_size().x;
383421
}
384422
r.position = r.position.floor();
423+
424+
{
425+
float content_w = r.size.width;
426+
float viewport_w_local = viewport_w;
427+
ContentHAlign align_h = horizontal_content_align;
428+
if (is_layout_rtl()) {
429+
if (align_h == CONTENT_H_ALIGN_LEFT) {
430+
align_h = CONTENT_H_ALIGN_RIGHT;
431+
} else if (align_h == CONTENT_H_ALIGN_RIGHT) {
432+
align_h = CONTENT_H_ALIGN_LEFT;
433+
}
434+
}
435+
if (max_scroll_x <= 0.0f && content_w < viewport_w_local) {
436+
float extra = viewport_w_local - content_w;
437+
if (align_h == CONTENT_H_ALIGN_CENTER) {
438+
r.position.x += Math::floor(extra * 0.5f);
439+
} else if (align_h == CONTENT_H_ALIGN_RIGHT) {
440+
r.position.x += Math::floor(extra);
441+
}
442+
}
443+
}
444+
445+
r.position.x -= scroll_x;
446+
r.position.y -= scroll_y;
447+
448+
r.position = r.position.floor();
449+
385450
fit_child_in_rect(c, r);
386451
}
387452

@@ -752,12 +817,21 @@ void ScrollContainer::_bind_methods() {
752817
ClassDB::bind_method(D_METHOD("set_draw_focus_border", "draw"), &ScrollContainer::set_draw_focus_border);
753818
ClassDB::bind_method(D_METHOD("get_draw_focus_border"), &ScrollContainer::get_draw_focus_border);
754819

820+
ClassDB::bind_method(D_METHOD("set_horizontal_content_align", "alignment"), &ScrollContainer::set_horizontal_content_align);
821+
ClassDB::bind_method(D_METHOD("get_horizontal_content_align"), &ScrollContainer::get_horizontal_content_align);
822+
823+
ClassDB::bind_method(D_METHOD("set_vertical_content_align", "alignment"), &ScrollContainer::set_vertical_content_align);
824+
ClassDB::bind_method(D_METHOD("get_vertical_content_align"), &ScrollContainer::get_vertical_content_align);
825+
755826
ADD_SIGNAL(MethodInfo("scroll_started"));
756827
ADD_SIGNAL(MethodInfo("scroll_ended"));
757828

758829
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_focus"), "set_follow_focus", "is_following_focus");
759830
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "draw_focus_border"), "set_draw_focus_border", "get_draw_focus_border");
760831

832+
ADD_PROPERTY(PropertyInfo(Variant::INT, "horizontal_content_align", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_horizontal_content_align", "get_horizontal_content_align");
833+
ADD_PROPERTY(PropertyInfo(Variant::INT, "vertical_content_align", PROPERTY_HINT_ENUM, "Top,Center,Bottom"), "set_vertical_content_align", "get_vertical_content_align");
834+
761835
ADD_GROUP("Scroll", "scroll_");
762836
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_horizontal", PROPERTY_HINT_NONE, "suffix:px"), "set_h_scroll", "get_h_scroll");
763837
ADD_PROPERTY(PropertyInfo(Variant::INT, "scroll_vertical", PROPERTY_HINT_NONE, "suffix:px"), "set_v_scroll", "get_v_scroll");
@@ -773,6 +847,14 @@ void ScrollContainer::_bind_methods() {
773847
BIND_ENUM_CONSTANT(SCROLL_MODE_SHOW_NEVER);
774848
BIND_ENUM_CONSTANT(SCROLL_MODE_RESERVE);
775849

850+
BIND_ENUM_CONSTANT(CONTENT_H_ALIGN_LEFT);
851+
BIND_ENUM_CONSTANT(CONTENT_H_ALIGN_CENTER);
852+
BIND_ENUM_CONSTANT(CONTENT_H_ALIGN_RIGHT);
853+
854+
BIND_ENUM_CONSTANT(CONTENT_V_ALIGN_TOP);
855+
BIND_ENUM_CONSTANT(CONTENT_V_ALIGN_CENTER);
856+
BIND_ENUM_CONSTANT(CONTENT_V_ALIGN_BOTTOM);
857+
776858
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, panel_style, "panel");
777859
BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollContainer, focus_style, "focus");
778860

@@ -824,3 +906,29 @@ ScrollContainer::ScrollContainer() {
824906

825907
set_clip_contents(true);
826908
}
909+
910+
void ScrollContainer::set_horizontal_content_align(ContentHAlign p_align) {
911+
ERR_FAIL_INDEX(p_align, CONTENT_H_ALIGN_MAX);
912+
ContentHAlign new_align = static_cast<ContentHAlign>(p_align);
913+
if (horizontal_content_align != new_align) {
914+
horizontal_content_align = new_align;
915+
queue_sort();
916+
}
917+
}
918+
919+
ScrollContainer::ContentHAlign ScrollContainer::get_horizontal_content_align() const {
920+
return horizontal_content_align;
921+
}
922+
923+
void ScrollContainer::set_vertical_content_align(ContentVAlign p_align) {
924+
ERR_FAIL_INDEX(p_align, CONTENT_V_ALIGN_MAX);
925+
ContentVAlign new_align = static_cast<ContentVAlign>(p_align);
926+
if (vertical_content_align != new_align) {
927+
vertical_content_align = new_align;
928+
queue_sort();
929+
}
930+
}
931+
932+
ScrollContainer::ContentVAlign ScrollContainer::get_vertical_content_align() const {
933+
return vertical_content_align;
934+
}

scene/gui/scroll_container.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,28 @@ class ScrollContainer : public Container {
4848
SCROLL_MODE_RESERVE,
4949
};
5050

51+
enum ContentHAlign {
52+
CONTENT_H_ALIGN_LEFT,
53+
CONTENT_H_ALIGN_CENTER,
54+
CONTENT_H_ALIGN_RIGHT,
55+
CONTENT_H_ALIGN_MAX
56+
};
57+
58+
enum ContentVAlign {
59+
CONTENT_V_ALIGN_TOP,
60+
CONTENT_V_ALIGN_CENTER,
61+
CONTENT_V_ALIGN_BOTTOM,
62+
CONTENT_V_ALIGN_MAX
63+
};
64+
5165
private:
5266
HScrollBar *h_scroll = nullptr;
5367
VScrollBar *v_scroll = nullptr;
5468
PanelContainer *focus_panel = nullptr;
5569

70+
ContentHAlign horizontal_content_align = CONTENT_H_ALIGN_LEFT;
71+
ContentVAlign vertical_content_align = CONTENT_V_ALIGN_TOP;
72+
5673
mutable Size2 largest_child_min_size; // The largest one among the min sizes of all available child controls.
5774

5875
void update_scrollbars();
@@ -113,6 +130,12 @@ class ScrollContainer : public Container {
113130
public:
114131
virtual void gui_input(const Ref<InputEvent> &p_gui_input) override;
115132

133+
void set_horizontal_content_align(ContentHAlign p_align);
134+
ContentHAlign get_horizontal_content_align() const;
135+
136+
void set_vertical_content_align(ContentVAlign p_align);
137+
ContentVAlign get_vertical_content_align() const;
138+
116139
void set_h_scroll(int p_pos);
117140
int get_h_scroll() const;
118141

@@ -152,3 +175,5 @@ class ScrollContainer : public Container {
152175
};
153176

154177
VARIANT_ENUM_CAST(ScrollContainer::ScrollMode);
178+
VARIANT_ENUM_CAST(ScrollContainer::ContentHAlign);
179+
VARIANT_ENUM_CAST(ScrollContainer::ContentVAlign);

0 commit comments

Comments
 (0)