1/**************************************************************************/
2/* navigation_link_2d_editor_plugin.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "navigation_link_2d_editor_plugin.h"
32
33#include "canvas_item_editor_plugin.h"
34#include "editor/editor_node.h"
35#include "editor/editor_settings.h"
36#include "editor/editor_undo_redo_manager.h"
37#include "servers/navigation_server_3d.h"
38
39void NavigationLink2DEditor::_notification(int p_what) {
40 switch (p_what) {
41 case NOTIFICATION_ENTER_TREE: {
42 get_tree()->connect("node_removed", callable_mp(this, &NavigationLink2DEditor::_node_removed));
43 } break;
44
45 case NOTIFICATION_EXIT_TREE: {
46 get_tree()->disconnect("node_removed", callable_mp(this, &NavigationLink2DEditor::_node_removed));
47 } break;
48 }
49}
50
51void NavigationLink2DEditor::_node_removed(Node *p_node) {
52 if (p_node == node) {
53 node = nullptr;
54 }
55}
56
57bool NavigationLink2DEditor::forward_canvas_gui_input(const Ref<InputEvent> &p_event) {
58 if (!node || !node->is_visible_in_tree()) {
59 return false;
60 }
61
62 real_t grab_threshold = EDITOR_GET("editors/polygon_editor/point_grab_radius");
63 Transform2D xform = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
64
65 Ref<InputEventMouseButton> mb = p_event;
66 if (mb.is_valid() && mb->get_button_index() == MouseButton::LEFT) {
67 if (mb->is_pressed()) {
68 // Start position
69 if (xform.xform(node->get_start_position()).distance_to(mb->get_position()) < grab_threshold) {
70 start_grabbed = true;
71 original_start_position = node->get_start_position();
72
73 return true;
74 } else {
75 start_grabbed = false;
76 }
77
78 // End position
79 if (xform.xform(node->get_end_position()).distance_to(mb->get_position()) < grab_threshold) {
80 end_grabbed = true;
81 original_end_position = node->get_end_position();
82
83 return true;
84 } else {
85 end_grabbed = false;
86 }
87 } else {
88 EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
89 if (start_grabbed) {
90 undo_redo->create_action(TTR("Set start_position"));
91 undo_redo->add_do_method(node, "set_start_position", node->get_start_position());
92 undo_redo->add_do_method(canvas_item_editor, "update_viewport");
93 undo_redo->add_undo_method(node, "set_start_position", original_start_position);
94 undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
95 undo_redo->commit_action();
96
97 start_grabbed = false;
98
99 return true;
100 }
101
102 if (end_grabbed) {
103 undo_redo->create_action(TTR("Set end_position"));
104 undo_redo->add_do_method(node, "set_end_position", node->get_end_position());
105 undo_redo->add_do_method(canvas_item_editor, "update_viewport");
106 undo_redo->add_undo_method(node, "set_end_position", original_end_position);
107 undo_redo->add_undo_method(canvas_item_editor, "update_viewport");
108 undo_redo->commit_action();
109
110 end_grabbed = false;
111
112 return true;
113 }
114 }
115 }
116
117 Ref<InputEventMouseMotion> mm = p_event;
118 if (mm.is_valid()) {
119 Vector2 point = canvas_item_editor->snap_point(canvas_item_editor->get_canvas_transform().affine_inverse().xform(mm->get_position()));
120 point = node->get_global_transform().affine_inverse().xform(point);
121
122 if (start_grabbed) {
123 node->set_start_position(point);
124 canvas_item_editor->update_viewport();
125
126 return true;
127 }
128
129 if (end_grabbed) {
130 node->set_end_position(point);
131 canvas_item_editor->update_viewport();
132
133 return true;
134 }
135 }
136
137 return false;
138}
139
140void NavigationLink2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) {
141 if (!node || !node->is_visible_in_tree()) {
142 return;
143 }
144
145 Transform2D gt = canvas_item_editor->get_canvas_transform() * node->get_global_transform();
146 Vector2 global_start_position = gt.xform(node->get_start_position());
147 Vector2 global_end_position = gt.xform(node->get_end_position());
148
149 // Only drawing the handles here, since the debug rendering will fill in the rest.
150 const Ref<Texture2D> handle = get_editor_theme_icon(SNAME("EditorHandle"));
151 p_overlay->draw_texture(handle, global_start_position - handle->get_size() / 2);
152 p_overlay->draw_texture(handle, global_end_position - handle->get_size() / 2);
153}
154
155void NavigationLink2DEditor::edit(NavigationLink2D *p_node) {
156 if (!canvas_item_editor) {
157 canvas_item_editor = CanvasItemEditor::get_singleton();
158 }
159
160 if (p_node) {
161 node = p_node;
162 } else {
163 node = nullptr;
164 }
165
166 canvas_item_editor->update_viewport();
167}
168
169///////////////////////
170
171void NavigationLink2DEditorPlugin::edit(Object *p_object) {
172 editor->edit(Object::cast_to<NavigationLink2D>(p_object));
173}
174
175bool NavigationLink2DEditorPlugin::handles(Object *p_object) const {
176 return Object::cast_to<NavigationLink2D>(p_object) != nullptr;
177}
178
179void NavigationLink2DEditorPlugin::make_visible(bool p_visible) {
180 if (!p_visible) {
181 edit(nullptr);
182 }
183}
184
185NavigationLink2DEditorPlugin::NavigationLink2DEditorPlugin() {
186 editor = memnew(NavigationLink2DEditor);
187 EditorNode::get_singleton()->get_gui_base()->add_child(editor);
188}
189