diff --git a/doc/classes/SpinBox.xml b/doc/classes/SpinBox.xml
index 3e39e9eba947..93ee65b219fb 100644
--- a/doc/classes/SpinBox.xml
+++ b/doc/classes/SpinBox.xml
@@ -49,6 +49,9 @@
Changes the alignment of the underlying [LineEdit].
+
+ Controls which side the up and down buttons appear on. [constant BUTTON_ALIGNMENT_DEFAULT] follows the layout direction (right in LTR, left in RTL). [constant BUTTON_ALIGNMENT_LEFT] and [constant BUTTON_ALIGNMENT_RIGHT] override this regardless of layout direction.
+
If [code]true[/code], the value will be rounded to a multiple of [member custom_arrow_step] when interacting with the arrow buttons. Otherwise, increments the value by [member custom_arrow_step] and then rounds it according to [member Range.step].
@@ -75,6 +78,17 @@
[b]Note:[/b] If set to [code]true[/code], this will interfere with entering mathematical expressions in the [SpinBox]. The [SpinBox] will try to evaluate the expression as you type, which means symbols like a trailing [code]+[/code] are removed immediately by the expression being evaluated.
+
+
+ The up and down buttons follow the layout direction: on the right in left-to-right layouts, on the left in right-to-left layouts.
+
+
+ The up and down buttons are always placed on the left, regardless of layout direction.
+
+
+ The up and down buttons are always placed on the right, regardless of layout direction.
+
+
Down button icon modulation color, when the button is disabled.
diff --git a/editor/animation/animation_blend_space_1d_editor.cpp b/editor/animation/animation_blend_space_1d_editor.cpp
index b0f7fdd97359..1a33a24b9ec2 100644
--- a/editor/animation/animation_blend_space_1d_editor.cpp
+++ b/editor/animation/animation_blend_space_1d_editor.cpp
@@ -1080,6 +1080,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
min_value->set_step(STEP_UNIT);
min_value->get_line_edit()->set_expand_to_text_length_enabled(true);
min_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ min_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_RIGHT);
min_value->set_accessibility_name(TTRC("Min"));
max_value = memnew(SpinBox);
@@ -1088,6 +1089,7 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() {
max_value->set_step(STEP_UNIT);
max_value->get_line_edit()->set_expand_to_text_length_enabled(true);
max_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ max_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_LEFT);
max_value->set_accessibility_name(TTRC("Max"));
label_value = memnew(LineEdit);
diff --git a/editor/animation/animation_blend_space_2d_editor.cpp b/editor/animation/animation_blend_space_2d_editor.cpp
index 1d46aa4eba7b..928711dac038 100644
--- a/editor/animation/animation_blend_space_2d_editor.cpp
+++ b/editor/animation/animation_blend_space_2d_editor.cpp
@@ -1339,6 +1339,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
max_y_value->set_accessibility_name(TTRC("Max Y"));
max_y_value->get_line_edit()->set_expand_to_text_length_enabled(true);
max_y_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ max_y_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_LEFT);
left_vbox->add_child(max_y_value);
left_vbox->add_spacer();
label_y = memnew(LineEdit);
@@ -1351,6 +1352,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
min_y_value->set_accessibility_name(TTRC("Min Y"));
min_y_value->get_line_edit()->set_expand_to_text_length_enabled(true);
min_y_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ min_y_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_LEFT);
left_vbox->add_child(min_y_value);
max_y_value->set_max(ABS_MAX);
@@ -1385,6 +1387,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
min_x_value->set_accessibility_name(TTRC("Min X"));
min_x_value->get_line_edit()->set_expand_to_text_length_enabled(true);
min_x_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_LEFT);
+ min_x_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_RIGHT);
bottom_vbox->add_child(min_x_value);
bottom_vbox->add_spacer();
label_x = memnew(LineEdit);
@@ -1397,6 +1400,7 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() {
max_x_value->set_accessibility_name(TTRC("Max X"));
max_x_value->get_line_edit()->set_expand_to_text_length_enabled(true);
max_x_value->set_horizontal_alignment(HORIZONTAL_ALIGNMENT_RIGHT);
+ max_x_value->set_button_alignment(SpinBox::BUTTON_ALIGNMENT_LEFT);
bottom_vbox->add_child(max_x_value);
max_x_value->set_max(ABS_MAX);
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index 9fabf78037da..58254b2cbcef 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -401,23 +401,26 @@ inline void SpinBox::_compute_sizes() {
#endif
int w = min_width_from_icons != 0 ? MAX(buttons_block_icon_enforced_width, buttons_block_wanted_width) : buttons_block_wanted_width;
- if (w != sizing_cache.buttons_block_width) {
+ if (_are_buttons_on_left()) {
+ line_edit->set_offset(SIDE_LEFT, w);
+ line_edit->set_offset(SIDE_RIGHT, 0);
+ } else {
line_edit->set_offset(SIDE_LEFT, 0);
line_edit->set_offset(SIDE_RIGHT, -w);
- sizing_cache.buttons_block_width = w;
}
+ sizing_cache.buttons_block_width = w;
Size2i size = get_size();
sizing_cache.buttons_width = w - theme_cache.field_and_buttons_separation;
sizing_cache.buttons_vertical_separation = CLAMP(theme_cache.buttons_vertical_separation, 0, size.height);
- sizing_cache.buttons_left = is_layout_rtl() ? 0 : size.width - sizing_cache.buttons_width;
+ sizing_cache.buttons_left = _are_buttons_on_left() ? 0 : size.width - sizing_cache.buttons_width;
sizing_cache.button_up_height = (size.height - sizing_cache.buttons_vertical_separation) / 2;
sizing_cache.button_down_height = size.height - sizing_cache.button_up_height - sizing_cache.buttons_vertical_separation;
sizing_cache.second_button_top = size.height - sizing_cache.button_down_height;
sizing_cache.buttons_separator_top = sizing_cache.button_up_height;
- sizing_cache.field_and_buttons_separator_left = is_layout_rtl() ? sizing_cache.buttons_width : size.width - sizing_cache.buttons_block_width;
+ sizing_cache.field_and_buttons_separator_left = _are_buttons_on_left() ? sizing_cache.buttons_width : size.width - sizing_cache.buttons_block_width;
sizing_cache.field_and_buttons_separator_width = theme_cache.field_and_buttons_separation;
}
@@ -437,6 +440,17 @@ inline int SpinBox::_get_widest_button_icon_width() {
return max;
}
+inline bool SpinBox::_are_buttons_on_left() const {
+ switch (button_alignment) {
+ case BUTTON_ALIGNMENT_LEFT:
+ return true;
+ case BUTTON_ALIGNMENT_RIGHT:
+ return false;
+ default:
+ return is_layout_rtl();
+ }
+}
+
void SpinBox::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_DRAW: {
@@ -545,6 +559,7 @@ void SpinBox::_notification(int p_what) {
} break;
case NOTIFICATION_LAYOUT_DIRECTION_CHANGED: {
+ _compute_sizes();
queue_redraw();
} break;
}
@@ -639,6 +654,19 @@ bool SpinBox::is_custom_arrow_rounding() const {
return custom_arrow_round;
}
+void SpinBox::set_button_alignment(ButtonAlignment p_side) {
+ if (button_alignment == p_side) {
+ return;
+ }
+ button_alignment = p_side;
+ _compute_sizes();
+ queue_redraw();
+}
+
+SpinBox::ButtonAlignment SpinBox::get_button_alignment() const {
+ return button_alignment;
+}
+
void SpinBox::_value_changed(double p_value) {
_update_buttons_state_for_current_value();
}
@@ -680,6 +708,12 @@ void SpinBox::_bind_methods() {
ClassDB::bind_method(D_METHOD("is_select_all_on_focus"), &SpinBox::is_select_all_on_focus);
ClassDB::bind_method(D_METHOD("apply"), &SpinBox::apply);
ClassDB::bind_method(D_METHOD("get_line_edit"), &SpinBox::get_line_edit);
+ ClassDB::bind_method(D_METHOD("set_button_alignment", "side"), &SpinBox::set_button_alignment);
+ ClassDB::bind_method(D_METHOD("get_button_alignment"), &SpinBox::get_button_alignment);
+
+ BIND_ENUM_CONSTANT(BUTTON_ALIGNMENT_DEFAULT);
+ BIND_ENUM_CONSTANT(BUTTON_ALIGNMENT_LEFT);
+ BIND_ENUM_CONSTANT(BUTTON_ALIGNMENT_RIGHT);
ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), "set_horizontal_alignment", "get_horizontal_alignment");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "editable"), "set_editable", "is_editable");
@@ -689,6 +723,7 @@ void SpinBox::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "custom_arrow_step", PROPERTY_HINT_RANGE, "0,10000,0.0001,or_greater"), "set_custom_arrow_step", "get_custom_arrow_step");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_arrow_round"), "set_custom_arrow_round", "is_custom_arrow_rounding");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "select_all_on_focus"), "set_select_all_on_focus", "is_select_all_on_focus");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "button_alignment", PROPERTY_HINT_ENUM, "Default,Left,Right"), "set_button_alignment", "get_button_alignment");
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, buttons_vertical_separation);
BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, SpinBox, field_and_buttons_separation);
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 8a4f7a5cc5a9..ffbe20df7c72 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -49,6 +49,14 @@ class SpinBoxLineEdit : public LineEdit {
class SpinBox : public Range {
GDCLASS(SpinBox, Range);
+public:
+ enum ButtonAlignment {
+ BUTTON_ALIGNMENT_DEFAULT,
+ BUTTON_ALIGNMENT_LEFT,
+ BUTTON_ALIGNMENT_RIGHT,
+ };
+
+private:
SpinBoxLineEdit *line_edit = nullptr;
bool update_on_text_changed = false;
bool accepted = true;
@@ -81,6 +89,8 @@ class SpinBox : public Range {
double custom_arrow_step = 0.0;
bool custom_arrow_round = false;
+ ButtonAlignment button_alignment = BUTTON_ALIGNMENT_DEFAULT;
+
void _line_edit_input(const Ref &p_event);
struct Drag {
@@ -104,6 +114,7 @@ class SpinBox : public Range {
inline void _compute_sizes();
inline int _get_widest_button_icon_width();
+ inline bool _are_buttons_on_left() const;
struct ThemeCache {
Ref up_icon;
@@ -187,5 +198,10 @@ class SpinBox : public Range {
void set_custom_arrow_round(bool p_round);
bool is_custom_arrow_rounding() const;
+ void set_button_alignment(ButtonAlignment p_alignment);
+ ButtonAlignment get_button_alignment() const;
+
SpinBox();
};
+
+VARIANT_ENUM_CAST(SpinBox::ButtonAlignment);