From c00bd0008a9ed2d0c31d23c9364f7d818d3beb08 Mon Sep 17 00:00:00 2001 From: Christian Kaiser Date: Wed, 17 Jan 2024 21:49:57 -0300 Subject: [PATCH] Add indeterminate mode to ProgressBar --- doc/classes/ProgressBar.xml | 6 +++ scene/gui/progress_bar.cpp | 104 +++++++++++++++++++++++++++++++++++- scene/gui/progress_bar.h | 14 +++++ 3 files changed, 123 insertions(+), 1 deletion(-) diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml index 1102f0369ac..02f82f6a9a5 100644 --- a/doc/classes/ProgressBar.xml +++ b/doc/classes/ProgressBar.xml @@ -9,9 +9,15 @@ + + If [code]false[/code], the [member indeterminate] animation will be paused in the editor. + The fill direction. See [enum FillMode] for possible values. + + When set to [code]true[/code], the progress bar indicates that something is happening with an animation, but does not show the fill percentage or value. + If [code]true[/code], the fill percentage is displayed on the bar. diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index b86309353b4..c66b30f130b 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -50,9 +50,58 @@ Size2 ProgressBar::get_minimum_size() const { void ProgressBar::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_INTERNAL_PROCESS: { + if (is_visible_in_tree()) { + _inderminate_fill_progress += get_process_delta_time() * MAX(indeterminate_min_speed, MAX(get_size().width, get_size().height) / 2); + queue_redraw(); + } + } break; case NOTIFICATION_DRAW: { draw_style_box(theme_cache.background_style, Rect2(Point2(), get_size())); + if (indeterminate) { + Size2 size = get_size(); + real_t fill_size = MIN(size.width, size.height) * 2; + + if (Engine::get_singleton()->is_editor_hint() && !editor_preview_indeterminate) { + // Center the filled bar when we're not previewing the animation. + _inderminate_fill_progress = (MAX(size.width, size.height) / 2) + (fill_size / 2); + } + + switch (mode) { + case FILL_END_TO_BEGIN: + case FILL_BEGIN_TO_END: { + // Follow the RTL layout with the animation to match how the bar would fill. + bool right_to_left = mode == (is_layout_rtl() ? FILL_BEGIN_TO_END : FILL_END_TO_BEGIN); + + if (_inderminate_fill_progress > size.width + fill_size) { + _inderminate_fill_progress = right_to_left ? -fill_size : 0; + } + + real_t x = right_to_left ? size.width - _inderminate_fill_progress : _inderminate_fill_progress - fill_size; + draw_style_box(theme_cache.fill_style, Rect2(x, 0, fill_size, size.height).intersection(Rect2(Point2(), size))); + } break; + case FILL_TOP_TO_BOTTOM: { + if (_inderminate_fill_progress > size.height + fill_size) { + _inderminate_fill_progress = 0; + } + + draw_style_box(theme_cache.fill_style, Rect2(0, _inderminate_fill_progress - fill_size, size.width, fill_size).intersection(Rect2(Point2(), size))); + } break; + case FILL_BOTTOM_TO_TOP: { + if (_inderminate_fill_progress > size.height + fill_size) { + _inderminate_fill_progress = -fill_size; + } + + draw_style_box(theme_cache.fill_style, Rect2(0, size.height - _inderminate_fill_progress, size.width, fill_size).intersection(Rect2(Point2(), size))); + } break; + case FILL_MODE_MAX: + break; + } + + return; + } + float r = get_as_ratio(); switch (mode) { @@ -62,7 +111,7 @@ void ProgressBar::_notification(int p_what) { int p = round(r * (get_size().width - mp)); // We want FILL_BEGIN_TO_END to map to right to left when UI layout is RTL, // and left to right otherwise. And likewise for FILL_END_TO_BEGIN. - bool right_to_left = is_layout_rtl() ? (mode == FILL_BEGIN_TO_END) : (mode == FILL_END_TO_BEGIN); + bool right_to_left = mode == (is_layout_rtl() ? FILL_BEGIN_TO_END : FILL_END_TO_BEGIN); if (p > 0) { if (right_to_left) { int p_remaining = round((1.0 - r) * (get_size().width - mp)); @@ -130,9 +179,19 @@ void ProgressBar::_notification(int p_what) { } } +void ProgressBar::_validate_property(PropertyInfo &p_property) const { + if (indeterminate && p_property.name == "show_percentage") { + p_property.usage |= PROPERTY_USAGE_READ_ONLY; + } + if (!indeterminate && p_property.name == "editor_preview_indeterminate") { + p_property.usage = PROPERTY_USAGE_NONE; + } +} + void ProgressBar::set_fill_mode(int p_fill) { ERR_FAIL_INDEX(p_fill, FILL_MODE_MAX); mode = (FillMode)p_fill; + _inderminate_fill_progress = 0; queue_redraw(); } @@ -153,14 +212,57 @@ bool ProgressBar::is_percentage_shown() const { return show_percentage; } +void ProgressBar::set_indeterminate(bool p_indeterminate) { + if (indeterminate == p_indeterminate) { + return; + } + indeterminate = p_indeterminate; + _inderminate_fill_progress = 0; + + bool should_process = !Engine::get_singleton()->is_editor_hint() || editor_preview_indeterminate; + set_process_internal(indeterminate && should_process); + + notify_property_list_changed(); + update_minimum_size(); + queue_redraw(); +} + +bool ProgressBar::is_indeterminate() const { + return indeterminate; +} + +void ProgressBar::set_editor_preview_indeterminate(bool p_preview_indeterminate) { + if (editor_preview_indeterminate == p_preview_indeterminate) { + return; + } + editor_preview_indeterminate = p_preview_indeterminate; + + if (Engine::get_singleton()->is_editor_hint()) { + _inderminate_fill_progress = 0; + set_process_internal(indeterminate && editor_preview_indeterminate); + queue_redraw(); + } +} + +bool ProgressBar::is_editor_preview_indeterminate_enabled() const { + return editor_preview_indeterminate; +} + void ProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_fill_mode", "mode"), &ProgressBar::set_fill_mode); ClassDB::bind_method(D_METHOD("get_fill_mode"), &ProgressBar::get_fill_mode); ClassDB::bind_method(D_METHOD("set_show_percentage", "visible"), &ProgressBar::set_show_percentage); ClassDB::bind_method(D_METHOD("is_percentage_shown"), &ProgressBar::is_percentage_shown); + ClassDB::bind_method(D_METHOD("set_indeterminate", "indeterminate"), &ProgressBar::set_indeterminate); + ClassDB::bind_method(D_METHOD("is_indeterminate"), &ProgressBar::is_indeterminate); + ClassDB::bind_method(D_METHOD("set_editor_preview_indeterminate", "preview_indeterminate"), &ProgressBar::set_editor_preview_indeterminate); + ClassDB::bind_method(D_METHOD("is_editor_preview_indeterminate_enabled"), &ProgressBar::is_editor_preview_indeterminate_enabled); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Begin to End,End to Begin,Top to Bottom,Bottom to Top"), "set_fill_mode", "get_fill_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_percentage"), "set_show_percentage", "is_percentage_shown"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "indeterminate"), "set_indeterminate", "is_indeterminate"); + ADD_GROUP("Editor", "editor_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editor_preview_indeterminate"), "set_editor_preview_indeterminate", "is_editor_preview_indeterminate_enabled"); BIND_ENUM_CONSTANT(FILL_BEGIN_TO_END); BIND_ENUM_CONSTANT(FILL_END_TO_BEGIN); diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index 48daf8d6109..2d191b12e63 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -37,6 +37,8 @@ class ProgressBar : public Range { GDCLASS(ProgressBar, Range); bool show_percentage = true; + bool indeterminate = false; + bool editor_preview_indeterminate = false; struct ThemeCache { Ref background_style; @@ -51,8 +53,12 @@ class ProgressBar : public Range { protected: void _notification(int p_what); + void _validate_property(PropertyInfo &p_property) const; + static void _bind_methods(); + double indeterminate_min_speed = 200.0; + public: enum FillMode { FILL_BEGIN_TO_END, @@ -68,10 +74,18 @@ public: void set_show_percentage(bool p_visible); bool is_percentage_shown() const; + void set_indeterminate(bool p_indeterminate); + bool is_indeterminate() const; + + void set_editor_preview_indeterminate(bool p_indeterminate_preview); + bool is_editor_preview_indeterminate_enabled() const; + Size2 get_minimum_size() const override; ProgressBar(); private: + float _inderminate_fill_progress = 0; + FillMode mode = FILL_BEGIN_TO_END; };