1 | /**************************************************************************/ |
2 | /* graph_edit.h */ |
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 | #ifndef GRAPH_EDIT_H |
32 | #define GRAPH_EDIT_H |
33 | |
34 | #include "scene/gui/box_container.h" |
35 | #include "scene/gui/button.h" |
36 | #include "scene/gui/graph_node.h" |
37 | #include "scene/gui/label.h" |
38 | #include "scene/gui/scroll_bar.h" |
39 | #include "scene/gui/spin_box.h" |
40 | |
41 | class GraphEdit; |
42 | class GraphEditArranger; |
43 | class ViewPanner; |
44 | |
45 | class GraphEditFilter : public Control { |
46 | GDCLASS(GraphEditFilter, Control); |
47 | |
48 | friend class GraphEdit; |
49 | friend class GraphEditMinimap; |
50 | |
51 | GraphEdit *ge = nullptr; |
52 | |
53 | virtual bool has_point(const Point2 &p_point) const override; |
54 | |
55 | public: |
56 | GraphEditFilter(GraphEdit *p_edit); |
57 | }; |
58 | |
59 | class GraphEditMinimap : public Control { |
60 | GDCLASS(GraphEditMinimap, Control); |
61 | |
62 | friend class GraphEdit; |
63 | friend class GraphEditFilter; |
64 | |
65 | GraphEdit *ge = nullptr; |
66 | |
67 | Vector2 minimap_padding; |
68 | Vector2 minimap_offset; |
69 | Vector2 graph_proportions = Vector2(1, 1); |
70 | Vector2 graph_padding = Vector2(0, 0); |
71 | Vector2 camera_position = Vector2(100, 50); |
72 | Vector2 camera_size = Vector2(200, 200); |
73 | |
74 | bool is_pressing = false; |
75 | bool is_resizing = false; |
76 | |
77 | struct ThemeCache { |
78 | Ref<StyleBox> panel; |
79 | Ref<StyleBox> node_style; |
80 | Ref<StyleBox> camera_style; |
81 | |
82 | Ref<Texture2D> resizer; |
83 | Color resizer_color; |
84 | } theme_cache; |
85 | |
86 | Vector2 _get_render_size(); |
87 | Vector2 _get_graph_offset(); |
88 | Vector2 _get_graph_size(); |
89 | |
90 | Vector2 _convert_from_graph_position(const Vector2 &p_position); |
91 | Vector2 _convert_to_graph_position(const Vector2 &p_position); |
92 | |
93 | virtual void gui_input(const Ref<InputEvent> &p_ev) override; |
94 | |
95 | void _adjust_graph_scroll(const Vector2 &p_offset); |
96 | |
97 | protected: |
98 | static void _bind_methods(); |
99 | |
100 | public: |
101 | virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; |
102 | |
103 | void update_minimap(); |
104 | Rect2 get_camera_rect(); |
105 | |
106 | GraphEditMinimap(GraphEdit *p_edit); |
107 | }; |
108 | |
109 | class GraphEdit : public Control { |
110 | GDCLASS(GraphEdit, Control); |
111 | |
112 | public: |
113 | struct Connection { |
114 | StringName from_node; |
115 | StringName to_node; |
116 | int from_port = 0; |
117 | int to_port = 0; |
118 | float activity = 0.0; |
119 | }; |
120 | |
121 | // Should be in sync with ControlScheme in ViewPanner. |
122 | enum PanningScheme { |
123 | SCROLL_ZOOMS, |
124 | SCROLL_PANS, |
125 | }; |
126 | |
127 | private: |
128 | struct ConnectionType { |
129 | union { |
130 | struct { |
131 | uint32_t type_a; |
132 | uint32_t type_b; |
133 | }; |
134 | uint64_t key = 0; |
135 | }; |
136 | |
137 | static uint32_t hash(const ConnectionType &p_conn) { |
138 | return hash_one_uint64(p_conn.key); |
139 | } |
140 | bool operator==(const ConnectionType &p_type) const { |
141 | return key == p_type.key; |
142 | } |
143 | |
144 | ConnectionType(uint32_t a = 0, uint32_t b = 0) { |
145 | type_a = a; |
146 | type_b = b; |
147 | } |
148 | }; |
149 | |
150 | Label *zoom_label = nullptr; |
151 | Button *zoom_minus_button = nullptr; |
152 | Button *zoom_reset_button = nullptr; |
153 | Button *zoom_plus_button = nullptr; |
154 | |
155 | Button *toggle_snapping_button = nullptr; |
156 | SpinBox *snapping_distance_spinbox = nullptr; |
157 | Button *show_grid_button = nullptr; |
158 | Button *minimap_button = nullptr; |
159 | |
160 | Button *layout_button = nullptr; |
161 | |
162 | HScrollBar *h_scrollbar = nullptr; |
163 | VScrollBar *v_scrollbar = nullptr; |
164 | |
165 | Ref<ViewPanner> panner; |
166 | bool warped_panning = true; |
167 | |
168 | bool arrange_nodes_button_hidden = false; |
169 | |
170 | bool snapping_enabled = true; |
171 | int snapping_distance = 20; |
172 | bool show_grid = true; |
173 | |
174 | bool connecting = false; |
175 | String connecting_from; |
176 | bool connecting_out = false; |
177 | int connecting_index = 0; |
178 | int connecting_type = 0; |
179 | Color connecting_color; |
180 | bool connecting_target = false; |
181 | Vector2 connecting_to; |
182 | StringName connecting_target_to; |
183 | int connecting_target_index = 0; |
184 | |
185 | bool just_disconnected = false; |
186 | bool connecting_valid = false; |
187 | |
188 | Vector2 click_pos; |
189 | |
190 | PanningScheme panning_scheme = SCROLL_ZOOMS; |
191 | bool dragging = false; |
192 | bool just_selected = false; |
193 | bool moving_selection = false; |
194 | Vector2 drag_accum; |
195 | |
196 | float zoom = 1.0; |
197 | float zoom_step = 1.2; |
198 | // Proper values set in constructor. |
199 | float zoom_min = 0.0; |
200 | float zoom_max = 0.0; |
201 | |
202 | bool box_selecting = false; |
203 | bool box_selection_mode_additive = false; |
204 | Point2 box_selecting_from; |
205 | Point2 box_selecting_to; |
206 | Rect2 box_selecting_rect; |
207 | List<GraphElement *> prev_selected; |
208 | |
209 | bool setting_scroll_offset = false; |
210 | bool right_disconnects = false; |
211 | bool updating = false; |
212 | bool awaiting_scroll_offset_update = false; |
213 | List<Connection> connections; |
214 | |
215 | float lines_thickness = 2.0f; |
216 | float lines_curvature = 0.5f; |
217 | bool lines_antialiased = true; |
218 | |
219 | HBoxContainer * = nullptr; |
220 | Control *connections_layer = nullptr; |
221 | GraphEditFilter *top_layer = nullptr; |
222 | GraphEditMinimap *minimap = nullptr; |
223 | |
224 | Ref<GraphEditArranger> arranger; |
225 | |
226 | HashSet<ConnectionType, ConnectionType> valid_connection_types; |
227 | HashSet<int> valid_left_disconnect_types; |
228 | HashSet<int> valid_right_disconnect_types; |
229 | |
230 | struct ThemeCache { |
231 | float base_scale = 1.0; |
232 | |
233 | Ref<StyleBox> panel; |
234 | Color grid_major; |
235 | Color grid_minor; |
236 | |
237 | Color activity_color; |
238 | Color selection_fill; |
239 | Color selection_stroke; |
240 | |
241 | Ref<Texture2D> zoom_in; |
242 | Ref<Texture2D> zoom_out; |
243 | Ref<Texture2D> zoom_reset; |
244 | |
245 | Ref<Texture2D> snapping_toggle; |
246 | Ref<Texture2D> grid_toggle; |
247 | Ref<Texture2D> minimap_toggle; |
248 | Ref<Texture2D> layout; |
249 | |
250 | float port_hotzone_inner_extent = 0.0; |
251 | float port_hotzone_outer_extent = 0.0; |
252 | } theme_cache; |
253 | |
254 | void _pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event); |
255 | void _zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event); |
256 | |
257 | void _zoom_minus(); |
258 | void _zoom_reset(); |
259 | void _zoom_plus(); |
260 | void _update_zoom_label(); |
261 | |
262 | void _draw_connection_line(CanvasItem *p_where, const Vector2 &p_from, const Vector2 &p_to, const Color &p_color, const Color &p_to_color, float p_width, float p_zoom); |
263 | |
264 | void _graph_element_selected(Node *p_node); |
265 | void _graph_element_deselected(Node *p_node); |
266 | void _graph_element_moved_to_front(Node *p_node); |
267 | void _graph_element_resized(Vector2 p_new_minsize, Node *p_node); |
268 | void _graph_element_moved(Node *p_node); |
269 | void _graph_node_slot_updated(int p_index, Node *p_node); |
270 | |
271 | void _update_scroll(); |
272 | void _update_scroll_offset(); |
273 | void _scroll_moved(double); |
274 | virtual void gui_input(const Ref<InputEvent> &p_ev) override; |
275 | void _top_layer_input(const Ref<InputEvent> &p_ev); |
276 | |
277 | bool is_in_port_hotzone(const Vector2 &p_pos, const Vector2 &p_mouse_pos, const Vector2i &p_port_size, bool p_left); |
278 | |
279 | void _top_layer_draw(); |
280 | void _connections_layer_draw(); |
281 | void _minimap_draw(); |
282 | |
283 | TypedArray<Dictionary> _get_connection_list() const; |
284 | |
285 | friend class GraphEditFilter; |
286 | bool _filter_input(const Point2 &p_point); |
287 | void _snapping_toggled(); |
288 | void _snapping_distance_changed(double); |
289 | void _show_grid_toggled(); |
290 | |
291 | friend class GraphEditMinimap; |
292 | void _minimap_toggled(); |
293 | |
294 | bool _check_clickable_control(Control *p_control, const Vector2 &r_mouse_pos, const Vector2 &p_offset); |
295 | |
296 | protected: |
297 | virtual void _update_theme_item_cache() override; |
298 | |
299 | virtual void add_child_notify(Node *p_child) override; |
300 | virtual void remove_child_notify(Node *p_child) override; |
301 | |
302 | void _notification(int p_what); |
303 | static void _bind_methods(); |
304 | |
305 | virtual bool is_in_input_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); |
306 | virtual bool is_in_output_hotzone(GraphNode *p_graph_node, int p_port_idx, const Vector2 &p_mouse_pos, const Vector2i &p_port_size); |
307 | |
308 | GDVIRTUAL2RC(Vector<Vector2>, _get_connection_line, Vector2, Vector2) |
309 | GDVIRTUAL3R(bool, _is_in_input_hotzone, Object *, int, Vector2) |
310 | GDVIRTUAL3R(bool, _is_in_output_hotzone, Object *, int, Vector2) |
311 | GDVIRTUAL4R(bool, _is_node_hover_valid, StringName, int, StringName, int); |
312 | |
313 | public: |
314 | virtual CursorShape get_cursor_shape(const Point2 &p_pos = Point2i()) const override; |
315 | |
316 | PackedStringArray get_configuration_warnings() const override; |
317 | |
318 | Error connect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); |
319 | bool is_node_connected(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); |
320 | void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); |
321 | void clear_connections(); |
322 | void force_connection_drag_end(); |
323 | |
324 | virtual PackedVector2Array get_connection_line(const Vector2 &p_from, const Vector2 &p_to); |
325 | virtual bool is_node_hover_valid(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port); |
326 | |
327 | void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity); |
328 | |
329 | void add_valid_connection_type(int p_type, int p_with_type); |
330 | void remove_valid_connection_type(int p_type, int p_with_type); |
331 | bool is_valid_connection_type(int p_type, int p_with_type) const; |
332 | |
333 | void set_panning_scheme(PanningScheme p_scheme); |
334 | PanningScheme get_panning_scheme() const; |
335 | |
336 | void set_zoom(float p_zoom); |
337 | void set_zoom_custom(float p_zoom, const Vector2 &p_center); |
338 | float get_zoom() const; |
339 | |
340 | void set_zoom_min(float p_zoom_min); |
341 | float get_zoom_min() const; |
342 | |
343 | void set_zoom_max(float p_zoom_max); |
344 | float get_zoom_max() const; |
345 | |
346 | void set_zoom_step(float p_zoom_step); |
347 | float get_zoom_step() const; |
348 | |
349 | void set_show_zoom_label(bool p_enable); |
350 | bool is_showing_zoom_label() const; |
351 | |
352 | void set_minimap_size(Vector2 p_size); |
353 | Vector2 get_minimap_size() const; |
354 | void set_minimap_opacity(float p_opacity); |
355 | float get_minimap_opacity() const; |
356 | |
357 | void set_minimap_enabled(bool p_enable); |
358 | bool is_minimap_enabled() const; |
359 | |
360 | void set_arrange_nodes_button_hidden(bool p_enable); |
361 | bool is_arrange_nodes_button_hidden() const; |
362 | |
363 | GraphEditFilter *get_top_layer() const { return top_layer; } |
364 | GraphEditMinimap *get_minimap() const { return minimap; } |
365 | |
366 | void get_connection_list(List<Connection> *r_connections) const; |
367 | |
368 | void set_right_disconnects(bool p_enable); |
369 | bool is_right_disconnects_enabled() const; |
370 | |
371 | void add_valid_right_disconnect_type(int p_type); |
372 | void remove_valid_right_disconnect_type(int p_type); |
373 | |
374 | void add_valid_left_disconnect_type(int p_type); |
375 | void remove_valid_left_disconnect_type(int p_type); |
376 | |
377 | void set_scroll_offset(const Vector2 &p_ofs); |
378 | Vector2 get_scroll_offset() const; |
379 | |
380 | void set_selected(Node *p_child); |
381 | |
382 | void set_snapping_enabled(bool p_enable); |
383 | bool is_snapping_enabled() const; |
384 | |
385 | void set_snapping_distance(int p_snapping_distance); |
386 | int get_snapping_distance() const; |
387 | |
388 | void set_show_grid(bool p_enable); |
389 | bool is_showing_grid() const; |
390 | |
391 | void set_connection_lines_curvature(float p_curvature); |
392 | float get_connection_lines_curvature() const; |
393 | |
394 | void set_connection_lines_thickness(float p_thickness); |
395 | float get_connection_lines_thickness() const; |
396 | |
397 | void set_connection_lines_antialiased(bool p_antialiased); |
398 | bool is_connection_lines_antialiased() const; |
399 | |
400 | HBoxContainer *(); |
401 | Ref<ViewPanner> get_panner(); |
402 | void set_warped_panning(bool p_warped); |
403 | |
404 | void arrange_nodes(); |
405 | |
406 | GraphEdit(); |
407 | }; |
408 | |
409 | VARIANT_ENUM_CAST(GraphEdit::PanningScheme); |
410 | |
411 | #endif // GRAPH_EDIT_H |
412 | |