summaryrefslogtreecommitdiff
path: root/scene/gui/scroll_container.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'scene/gui/scroll_container.cpp')
-rw-r--r--scene/gui/scroll_container.cpp263
1 files changed, 132 insertions, 131 deletions
diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp
index fb17adf96e..411891ece8 100644
--- a/scene/gui/scroll_container.cpp
+++ b/scene/gui/scroll_container.cpp
@@ -5,8 +5,8 @@
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
-/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
-/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
@@ -33,24 +33,24 @@
#include "scene/main/window.h"
bool ScrollContainer::clips_input() const {
-
return true;
}
Size2 ScrollContainer::get_minimum_size() const {
-
Ref<StyleBox> sb = get_theme_stylebox("bg");
Size2 min_size;
for (int i = 0; i < get_child_count(); i++) {
-
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c)
+ if (!c) {
continue;
- if (c->is_set_as_toplevel())
+ }
+ if (c->is_set_as_top_level()) {
continue;
- if (c == h_scroll || c == v_scroll)
+ }
+ if (c == h_scroll || c == v_scroll) {
continue;
+ }
Size2 minsize = c->get_combined_minimum_size();
if (!scroll_h) {
@@ -88,14 +88,12 @@ void ScrollContainer::_cancel_drag() {
}
void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
-
double prev_v_scroll = v_scroll->get_value();
double prev_h_scroll = h_scroll->get_value();
Ref<InputEventMouseButton> mb = p_gui_input;
if (mb.is_valid()) {
-
if (mb->get_button_index() == BUTTON_WHEEL_UP && mb->is_pressed()) {
// only horizontal is enabled, scroll horizontally
if (h_scroll->is_visible() && (!v_scroll->is_visible() || mb->get_shift())) {
@@ -126,17 +124,19 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
accept_event(); //accept event if scroll changed
+ }
- if (!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id())))
+ if (!DisplayServer::get_singleton()->screen_is_touchscreen(DisplayServer::get_singleton()->window_get_current_screen(get_viewport()->get_window_id()))) {
return;
+ }
- if (mb->get_button_index() != BUTTON_LEFT)
+ if (mb->get_button_index() != BUTTON_LEFT) {
return;
+ }
if (mb->is_pressed()) {
-
if (drag_touching) {
_cancel_drag();
}
@@ -156,11 +156,9 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
} else {
if (drag_touching) {
-
if (drag_speed == Vector2()) {
_cancel_drag();
} else {
-
drag_touching_deaccel = true;
}
}
@@ -170,9 +168,7 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventMouseMotion> mm = p_gui_input;
if (mm.is_valid()) {
-
if (drag_touching && !drag_touching_deaccel) {
-
Vector2 motion = Vector2(mm->get_relative().x, mm->get_relative().y);
drag_accum -= motion;
@@ -186,14 +182,16 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
drag_accum = -motion;
}
Vector2 diff = drag_from + drag_accum;
- if (scroll_h)
+ if (scroll_h) {
h_scroll->set_value(diff.x);
- else
+ } else {
drag_accum.x = 0;
- if (scroll_v)
+ }
+ if (scroll_v) {
v_scroll->set_value(diff.y);
- else
+ } else {
drag_accum.y = 0;
+ }
time_since_motion = 0;
}
}
@@ -201,7 +199,6 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
Ref<InputEventPanGesture> pan_gesture = p_gui_input;
if (pan_gesture.is_valid()) {
-
if (h_scroll->is_visible_in_tree()) {
h_scroll->set_value(h_scroll->get_value() + h_scroll->get_page() * pan_gesture->get_delta().x / 8);
}
@@ -210,31 +207,36 @@ void ScrollContainer::_gui_input(const Ref<InputEvent> &p_gui_input) {
}
}
- if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll)
+ if (v_scroll->get_value() != prev_v_scroll || h_scroll->get_value() != prev_h_scroll) {
accept_event(); //accept event if scroll changed
+ }
}
void ScrollContainer::_update_scrollbar_position() {
+ if (!_updating_scrollbars) {
+ return;
+ }
Size2 hmin = h_scroll->get_combined_minimum_size();
Size2 vmin = v_scroll->get_combined_minimum_size();
- h_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 0);
- h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
- h_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -hmin.height);
- h_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
+ h_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_BEGIN, 0);
+ h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ h_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_END, -hmin.height);
+ h_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
- v_scroll->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_END, -vmin.width);
- v_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, 0);
- v_scroll->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 0);
- v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_LEFT, ANCHOR_END, -vmin.width);
+ v_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, 0);
+ v_scroll->set_anchor_and_offset(SIDE_TOP, ANCHOR_BEGIN, 0);
+ v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, 0);
h_scroll->raise();
v_scroll->raise();
+
+ _updating_scrollbars = false;
}
void ScrollContainer::_ensure_focused_visible(Control *p_control) {
-
if (!follow_focus) {
return;
}
@@ -242,87 +244,103 @@ void ScrollContainer::_ensure_focused_visible(Control *p_control) {
if (is_a_parent_of(p_control)) {
Rect2 global_rect = get_global_rect();
Rect2 other_rect = p_control->get_global_rect();
- float right_margin = 0;
+ float right_margin = 0.0;
if (v_scroll->is_visible()) {
right_margin += v_scroll->get_size().x;
}
- float bottom_margin = 0;
+ float bottom_margin = 0.0;
if (h_scroll->is_visible()) {
bottom_margin += h_scroll->get_size().y;
}
float diff = MAX(MIN(other_rect.position.y, global_rect.position.y), other_rect.position.y + other_rect.size.y - global_rect.size.y + bottom_margin);
set_v_scroll(get_v_scroll() + (diff - global_rect.position.y));
- diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + right_margin);
+ if (is_layout_rtl()) {
+ diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x);
+ } else {
+ diff = MAX(MIN(other_rect.position.x, global_rect.position.x), other_rect.position.x + other_rect.size.x - global_rect.size.x + right_margin);
+ }
set_h_scroll(get_h_scroll() + (diff - global_rect.position.x));
}
}
-void ScrollContainer::_notification(int p_what) {
+void ScrollContainer::_update_dimensions() {
+ child_max_size = Size2(0, 0);
+ Size2 size = get_size();
+ Point2 ofs;
+
+ Ref<StyleBox> sb = get_theme_stylebox("bg");
+ size -= sb->get_minimum_size();
+ ofs += sb->get_offset();
+ bool rtl = is_layout_rtl();
+
+ if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
+ size.y -= h_scroll->get_minimum_size().y;
+ }
+
+ if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) { //scrolls may have been moved out for reasons
+ size.x -= v_scroll->get_minimum_size().x;
+ }
+
+ for (int i = 0; i < get_child_count(); i++) {
+ Control *c = Object::cast_to<Control>(get_child(i));
+ if (!c) {
+ continue;
+ }
+ if (c->is_set_as_top_level()) {
+ continue;
+ }
+ if (c == h_scroll || c == v_scroll) {
+ continue;
+ }
+ Size2 minsize = c->get_combined_minimum_size();
+ child_max_size.x = MAX(child_max_size.x, minsize.x);
+ child_max_size.y = MAX(child_max_size.y, minsize.y);
+
+ Rect2 r = Rect2(-scroll, minsize);
+ if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
+ r.position.x = 0;
+ if (c->get_h_size_flags() & SIZE_EXPAND) {
+ r.size.width = MAX(size.width, minsize.width);
+ } else {
+ r.size.width = minsize.width;
+ }
+ }
+ if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
+ r.position.y = 0;
+ if (c->get_v_size_flags() & SIZE_EXPAND) {
+ r.size.height = MAX(size.height, minsize.height);
+ } else {
+ r.size.height = minsize.height;
+ }
+ }
+ r.position += ofs;
+ if (rtl && v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) {
+ r.position.x += v_scroll->get_minimum_size().x;
+ }
+ r.position = r.position.floor();
+ fit_child_in_rect(c, r);
+ }
- if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
+ update();
+}
+void ScrollContainer::_notification(int p_what) {
+ if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED || p_what == NOTIFICATION_LAYOUT_DIRECTION_CHANGED || p_what == NOTIFICATION_TRANSLATION_CHANGED) {
+ _updating_scrollbars = true;
call_deferred("_update_scrollbar_position");
};
if (p_what == NOTIFICATION_READY) {
-
get_viewport()->connect("gui_focus_changed", callable_mp(this, &ScrollContainer::_ensure_focused_visible));
+ _update_dimensions();
}
if (p_what == NOTIFICATION_SORT_CHILDREN) {
-
- child_max_size = Size2(0, 0);
- Size2 size = get_size();
- Point2 ofs;
-
- Ref<StyleBox> sb = get_theme_stylebox("bg");
- size -= sb->get_minimum_size();
- ofs += sb->get_offset();
-
- if (h_scroll->is_visible_in_tree() && h_scroll->get_parent() == this) //scrolls may have been moved out for reasons
- size.y -= h_scroll->get_minimum_size().y;
-
- if (v_scroll->is_visible_in_tree() && v_scroll->get_parent() == this) //scrolls may have been moved out for reasons
- size.x -= v_scroll->get_minimum_size().x;
-
- for (int i = 0; i < get_child_count(); i++) {
-
- Control *c = Object::cast_to<Control>(get_child(i));
- if (!c)
- continue;
- if (c->is_set_as_toplevel())
- continue;
- if (c == h_scroll || c == v_scroll)
- continue;
- Size2 minsize = c->get_combined_minimum_size();
- child_max_size.x = MAX(child_max_size.x, minsize.x);
- child_max_size.y = MAX(child_max_size.y, minsize.y);
-
- Rect2 r = Rect2(-scroll, minsize);
- if (!scroll_h || (!h_scroll->is_visible_in_tree() && c->get_h_size_flags() & SIZE_EXPAND)) {
- r.position.x = 0;
- if (c->get_h_size_flags() & SIZE_EXPAND)
- r.size.width = MAX(size.width, minsize.width);
- else
- r.size.width = minsize.width;
- }
- if (!scroll_v || (!v_scroll->is_visible_in_tree() && c->get_v_size_flags() & SIZE_EXPAND)) {
- r.position.y = 0;
- if (c->get_v_size_flags() & SIZE_EXPAND)
- r.size.height = MAX(size.height, minsize.height);
- else
- r.size.height = minsize.height;
- }
- r.position += ofs;
- fit_child_in_rect(c, r);
- }
-
- update();
+ _update_dimensions();
};
if (p_what == NOTIFICATION_DRAW) {
-
Ref<StyleBox> sb = get_theme_stylebox("bg");
draw_style_box(sb, Rect2(Vector2(), get_size()));
@@ -330,11 +348,8 @@ void ScrollContainer::_notification(int p_what) {
}
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
-
if (drag_touching) {
-
if (drag_touching_deaccel) {
-
Vector2 pos = Vector2(h_scroll->get_value(), v_scroll->get_value());
pos += drag_speed * get_physics_process_delta_time();
@@ -359,10 +374,12 @@ void ScrollContainer::_notification(int p_what) {
turnoff_v = true;
}
- if (scroll_h)
+ if (scroll_h) {
h_scroll->set_value(pos.x);
- if (scroll_v)
+ }
+ if (scroll_v) {
v_scroll->set_value(pos.y);
+ }
float sgn_x = drag_speed.x < 0 ? -1 : 1;
float val_x = Math::abs(drag_speed.x);
@@ -387,9 +404,7 @@ void ScrollContainer::_notification(int p_what) {
}
} else {
-
if (time_since_motion == 0 || time_since_motion > 0.1) {
-
Vector2 diff = drag_accum - last_drag_accum;
last_drag_accum = drag_accum;
drag_speed = diff / get_physics_process_delta_time();
@@ -402,7 +417,6 @@ void ScrollContainer::_notification(int p_what) {
};
void ScrollContainer::update_scrollbars() {
-
Size2 size = get_size();
Ref<StyleBox> sb = get_theme_stylebox("bg");
size -= sb->get_minimum_size();
@@ -421,14 +435,13 @@ void ScrollContainer::update_scrollbars() {
bool hide_scroll_v = !scroll_v || min.height <= size.height;
bool hide_scroll_h = !scroll_h || min.width <= size.width;
+ v_scroll->set_max(min.height);
if (hide_scroll_v) {
-
+ v_scroll->set_page(size.height);
v_scroll->hide();
scroll.y = 0;
} else {
-
v_scroll->show();
- v_scroll->set_max(min.height);
if (hide_scroll_h) {
v_scroll->set_page(size.height);
} else {
@@ -438,14 +451,13 @@ void ScrollContainer::update_scrollbars() {
scroll.y = v_scroll->get_value();
}
+ h_scroll->set_max(min.width);
if (hide_scroll_h) {
-
+ h_scroll->set_page(size.width);
h_scroll->hide();
scroll.x = 0;
} else {
-
h_scroll->show();
- h_scroll->set_max(min.width);
if (hide_scroll_v) {
h_scroll->set_page(size.width);
} else {
@@ -456,12 +468,11 @@ void ScrollContainer::update_scrollbars() {
}
// Avoid scrollbar overlapping.
- h_scroll->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width);
- v_scroll->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height);
+ h_scroll->set_anchor_and_offset(SIDE_RIGHT, ANCHOR_END, hide_scroll_v ? 0 : -vmin.width);
+ v_scroll->set_anchor_and_offset(SIDE_BOTTOM, ANCHOR_END, hide_scroll_h ? 0 : -hmin.height);
}
void ScrollContainer::_scroll_moved(float) {
-
scroll.x = h_scroll->get_value();
scroll.y = v_scroll->get_value();
queue_sort();
@@ -480,7 +491,6 @@ void ScrollContainer::set_enable_h_scroll(bool p_enable) {
}
bool ScrollContainer::is_h_scroll_enabled() const {
-
return scroll_h;
}
@@ -495,26 +505,23 @@ void ScrollContainer::set_enable_v_scroll(bool p_enable) {
}
bool ScrollContainer::is_v_scroll_enabled() const {
-
return scroll_v;
}
int ScrollContainer::get_v_scroll() const {
-
return v_scroll->get_value();
}
-void ScrollContainer::set_v_scroll(int p_pos) {
+void ScrollContainer::set_v_scroll(int p_pos) {
v_scroll->set_value(p_pos);
_cancel_drag();
}
int ScrollContainer::get_h_scroll() const {
-
return h_scroll->get_value();
}
-void ScrollContainer::set_h_scroll(int p_pos) {
+void ScrollContainer::set_h_scroll(int p_pos) {
h_scroll->set_value(p_pos);
_cancel_drag();
}
@@ -536,40 +543,43 @@ void ScrollContainer::set_follow_focus(bool p_follow) {
}
String ScrollContainer::get_configuration_warning() const {
+ String warning = Container::get_configuration_warning();
int found = 0;
for (int i = 0; i < get_child_count(); i++) {
-
Control *c = Object::cast_to<Control>(get_child(i));
- if (!c)
+ if (!c) {
continue;
- if (c->is_set_as_toplevel())
+ }
+ if (c->is_set_as_top_level()) {
continue;
- if (c == h_scroll || c == v_scroll)
+ }
+ if (c == h_scroll || c == v_scroll) {
continue;
+ }
found++;
}
- if (found != 1)
- return TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually.");
- else
- return "";
+ if (found != 1) {
+ if (!warning.is_empty()) {
+ warning += "\n\n";
+ }
+ warning += TTR("ScrollContainer is intended to work with a single child control.\nUse a container as child (VBox, HBox, etc.), or a Control and set the custom minimum size manually.");
+ }
+ return warning;
}
HScrollBar *ScrollContainer::get_h_scrollbar() {
-
return h_scroll;
}
VScrollBar *ScrollContainer::get_v_scrollbar() {
-
return v_scroll;
}
void ScrollContainer::_bind_methods() {
-
ClassDB::bind_method(D_METHOD("_gui_input"), &ScrollContainer::_gui_input);
ClassDB::bind_method(D_METHOD("set_enable_h_scroll", "enable"), &ScrollContainer::set_enable_h_scroll);
ClassDB::bind_method(D_METHOD("is_h_scroll_enabled"), &ScrollContainer::is_h_scroll_enabled);
@@ -604,7 +614,6 @@ void ScrollContainer::_bind_methods() {
};
ScrollContainer::ScrollContainer() {
-
h_scroll = memnew(HScrollBar);
h_scroll->set_name("_h_scroll");
add_child(h_scroll);
@@ -615,15 +624,7 @@ ScrollContainer::ScrollContainer() {
add_child(v_scroll);
v_scroll->connect("value_changed", callable_mp(this, &ScrollContainer::_scroll_moved));
- drag_speed = Vector2();
- drag_touching = false;
- drag_touching_deaccel = false;
- beyond_deadzone = false;
- scroll_h = true;
- scroll_v = true;
-
deadzone = GLOBAL_GET("gui/common/default_scroll_deadzone");
- follow_focus = false;
set_clip_contents(true);
};