Add pivot_offset_ratio property to Control

This commit is contained in:
kobewi 2022-12-27 23:51:08 +01:00
parent 6e33e3b7af
commit 618afcffa6
5 changed files with 57 additions and 11 deletions

View file

@ -409,6 +409,12 @@
Returns combined minimum size from [member custom_minimum_size] and [method get_minimum_size]. Returns combined minimum size from [member custom_minimum_size] and [method get_minimum_size].
</description> </description>
</method> </method>
<method name="get_combined_pivot_offset" qualifiers="const">
<return type="Vector2" />
<description>
Returns the combined value of [member pivot_offset] and [member pivot_offset_ratio], in pixels. The ratio is multiplied by the control's size.
</description>
</method>
<method name="get_cursor_shape" qualifiers="const"> <method name="get_cursor_shape" qualifiers="const">
<return type="int" enum="Control.CursorShape" /> <return type="int" enum="Control.CursorShape" />
<param index="0" name="position" type="Vector2" default="Vector2(0, 0)" /> <param index="0" name="position" type="Vector2" default="Vector2(0, 0)" />
@ -1090,7 +1096,12 @@
</member> </member>
<member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="2" /> <member name="physics_interpolation_mode" type="int" setter="set_physics_interpolation_mode" getter="get_physics_interpolation_mode" overrides="Node" enum="Node.PhysicsInterpolationMode" default="2" />
<member name="pivot_offset" type="Vector2" setter="set_pivot_offset" getter="get_pivot_offset" default="Vector2(0, 0)"> <member name="pivot_offset" type="Vector2" setter="set_pivot_offset" getter="get_pivot_offset" default="Vector2(0, 0)">
By default, the node's pivot is its top-left corner. When you change its [member rotation] or [member scale], it will rotate or scale around this pivot. Set this property to [member size] / 2 to pivot around the Control's center. By default, the node's pivot is its top-left corner. When you change its [member rotation] or [member scale], it will rotate or scale around this pivot.
The actual offset is the combined value of this property and [member pivot_offset_ratio].
</member>
<member name="pivot_offset_ratio" type="Vector2" setter="set_pivot_offset_ratio" getter="get_pivot_offset_ratio" default="Vector2(0, 0)">
Same as [member pivot_offset], but expressed as uniform vector, where [code]Vector2(0, 0)[/code] is the top-left corner of this control, and [code]Vector2(1, 1)[/code] is its bottom-right corner. Set this property to [code]Vector2(0.5, 0.5)[/code] to pivot around this control's center.
The actual offset is the combined value of this property and [member pivot_offset].
</member> </member>
<member name="position" type="Vector2" setter="_set_position" getter="get_position" default="Vector2(0, 0)"> <member name="position" type="Vector2" setter="_set_position" getter="get_position" default="Vector2(0, 0)">
The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset]. The node's position, relative to its containing node. It corresponds to the rectangle's top-left corner. The property is not affected by [member pivot_offset].

View file

@ -4205,17 +4205,18 @@ void CanvasItemEditor::_notification(int p_what) {
Control *control = Object::cast_to<Control>(ci); Control *control = Object::cast_to<Control>(ci);
if (control) { if (control) {
real_t anchors[4]; Vector2 pivot = control->get_pivot_offset();
Vector2 pivot; Vector2 pivot_ratio = control->get_pivot_offset_ratio();
pivot = control->get_pivot_offset(); real_t anchors[4];
anchors[SIDE_LEFT] = control->get_anchor(SIDE_LEFT); anchors[SIDE_LEFT] = control->get_anchor(SIDE_LEFT);
anchors[SIDE_RIGHT] = control->get_anchor(SIDE_RIGHT); anchors[SIDE_RIGHT] = control->get_anchor(SIDE_RIGHT);
anchors[SIDE_TOP] = control->get_anchor(SIDE_TOP); anchors[SIDE_TOP] = control->get_anchor(SIDE_TOP);
anchors[SIDE_BOTTOM] = control->get_anchor(SIDE_BOTTOM); anchors[SIDE_BOTTOM] = control->get_anchor(SIDE_BOTTOM);
if (pivot != se->prev_pivot || anchors[SIDE_LEFT] != se->prev_anchors[SIDE_LEFT] || anchors[SIDE_RIGHT] != se->prev_anchors[SIDE_RIGHT] || anchors[SIDE_TOP] != se->prev_anchors[SIDE_TOP] || anchors[SIDE_BOTTOM] != se->prev_anchors[SIDE_BOTTOM]) { if (pivot != se->prev_pivot || pivot_ratio != se->prev_pivot_ratio || anchors[SIDE_LEFT] != se->prev_anchors[SIDE_LEFT] || anchors[SIDE_RIGHT] != se->prev_anchors[SIDE_RIGHT] || anchors[SIDE_TOP] != se->prev_anchors[SIDE_TOP] || anchors[SIDE_BOTTOM] != se->prev_anchors[SIDE_BOTTOM]) {
se->prev_pivot = pivot; se->prev_pivot = pivot;
se->prev_pivot_ratio = pivot_ratio;
se->prev_anchors[SIDE_LEFT] = anchors[SIDE_LEFT]; se->prev_anchors[SIDE_LEFT] = anchors[SIDE_LEFT];
se->prev_anchors[SIDE_RIGHT] = anchors[SIDE_RIGHT]; se->prev_anchors[SIDE_RIGHT] = anchors[SIDE_RIGHT];
se->prev_anchors[SIDE_TOP] = anchors[SIDE_TOP]; se->prev_anchors[SIDE_TOP] = anchors[SIDE_TOP];

View file

@ -58,6 +58,7 @@ public:
Transform2D prev_xform; Transform2D prev_xform;
Rect2 prev_rect; Rect2 prev_rect;
Vector2 prev_pivot; Vector2 prev_pivot;
Vector2 prev_pivot_ratio;
real_t prev_anchors[4] = { (real_t)0.0 }; real_t prev_anchors[4] = { (real_t)0.0 };
Transform2D pre_drag_xform; Transform2D pre_drag_xform;

View file

@ -58,6 +58,7 @@ Dictionary Control::_edit_get_state() const {
s["rotation"] = get_rotation(); s["rotation"] = get_rotation();
s["scale"] = get_scale(); s["scale"] = get_scale();
s["pivot"] = get_pivot_offset(); s["pivot"] = get_pivot_offset();
s["pivot_ratio"] = get_pivot_offset_ratio();
Array anchors = { get_anchor(SIDE_LEFT), get_anchor(SIDE_TOP), get_anchor(SIDE_RIGHT), get_anchor(SIDE_BOTTOM) }; Array anchors = { get_anchor(SIDE_LEFT), get_anchor(SIDE_TOP), get_anchor(SIDE_RIGHT), get_anchor(SIDE_BOTTOM) };
s["anchors"] = anchors; s["anchors"] = anchors;
@ -74,13 +75,14 @@ Dictionary Control::_edit_get_state() const {
void Control::_edit_set_state(const Dictionary &p_state) { void Control::_edit_set_state(const Dictionary &p_state) {
ERR_FAIL_COND(p_state.is_empty() || ERR_FAIL_COND(p_state.is_empty() ||
!p_state.has("rotation") || !p_state.has("scale") || !p_state.has("rotation") || !p_state.has("scale") ||
!p_state.has("pivot") || !p_state.has("anchors") || !p_state.has("offsets") || !p_state.has("pivot") || !p_state.has("pivot_ratio") || !p_state.has("anchors") || !p_state.has("offsets") ||
!p_state.has("layout_mode") || !p_state.has("anchors_layout_preset")); !p_state.has("layout_mode") || !p_state.has("anchors_layout_preset"));
Dictionary state = p_state; Dictionary state = p_state;
set_rotation(state["rotation"]); set_rotation(state["rotation"]);
set_scale(state["scale"]); set_scale(state["scale"]);
set_pivot_offset(state["pivot"]); set_pivot_offset(state["pivot"]);
set_pivot_offset_ratio(state["pivot_ratio"]);
Array anchors = state["anchors"]; Array anchors = state["anchors"];
@ -152,10 +154,11 @@ void Control::_edit_set_pivot(const Point2 &p_pivot) {
Vector2 move = Vector2((std::cos(data.rotation) - 1.0) * delta_pivot.x - std::sin(data.rotation) * delta_pivot.y, std::sin(data.rotation) * delta_pivot.x + (std::cos(data.rotation) - 1.0) * delta_pivot.y); Vector2 move = Vector2((std::cos(data.rotation) - 1.0) * delta_pivot.x - std::sin(data.rotation) * delta_pivot.y, std::sin(data.rotation) * delta_pivot.x + (std::cos(data.rotation) - 1.0) * delta_pivot.y);
set_position(get_position() + move); set_position(get_position() + move);
set_pivot_offset(p_pivot); set_pivot_offset(p_pivot);
set_pivot_offset_ratio(Vector2());
} }
Point2 Control::_edit_get_pivot() const { Point2 Control::_edit_get_pivot() const {
return get_pivot_offset(); return get_combined_pivot_offset();
} }
bool Control::_edit_use_pivot() const { bool Control::_edit_use_pivot() const {
@ -536,7 +539,7 @@ void Control::_validate_property(PropertyInfo &p_property) const {
// If the parent is a container, display only container-related properties. // If the parent is a container, display only container-related properties.
if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset") { if (p_property.name.begins_with("anchor_") || p_property.name.begins_with("offset_") || p_property.name.begins_with("grow_") || p_property.name == "anchors_preset") {
p_property.usage ^= PROPERTY_USAGE_DEFAULT; p_property.usage ^= PROPERTY_USAGE_DEFAULT;
} else if (p_property.name == "position" || p_property.name == "rotation" || p_property.name == "scale" || p_property.name == "size" || p_property.name == "pivot_offset") { } else if (p_property.name == "position" || p_property.name == "rotation" || p_property.name == "scale" || p_property.name == "size" || p_property.name == "pivot_offset" || p_property.name == "pivot_offset_ratio") {
p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY; p_property.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_READ_ONLY;
} else if (Engine::get_singleton()->is_editor_hint() && p_property.name == "layout_mode") { } else if (Engine::get_singleton()->is_editor_hint() && p_property.name == "layout_mode") {
// Set the layout mode to be disabled with the proper value. // Set the layout mode to be disabled with the proper value.
@ -709,8 +712,8 @@ Size2 Control::get_parent_area_size() const {
Transform2D Control::_get_internal_transform() const { Transform2D Control::_get_internal_transform() const {
// T(pivot_offset) * R(rotation) * S(scale) * T(-pivot_offset) // T(pivot_offset) * R(rotation) * S(scale) * T(-pivot_offset)
Transform2D xform(data.rotation, data.scale, 0.0f, data.pivot_offset); Transform2D xform(data.rotation, data.scale, 0.0f, get_combined_pivot_offset());
xform.translate_local(-data.pivot_offset); xform.translate_local(-get_combined_pivot_offset());
return xform; return xform;
} }
@ -1606,6 +1609,23 @@ real_t Control::get_rotation_degrees() const {
return Math::rad_to_deg(get_rotation()); return Math::rad_to_deg(get_rotation());
} }
void Control::set_pivot_offset_ratio(const Vector2 &p_ratio) {
ERR_MAIN_THREAD_GUARD;
if (data.pivot_offset_ratio == p_ratio) {
return;
}
data.pivot_offset_ratio = p_ratio;
queue_redraw();
_notify_transform();
queue_accessibility_update();
}
Vector2 Control::get_pivot_offset_ratio() const {
ERR_READ_THREAD_GUARD_V(Vector2());
return data.pivot_offset_ratio;
}
void Control::set_pivot_offset(const Vector2 &p_pivot) { void Control::set_pivot_offset(const Vector2 &p_pivot) {
ERR_MAIN_THREAD_GUARD; ERR_MAIN_THREAD_GUARD;
if (data.pivot_offset == p_pivot) { if (data.pivot_offset == p_pivot) {
@ -1623,6 +1643,11 @@ Vector2 Control::get_pivot_offset() const {
return data.pivot_offset; return data.pivot_offset;
} }
Vector2 Control::get_combined_pivot_offset() const {
ERR_READ_THREAD_GUARD_V(Vector2());
return data.pivot_offset + data.pivot_offset_ratio * get_size();
}
/// Sizes. /// Sizes.
void Control::_update_minimum_size() { void Control::_update_minimum_size() {
@ -3984,6 +4009,7 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees); ClassDB::bind_method(D_METHOD("set_rotation_degrees", "degrees"), &Control::set_rotation_degrees);
ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Control::set_scale);
ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset); ClassDB::bind_method(D_METHOD("set_pivot_offset", "pivot_offset"), &Control::set_pivot_offset);
ClassDB::bind_method(D_METHOD("set_pivot_offset_ratio", "ratio"), &Control::set_pivot_offset_ratio);
ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin); ClassDB::bind_method(D_METHOD("get_begin"), &Control::get_begin);
ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end); ClassDB::bind_method(D_METHOD("get_end"), &Control::get_end);
ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position); ClassDB::bind_method(D_METHOD("get_position"), &Control::get_position);
@ -3992,6 +4018,8 @@ void Control::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Control::get_rotation_degrees); ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Control::get_rotation_degrees);
ClassDB::bind_method(D_METHOD("get_scale"), &Control::get_scale); ClassDB::bind_method(D_METHOD("get_scale"), &Control::get_scale);
ClassDB::bind_method(D_METHOD("get_pivot_offset"), &Control::get_pivot_offset); ClassDB::bind_method(D_METHOD("get_pivot_offset"), &Control::get_pivot_offset);
ClassDB::bind_method(D_METHOD("get_pivot_offset_ratio"), &Control::get_pivot_offset_ratio);
ClassDB::bind_method(D_METHOD("get_combined_pivot_offset"), &Control::get_combined_pivot_offset);
ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size); ClassDB::bind_method(D_METHOD("get_custom_minimum_size"), &Control::get_custom_minimum_size);
ClassDB::bind_method(D_METHOD("get_parent_area_size"), &Control::get_parent_area_size); ClassDB::bind_method(D_METHOD("get_parent_area_size"), &Control::get_parent_area_size);
ClassDB::bind_method(D_METHOD("get_global_position"), &Control::get_global_position); ClassDB::bind_method(D_METHOD("get_global_position"), &Control::get_global_position);
@ -4218,7 +4246,8 @@ void Control::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_less,or_greater,radians_as_degrees"), "set_rotation", "get_rotation");
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_rotation_degrees", "get_rotation_degrees");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "scale"), "set_scale", "get_scale");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset", PROPERTY_HINT_NONE, "suffix:px"), "set_pivot_offset", "get_pivot_offset"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset", PROPERTY_HINT_NONE, "suffix:px", PROPERTY_USAGE_EDITOR), "set_pivot_offset", "get_pivot_offset");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "pivot_offset_ratio"), "set_pivot_offset_ratio", "get_pivot_offset_ratio");
ADD_SUBGROUP("Container Sizing", "size_flags_"); ADD_SUBGROUP("Container Sizing", "size_flags_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags"); ADD_PROPERTY(PropertyInfo(Variant::INT, "size_flags_horizontal", PROPERTY_HINT_FLAGS, "Fill:1,Expand:2,Shrink Center:4,Shrink End:8"), "set_h_size_flags", "get_h_size_flags");

View file

@ -213,6 +213,7 @@ private:
real_t rotation = 0.0; real_t rotation = 0.0;
Vector2 scale = Vector2(1, 1); Vector2 scale = Vector2(1, 1);
Vector2 pivot_offset; Vector2 pivot_offset;
Vector2 pivot_offset_ratio;
Point2 pos_cache; Point2 pos_cache;
Size2 size_cache; Size2 size_cache;
@ -536,8 +537,11 @@ public:
void set_rotation_degrees(real_t p_degrees); void set_rotation_degrees(real_t p_degrees);
real_t get_rotation() const; real_t get_rotation() const;
real_t get_rotation_degrees() const; real_t get_rotation_degrees() const;
void set_pivot_offset_ratio(const Vector2 &p_ratio);
Vector2 get_pivot_offset_ratio() const;
void set_pivot_offset(const Vector2 &p_pivot); void set_pivot_offset(const Vector2 &p_pivot);
Vector2 get_pivot_offset() const; Vector2 get_pivot_offset() const;
Vector2 get_combined_pivot_offset() const;
void update_minimum_size(); void update_minimum_size();