Browse Source

Add indeterminate mode to ProgressBar

Christian Kaiser 1 year ago
parent
commit
c00bd0008a
3 changed files with 123 additions and 1 deletions
  1. 6 0
      doc/classes/ProgressBar.xml
  2. 103 1
      scene/gui/progress_bar.cpp
  3. 14 0
      scene/gui/progress_bar.h

+ 6 - 0
doc/classes/ProgressBar.xml

@@ -9,9 +9,15 @@
 	<tutorials>
 	</tutorials>
 	<members>
+		<member name="editor_preview_indeterminate" type="bool" setter="set_editor_preview_indeterminate" getter="is_editor_preview_indeterminate_enabled">
+			If [code]false[/code], the [member indeterminate] animation will be paused in the editor.
+		</member>
 		<member name="fill_mode" type="int" setter="set_fill_mode" getter="get_fill_mode" default="0">
 			The fill direction. See [enum FillMode] for possible values.
 		</member>
+		<member name="indeterminate" type="bool" setter="set_indeterminate" getter="is_indeterminate" default="false">
+			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.
+		</member>
 		<member name="show_percentage" type="bool" setter="set_show_percentage" getter="is_percentage_shown" default="true">
 			If [code]true[/code], the fill percentage is displayed on the bar.
 		</member>

+ 103 - 1
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);

+ 14 - 0
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<StyleBox> 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;
 };