Displays smart snapping guides when snapped

This commit is contained in:
Gilles Roudière 2019-04-05 10:18:21 +02:00 committed by Gilles Roudiere
parent 99980d856b
commit 692a78db19
3 changed files with 180 additions and 80 deletions

View file

@ -561,6 +561,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) {
// 2D // 2D
_initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07)); _initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07));
_initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8));
_initial_set("editors/2d/smart_snapping_line_color", Color(0.9, 0.1, 0.1));
_initial_set("editors/2d/bone_width", 5); _initial_set("editors/2d/bone_width", 5);
_initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9)); _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9));
_initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.9)); _initial_set("editors/2d/bone_color2", Color(0.6, 0.6, 0.6, 0.9));

View file

@ -174,15 +174,6 @@ public:
} }
}; };
void CanvasItemEditor::_snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius) {
float radius = p_radius / zoom;
float dist = Math::abs(p_value - p_target_snap);
if ((p_radius < 0 || dist < radius) && (!r_snapped || dist < Math::abs(r_current_snap - p_value))) {
r_current_snap = p_target_snap;
r_snapped = true;
}
}
bool CanvasItemEditor::_is_node_locked(const Node *p_node) { bool CanvasItemEditor::_is_node_locked(const Node *p_node) {
return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_"); return p_node->has_meta("_edit_lock_") && p_node->get_meta("_edit_lock_");
} }
@ -200,108 +191,174 @@ bool CanvasItemEditor::_is_node_movable(const Node *p_node, bool p_popup_warning
return true; return true;
} }
void CanvasItemEditor::_snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation, float p_radius) { void CanvasItemEditor::_snap_if_closer_float(
float p_value,
float &r_current_snap, SnapTarget &r_current_snap_target,
float p_target_value, SnapTarget p_snap_target,
float p_radius) {
float radius = p_radius / zoom;
float dist = Math::abs(p_value - p_target_value);
if ((p_radius < 0 || dist < radius) && (r_current_snap_target == SNAP_TARGET_NONE || dist < Math::abs(r_current_snap - p_value))) {
r_current_snap = p_target_value;
r_current_snap_target = p_snap_target;
}
}
void CanvasItemEditor::_snap_if_closer_point(
Point2 p_value,
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
Point2 p_target_value, SnapTarget p_snap_target,
real_t rotation,
float p_radius) {
Transform2D rot_trans = Transform2D(rotation, Point2()); Transform2D rot_trans = Transform2D(rotation, Point2());
p_value = rot_trans.inverse().xform(p_value); p_value = rot_trans.inverse().xform(p_value);
p_target_snap = rot_trans.inverse().xform(p_target_snap); p_target_value = rot_trans.inverse().xform(p_target_value);
r_current_snap = rot_trans.inverse().xform(r_current_snap); r_current_snap = rot_trans.inverse().xform(r_current_snap);
_snap_if_closer_float(p_value.x, p_target_snap.x, r_current_snap.x, r_snapped[0], p_radius); _snap_if_closer_float(
_snap_if_closer_float(p_value.y, p_target_snap.y, r_current_snap.y, r_snapped[1], p_radius); p_value.x,
r_current_snap.x,
r_current_snap_target[0],
p_target_value.x,
p_snap_target,
p_radius);
_snap_if_closer_float(
p_value.y,
r_current_snap.y,
r_current_snap_target[1],
p_target_value.y,
p_snap_target,
p_radius);
r_current_snap = rot_trans.xform(r_current_snap); r_current_snap = rot_trans.xform(r_current_snap);
} }
void CanvasItemEditor::_snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap) { void CanvasItemEditor::_snap_other_nodes(
const Point2 p_value,
const Transform2D p_transform_to_snap,
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
const SnapTarget p_snap_target, List<const CanvasItem *> p_exceptions,
const Node *p_current) {
const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_current); const CanvasItem *canvas_item = Object::cast_to<CanvasItem>(p_current);
if (canvas_item && (!p_to_snap || p_current != p_to_snap)) {
// Check if the element is in the exception
bool exception = false;
for (List<const CanvasItem *>::Element *E = p_exceptions.front(); E; E = E->next()) {
if (E->get() == p_current) {
exception = true;
break;
}
};
if (canvas_item && !exception) {
Transform2D ci_transform = canvas_item->get_global_transform_with_canvas(); Transform2D ci_transform = canvas_item->get_global_transform_with_canvas();
Transform2D to_snap_transform = p_to_snap ? p_to_snap->get_global_transform_with_canvas() : Transform2D(); if (fmod(ci_transform.get_rotation() - p_transform_to_snap.get_rotation(), (real_t)360.0) == 0.0) {
if (fmod(ci_transform.get_rotation() - to_snap_transform.get_rotation(), (real_t)360.0) == 0.0) {
if (canvas_item->_edit_use_rect()) { if (canvas_item->_edit_use_rect()) {
Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position()); Point2 begin = ci_transform.xform(canvas_item->_edit_get_rect().get_position());
Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size()); Point2 end = ci_transform.xform(canvas_item->_edit_get_rect().get_position() + canvas_item->_edit_get_rect().get_size());
_snap_if_closer_point(p_value, begin, r_current_snap, r_snapped, ci_transform.get_rotation());
_snap_if_closer_point(p_value, end, r_current_snap, r_snapped, ci_transform.get_rotation()); _snap_if_closer_point(p_value, r_current_snap, r_current_snap_target, begin, p_snap_target, ci_transform.get_rotation());
_snap_if_closer_point(p_value, r_current_snap, r_current_snap_target, end, p_snap_target, ci_transform.get_rotation());
} else { } else {
Point2 position = ci_transform.xform(Point2()); Point2 position = ci_transform.xform(Point2());
_snap_if_closer_point(p_value, position, r_current_snap, r_snapped, ci_transform.get_rotation()); _snap_if_closer_point(p_value, r_current_snap, r_current_snap_target, position, p_snap_target, ci_transform.get_rotation());
} }
} }
} }
for (int i = 0; i < p_current->get_child_count(); i++) { for (int i = 0; i < p_current->get_child_count(); i++) {
_snap_other_nodes(p_value, r_current_snap, r_snapped, p_current->get_child(i), p_to_snap); _snap_other_nodes(p_value, p_transform_to_snap, r_current_snap, r_current_snap_target, p_snap_target, p_exceptions, p_current->get_child(i));
} }
} }
Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const CanvasItem *p_canvas_item, unsigned int p_forced_modes) { Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsigned int p_forced_modes, const CanvasItem *p_self_canvas_item, List<CanvasItem *> p_other_nodes_exceptions) {
bool snapped[2] = { false, false };
snap_target[0] = SNAP_TARGET_NONE;
snap_target[1] = SNAP_TARGET_NONE;
bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL); bool is_snap_active = snap_active ^ Input::get_singleton()->is_key_pressed(KEY_CONTROL);
// Smart snap using the canvas position // Smart snap using the canvas position
Vector2 output = p_target; Vector2 output = p_target;
real_t rotation = 0.0; real_t rotation = 0.0;
if (p_canvas_item) { if (p_self_canvas_item) {
rotation = p_canvas_item->get_global_transform_with_canvas().get_rotation(); rotation = p_self_canvas_item->get_global_transform_with_canvas().get_rotation();
// Parent sides and center // Parent sides and center
if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) { if ((is_snap_active && snap_node_parent && (p_modes & SNAP_NODE_PARENT)) || (p_forced_modes & SNAP_NODE_PARENT)) {
if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { if (const Control *c = Object::cast_to<Control>(p_self_canvas_item)) {
Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0))); Point2 begin = p_self_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(0, 0)));
Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1))); Point2 end = p_self_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(1, 1)));
_snap_if_closer_point(p_target, begin, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, begin, SNAP_TARGET_PARENT, rotation);
_snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, (begin + end) / 2.0, SNAP_TARGET_PARENT, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, end, SNAP_TARGET_PARENT, rotation);
} else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_canvas_item->get_parent())) { } else if (const CanvasItem *parent_ci = Object::cast_to<CanvasItem>(p_self_canvas_item->get_parent())) {
if (parent_ci->_edit_use_rect()) { if (parent_ci->_edit_use_rect()) {
Point2 begin = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position()); Point2 begin = p_self_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position());
Point2 end = p_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size()); Point2 end = p_self_canvas_item->get_transform().affine_inverse().xform(parent_ci->_edit_get_rect().get_position() + parent_ci->_edit_get_rect().get_size());
_snap_if_closer_point(p_target, begin, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, begin, SNAP_TARGET_PARENT, rotation);
_snap_if_closer_point(p_target, (begin + end) / 2.0, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, (begin + end) / 2.0, SNAP_TARGET_PARENT, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, end, SNAP_TARGET_PARENT, rotation);
} else { } else {
Point2 position = p_canvas_item->get_transform().affine_inverse().xform(Point2()); Point2 position = p_self_canvas_item->get_transform().affine_inverse().xform(Point2());
_snap_if_closer_point(p_target, position, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, position, SNAP_TARGET_PARENT, rotation);
} }
} }
} }
// Self anchors // Self anchors
if ((is_snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) { if ((is_snap_active && snap_node_anchors && (p_modes & SNAP_NODE_ANCHORS)) || (p_forced_modes & SNAP_NODE_ANCHORS)) {
if (const Control *c = Object::cast_to<Control>(p_canvas_item)) { if (const Control *c = Object::cast_to<Control>(p_self_canvas_item)) {
Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP)))); Point2 begin = p_self_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_LEFT), c->get_anchor(MARGIN_TOP))));
Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM)))); Point2 end = p_self_canvas_item->get_global_transform_with_canvas().xform(_anchor_to_position(c, Point2(c->get_anchor(MARGIN_RIGHT), c->get_anchor(MARGIN_BOTTOM))));
_snap_if_closer_point(p_target, begin, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, begin, SNAP_TARGET_SELF_ANCHORS, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, end, SNAP_TARGET_SELF_ANCHORS, rotation);
} }
} }
// Self sides // Self sides
if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) { if ((is_snap_active && snap_node_sides && (p_modes & SNAP_NODE_SIDES)) || (p_forced_modes & SNAP_NODE_SIDES)) {
if (p_canvas_item->_edit_use_rect()) { if (p_self_canvas_item->_edit_use_rect()) {
Point2 begin = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position()); Point2 begin = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_position());
Point2 end = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size()); Point2 end = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_position() + p_self_canvas_item->_edit_get_rect().get_size());
_snap_if_closer_point(p_target, begin, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, begin, SNAP_TARGET_SELF, rotation);
_snap_if_closer_point(p_target, end, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, end, SNAP_TARGET_SELF, rotation);
} }
} }
// Self center // Self center
if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) { if ((is_snap_active && snap_node_center && (p_modes & SNAP_NODE_CENTER)) || (p_forced_modes & SNAP_NODE_CENTER)) {
if (p_canvas_item->_edit_use_rect()) { if (p_self_canvas_item->_edit_use_rect()) {
Point2 center = p_canvas_item->get_global_transform_with_canvas().xform(p_canvas_item->_edit_get_rect().get_position() + p_canvas_item->_edit_get_rect().get_size() / 2.0); Point2 center = p_self_canvas_item->get_global_transform_with_canvas().xform(p_self_canvas_item->_edit_get_rect().get_position() + p_self_canvas_item->_edit_get_rect().get_size() / 2.0);
_snap_if_closer_point(p_target, center, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, center, SNAP_TARGET_SELF, rotation);
} else { } else {
Point2 position = p_canvas_item->get_global_transform_with_canvas().xform(Point2()); Point2 position = p_self_canvas_item->get_global_transform_with_canvas().xform(Point2());
_snap_if_closer_point(p_target, position, output, snapped, rotation); _snap_if_closer_point(p_target, output, snap_target, position, SNAP_TARGET_SELF, rotation);
} }
} }
} }
// Other nodes sides // Other nodes sides
if ((is_snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) { if ((is_snap_active && snap_other_nodes && (p_modes & SNAP_OTHER_NODES)) || (p_forced_modes & SNAP_OTHER_NODES)) {
_snap_other_nodes(p_target, output, snapped, get_tree()->get_edited_scene_root(), p_canvas_item); Transform2D to_snap_transform = Transform2D();
List<const CanvasItem *> exceptions = List<const CanvasItem *>();
for (List<CanvasItem *>::Element *E = p_other_nodes_exceptions.front(); E; E = E->next()) {
exceptions.push_back(E->get());
}
if (p_self_canvas_item) {
exceptions.push_back(p_self_canvas_item);
to_snap_transform = p_self_canvas_item->get_global_transform_with_canvas();
}
_snap_other_nodes(
p_target, to_snap_transform,
output, snap_target,
SNAP_TARGET_OTHER_NODE,
exceptions,
get_tree()->get_edited_scene_root());
} }
if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) { if (((is_snap_active && snap_guides && (p_modes & SNAP_GUIDES)) || (p_forced_modes & SNAP_GUIDES)) && fmod(rotation, (real_t)360.0) == 0.0) {
@ -309,14 +366,14 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) { if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_vertical_guides_")) {
Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_"); Array vguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_vertical_guides_");
for (int i = 0; i < vguides.size(); i++) { for (int i = 0; i < vguides.size(); i++) {
_snap_if_closer_float(p_target.x, vguides[i], output.x, snapped[0]); _snap_if_closer_float(p_target.x, output.x, snap_target[0], vguides[i], SNAP_TARGET_GUIDE);
} }
} }
if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) { if (EditorNode::get_singleton()->get_edited_scene() && EditorNode::get_singleton()->get_edited_scene()->has_meta("_edit_horizontal_guides_")) {
Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_"); Array hguides = EditorNode::get_singleton()->get_edited_scene()->get_meta("_edit_horizontal_guides_");
for (int i = 0; i < hguides.size(); i++) { for (int i = 0; i < hguides.size(); i++) {
_snap_if_closer_float(p_target.y, hguides[i], output.y, snapped[1]); _snap_if_closer_float(p_target.y, output.y, snap_target[1], hguides[i], SNAP_TARGET_GUIDE);
} }
} }
} }
@ -335,7 +392,7 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
Point2 grid_output; Point2 grid_output;
grid_output.x = Math::stepify(p_target.x - offset.x, grid_step.x * Math::pow(2.0, grid_step_multiplier)) + offset.x; grid_output.x = Math::stepify(p_target.x - offset.x, grid_step.x * Math::pow(2.0, grid_step_multiplier)) + offset.x;
grid_output.y = Math::stepify(p_target.y - offset.y, grid_step.y * Math::pow(2.0, grid_step_multiplier)) + offset.y; grid_output.y = Math::stepify(p_target.y - offset.y, grid_step.y * Math::pow(2.0, grid_step_multiplier)) + offset.y;
_snap_if_closer_point(p_target, grid_output, output, snapped, 0.0, -1.0); _snap_if_closer_point(p_target, output, snap_target, grid_output, SNAP_TARGET_GRID, 0.0, -1.0);
} }
if (((snap_pixel && (p_modes & SNAP_PIXEL)) || (p_forced_modes & SNAP_PIXEL)) && rotation == 0.0) { if (((snap_pixel && (p_modes & SNAP_PIXEL)) || (p_forced_modes & SNAP_PIXEL)) && rotation == 0.0) {
@ -343,6 +400,8 @@ Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, const
output = output.snapped(Size2(1, 1)); output = output.snapped(Size2(1, 1));
} }
snap_transform = Transform2D(rotation, output);
return output; return output;
} }
@ -1205,10 +1264,11 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
if (drag_selection.size() > 0) { if (drag_selection.size() > 0) {
drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position()); drag_from = transform.affine_inverse().xform((b.is_valid()) ? b->get_position() : viewport->get_local_mouse_position());
Vector2 new_pos; Vector2 new_pos;
if (drag_selection.size() == 1) if (drag_selection.size() == 1) {
new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]); new_pos = snap_point(drag_from, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]);
else } else {
new_pos = snap_point(drag_from, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL); new_pos = snap_point(drag_from, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, NULL, drag_selection);
}
for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
CanvasItem *canvas_item = E->get(); CanvasItem *canvas_item = E->get();
canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos)); canvas_item->_edit_set_pivot(canvas_item->get_global_transform_with_canvas().affine_inverse().xform(new_pos));
@ -1228,7 +1288,7 @@ bool CanvasItemEditor::_gui_input_pivot(const Ref<InputEvent> &p_event) {
_restore_canvas_item_state(drag_selection); _restore_canvas_item_state(drag_selection);
Vector2 new_pos; Vector2 new_pos;
if (drag_selection.size() == 1) if (drag_selection.size() == 1)
new_pos = snap_point(drag_to, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, drag_selection[0]); new_pos = snap_point(drag_to, SNAP_NODE_SIDES | SNAP_NODE_CENTER | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, drag_selection[0]);
else else
new_pos = snap_point(drag_to, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL); new_pos = snap_point(drag_to, SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL);
for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) { for (List<CanvasItem *>::Element *E = drag_selection.front(); E; E = E->next()) {
@ -1478,7 +1538,7 @@ bool CanvasItemEditor::_gui_input_anchors(const Ref<InputEvent> &p_event) {
previous_anchor.y = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT) ? control->get_anchor(MARGIN_TOP) : control->get_anchor(MARGIN_BOTTOM); previous_anchor.y = (drag_type == DRAG_ANCHOR_TOP_LEFT || drag_type == DRAG_ANCHOR_TOP_RIGHT) ? control->get_anchor(MARGIN_TOP) : control->get_anchor(MARGIN_BOTTOM);
previous_anchor = xform.affine_inverse().xform(_anchor_to_position(control, previous_anchor)); previous_anchor = xform.affine_inverse().xform(_anchor_to_position(control, previous_anchor));
Vector2 new_anchor = xform.xform(snap_point(previous_anchor + (drag_to - drag_from), SNAP_GRID | SNAP_OTHER_NODES, control, SNAP_NODE_PARENT | SNAP_NODE_SIDES | SNAP_NODE_CENTER)); Vector2 new_anchor = xform.xform(snap_point(previous_anchor + (drag_to - drag_from), SNAP_GRID | SNAP_OTHER_NODES, SNAP_NODE_PARENT | SNAP_NODE_SIDES | SNAP_NODE_CENTER, control));
new_anchor = _position_to_anchor(control, new_anchor).snapped(Vector2(0.001, 0.001)); new_anchor = _position_to_anchor(control, new_anchor).snapped(Vector2(0.001, 0.001));
bool use_single_axis = m->get_shift(); bool use_single_axis = m->get_shift();
@ -1624,8 +1684,8 @@ bool CanvasItemEditor::_gui_input_resize(const Ref<InputEvent> &p_event) {
Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse(); Transform2D xform = canvas_item->get_global_transform_with_canvas().affine_inverse();
Point2 drag_to_snapped_begin = snap_point(xform.affine_inverse().xform(current_begin) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item); Point2 drag_to_snapped_begin = snap_point(xform.affine_inverse().xform(current_begin) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, canvas_item);
Point2 drag_to_snapped_end = snap_point(xform.affine_inverse().xform(current_end) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, canvas_item); Point2 drag_to_snapped_end = snap_point(xform.affine_inverse().xform(current_end) + (drag_to - drag_from), SNAP_NODE_ANCHORS | SNAP_NODE_PARENT | SNAP_OTHER_NODES | SNAP_GRID | SNAP_PIXEL, 0, canvas_item);
Point2 drag_begin = xform.xform(drag_to_snapped_begin); Point2 drag_begin = xform.xform(drag_to_snapped_begin);
Point2 drag_end = xform.xform(drag_to_snapped_end); Point2 drag_end = xform.xform(drag_to_snapped_end);
@ -1866,7 +1926,7 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) {
} else { } else {
previous_pos = _get_encompassing_rect_from_list(drag_selection).position; previous_pos = _get_encompassing_rect_from_list(drag_selection).position;
} }
Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES); Point2 new_pos = snap_point(previous_pos + (drag_to - drag_from), SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL | SNAP_NODE_PARENT | SNAP_NODE_ANCHORS | SNAP_OTHER_NODES, 0, NULL, drag_selection);
bool single_axis = m->get_shift(); bool single_axis = m->get_shift();
if (single_axis) { if (single_axis) {
if (ABS(new_pos.x - previous_pos.x) > ABS(new_pos.y - previous_pos.y)) { if (ABS(new_pos.x - previous_pos.x) > ABS(new_pos.y - previous_pos.y)) {
@ -2421,6 +2481,20 @@ void CanvasItemEditor::_draw_guides() {
} }
} }
void CanvasItemEditor::_draw_smart_snapping() {
Color line_color = EditorSettings::get_singleton()->get("editors/2d/smart_snapping_line_color");
if (snap_target[0] != SNAP_TARGET_NONE && snap_target[0] != SNAP_TARGET_GRID) {
viewport->draw_set_transform_matrix(viewport->get_transform() * transform * snap_transform);
viewport->draw_line(Point2(0, -1.0e+10F), Point2(0, 1.0e+10F), line_color);
viewport->draw_set_transform_matrix(viewport->get_transform());
}
if (snap_target[1] != SNAP_TARGET_NONE && snap_target[1] != SNAP_TARGET_GRID) {
viewport->draw_set_transform_matrix(viewport->get_transform() * transform * snap_transform);
viewport->draw_line(Point2(-1.0e+10F, 0), Point2(1.0e+10F, 0), line_color);
viewport->draw_set_transform_matrix(viewport->get_transform());
}
}
void CanvasItemEditor::_draw_rulers() { void CanvasItemEditor::_draw_rulers() {
Color bg_color = get_color("dark_color_2", "Editor"); Color bg_color = get_color("dark_color_2", "Editor");
Color graduation_color = get_color("font_color", "Editor").linear_interpolate(bg_color, 0.5); Color graduation_color = get_color("font_color", "Editor").linear_interpolate(bg_color, 0.5);
@ -2505,6 +2579,8 @@ void CanvasItemEditor::_draw_rulers() {
} }
} }
} }
// Draw the top left corner
viewport->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color); viewport->draw_rect(Rect2(Point2(), Size2(RULER_WIDTH, RULER_WIDTH)), graduation_color);
} }
@ -2623,7 +2699,7 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) {
if (dragged_anchor >= 0) { if (dragged_anchor >= 0) {
// Draw the 4 lines when dragged // Draw the 4 lines when dragged
bool snapped; bool anchor_snapped;
Color color_snapped = Color(0.64, 0.93, 0.67, 0.5); Color color_snapped = Color(0.64, 0.93, 0.67, 0.5);
Vector2 corners_pos[4]; Vector2 corners_pos[4];
@ -2637,14 +2713,8 @@ void CanvasItemEditor::_draw_control_helpers(Control *control) {
float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i]; float anchor_val = (i >= 2) ? ANCHOR_END - anchors_values[i] : anchors_values[i];
line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val); line_starts[i] = Vector2::linear_interpolate(corners_pos[i], corners_pos[(i + 1) % 4], anchor_val);
line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val); line_ends[i] = Vector2::linear_interpolate(corners_pos[(i + 3) % 4], corners_pos[(i + 2) % 4], anchor_val);
snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0; anchor_snapped = anchors_values[i] == 0.0 || anchors_values[i] == 0.5 || anchors_values[i] == 1.0;
int line_width; viewport->draw_line(line_starts[i], line_ends[i], anchor_snapped ? color_snapped : color_base, (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) ? 2 : 1);
if (i == dragged_anchor || (i + 3) % 4 == dragged_anchor) {
line_width = 2;
} else {
line_width = 1;
}
viewport->draw_line(line_starts[i], line_ends[i], snapped ? color_snapped : color_base, Math::round(line_width * EDSCALE));
} }
// Display the percentages next to the lines // Display the percentages next to the lines
@ -3312,6 +3382,7 @@ void CanvasItemEditor::_draw_viewport() {
_draw_rulers(); _draw_rulers();
if (show_guides) if (show_guides)
_draw_guides(); _draw_guides();
_draw_smart_snapping();
_draw_focus(); _draw_focus();
_draw_hover(); _draw_hover();
} }

View file

@ -86,6 +86,17 @@ public:
private: private:
EditorNode *editor; EditorNode *editor;
enum SnapTarget {
SNAP_TARGET_NONE = 0,
SNAP_TARGET_PARENT,
SNAP_TARGET_SELF_ANCHORS,
SNAP_TARGET_SELF,
SNAP_TARGET_OTHER_NODE,
SNAP_TARGET_GUIDE,
SNAP_TARGET_GRID,
SNAP_TARGET_PIXEL
};
enum MenuOption { enum MenuOption {
SNAP_USE, SNAP_USE,
SNAP_USE_NODE_PARENT, SNAP_USE_NODE_PARENT,
@ -440,6 +451,7 @@ private:
void _draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side); void _draw_percentage_at_position(float p_value, Point2 p_position, Margin p_side);
void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color); void _draw_straight_line(Point2 p_from, Point2 p_to, Color p_color);
void _draw_smart_snapping();
void _draw_rulers(); void _draw_rulers();
void _draw_guides(); void _draw_guides();
void _draw_focus(); void _draw_focus();
@ -475,9 +487,25 @@ private:
void _solve_IK(Node2D *leaf_node, Point2 target_position); void _solve_IK(Node2D *leaf_node, Point2 target_position);
void _snap_if_closer_float(float p_value, float p_target_snap, float &r_current_snap, bool &r_snapped, float p_radius = 10.0); SnapTarget snap_target[2];
void _snap_if_closer_point(Point2 p_value, Point2 p_target_snap, Point2 &r_current_snap, bool (&r_snapped)[2], real_t rotation = 0.0, float p_radius = 10.0); Transform2D snap_transform;
void _snap_other_nodes(Point2 p_value, Point2 &r_current_snap, bool (&r_snapped)[2], const Node *p_current, const CanvasItem *p_to_snap = NULL); void _snap_if_closer_float(
float p_value,
float &r_current_snap, SnapTarget &r_current_snap_target,
float p_target_value, SnapTarget p_snap_target,
float p_radius = 10.0);
void _snap_if_closer_point(
Point2 p_value,
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
Point2 p_target_value, SnapTarget p_snap_target,
real_t rotation = 0.0,
float p_radius = 10.0);
void _snap_other_nodes(
const Point2 p_value,
const Transform2D p_transform_to_snap,
Point2 &r_current_snap, SnapTarget (&r_current_snap_target)[2],
const SnapTarget p_snap_target, List<const CanvasItem *> p_exceptions,
const Node *p_current);
void _set_anchors_preset(Control::LayoutPreset p_preset); void _set_anchors_preset(Control::LayoutPreset p_preset);
void _set_margins_preset(Control::LayoutPreset p_preset); void _set_margins_preset(Control::LayoutPreset p_preset);
@ -558,7 +586,7 @@ public:
SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL, SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL,
}; };
Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, const CanvasItem *p_canvas_item = NULL, unsigned int p_forced_modes = 0); Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, unsigned int p_forced_modes = 0, const CanvasItem *p_self_canvas_item = NULL, List<CanvasItem *> p_other_nodes_exceptions = List<CanvasItem *>());
float snap_angle(float p_target, float p_start = 0) const; float snap_angle(float p_target, float p_start = 0) const;
Transform2D get_canvas_transform() const { return transform; } Transform2D get_canvas_transform() const { return transform; }