Blender V4.5
node_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <array>
10#include <cstdlib>
11#include <fmt/format.h>
12
13#include "DNA_node_types.h"
15
16#include "BLI_lasso_2d.hh"
17#include "BLI_listbase.h"
18#include "BLI_math_vector.h"
19#include "BLI_rect.h"
20#include "BLI_string.h"
21#include "BLI_string_utf8.h"
22#include "BLI_utildefines.h"
23
24#include "BKE_context.hh"
25#include "BKE_main.hh"
27#include "BKE_node.hh"
29#include "BKE_node_runtime.hh"
31#include "BKE_viewer_path.hh"
32#include "BKE_workspace.hh"
33
34#include "ED_node.hh" /* own include */
35#include "ED_screen.hh"
36#include "ED_select_utils.hh"
37#include "ED_view3d.hh"
38#include "ED_viewer_path.hh"
39
40#include "RNA_access.hh"
41#include "RNA_define.hh"
42
43#include "WM_api.hh"
44#include "WM_types.hh"
45
46#include "UI_interface.hh"
47#include "UI_resources.hh"
48#include "UI_string_search.hh"
49#include "UI_view2d.hh"
50
51#include "DEG_depsgraph.hh"
52
53#include "node_intern.hh" /* own include */
54
56
57static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event);
58
69 const Scene *scene,
70 const Object *ob)
71{
72 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
73 if (win->scene != scene) {
74 continue;
75 }
76 const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
77 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
78 if (area->spacetype == SPACE_VIEW3D) {
79 const View3D *v3d = (const View3D *)area->spacedata.first;
80
81 if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) {
82 return true;
83 }
84 }
85 }
86 }
87 return false;
88}
89
90/* -------------------------------------------------------------------- */
93
94rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node)
95{
96 const float margin = 4.0f * NODE_RESIZE_MARGIN * math::max(snode.runtime->aspect, 1.0f);
97 rctf frame_inside = {
98 node.runtime->draw_bounds.xmin,
99 node.runtime->draw_bounds.xmax,
100 node.runtime->draw_bounds.ymin,
101 node.runtime->draw_bounds.ymax,
102 };
103
104 BLI_rctf_pad(&frame_inside, -margin, -margin);
105
106 return frame_inside;
107}
108
110{
111 return is_event_over_node_or_socket(C, event);
112}
113
115 const bNode &node,
116 const float2 &mouse)
117{
118 /* Frame nodes are selectable by their borders (including their whole rect - as for other nodes -
119 * would prevent e.g. box selection of nodes inside that frame). */
120 const rctf frame_inside = node_frame_rect_inside(snode, node);
121 if (BLI_rctf_isect_pt(&node.runtime->draw_bounds, mouse.x, mouse.y) &&
122 !BLI_rctf_isect_pt(&frame_inside, mouse.x, mouse.y))
123 {
124 return true;
125 }
126
127 return false;
128}
129
130static bNode *node_under_mouse_select(const SpaceNode &snode, const float2 mouse)
131{
133 switch (node->type_legacy) {
134 case NODE_FRAME: {
135 if (node_frame_select_isect_mouse(snode, *node, mouse)) {
136 return node;
137 }
138 break;
139 }
140 default: {
141 if (BLI_rctf_isect_pt(&node->runtime->draw_bounds, int(mouse.x), int(mouse.y))) {
142 return node;
143 }
144 break;
145 }
146 }
147 }
148 return nullptr;
149}
150
151static bool is_position_over_node_or_socket(SpaceNode &snode, ARegion &region, const float2 &mouse)
152{
153 if (node_under_mouse_select(snode, mouse)) {
154 return true;
155 }
156 if (node_find_indicated_socket(snode, region, mouse, SOCK_IN | SOCK_OUT)) {
157 return true;
158 }
159 return false;
160}
161
162static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event)
163{
164 SpaceNode &snode = *CTX_wm_space_node(&C);
165 ARegion &region = *CTX_wm_region(&C);
166
167 int2 mval;
168 WM_event_drag_start_mval(&event, &region, mval);
169
170 float2 mouse;
171 UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &mouse.x, &mouse.y);
172 return is_position_over_node_or_socket(snode, region, mouse);
173}
174
176{
177 sock.flag |= SELECT;
178
179 /* select node too */
180 if (node) {
181 node->flag |= SELECT;
182 }
183}
184
185void node_socket_deselect(bNode *node, bNodeSocket &sock, const bool deselect_node)
186{
187 sock.flag &= ~SELECT;
188
189 if (node && deselect_node) {
190 bool sel = false;
191
192 /* if no selected sockets remain, also deselect the node */
194 if (input->flag & SELECT) {
195 sel = true;
196 break;
197 }
198 }
200 if (output->flag & SELECT) {
201 sel = true;
202 break;
203 }
204 }
205
206 if (!sel) {
207 node->flag &= ~SELECT;
208 }
209 }
210}
211
212static void node_socket_toggle(bNode *node, bNodeSocket &sock, bool deselect_node)
213{
214 if (sock.flag & SELECT) {
215 node_socket_deselect(node, sock, deselect_node);
216 }
217 else {
218 node_socket_select(node, sock);
219 }
220}
221
223{
224 bool changed = false;
225 for (bNode *node : node_tree.all_nodes()) {
226 changed |= bke::node_set_selected(*node, false);
227 }
228 return changed;
229}
230
231void node_deselect_all_input_sockets(bNodeTree &node_tree, const bool deselect_nodes)
232{
233 /* XXX not calling node_socket_deselect here each time, because this does iteration
234 * over all node sockets internally to check if the node stays selected.
235 * We can do that more efficiently here.
236 */
237
238 for (bNode *node : node_tree.all_nodes()) {
239 bool sel = false;
240
241 LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
242 socket->flag &= ~SELECT;
243 }
244
245 /* If no selected sockets remain, also deselect the node. */
246 if (deselect_nodes) {
247 LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
248 if (socket->flag & SELECT) {
249 sel = true;
250 break;
251 }
252 }
253
254 if (!sel) {
255 node->flag &= ~SELECT;
256 }
257 }
258 }
259}
260
261void node_deselect_all_output_sockets(bNodeTree &node_tree, const bool deselect_nodes)
262{
263 /* XXX not calling node_socket_deselect here each time, because this does iteration
264 * over all node sockets internally to check if the node stays selected.
265 * We can do that more efficiently here.
266 */
267
268 for (bNode *node : node_tree.all_nodes()) {
269 bool sel = false;
270
271 LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
272 socket->flag &= ~SELECT;
273 }
274
275 /* if no selected sockets remain, also deselect the node */
276 if (deselect_nodes) {
277 LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
278 if (socket->flag & SELECT) {
279 sel = true;
280 break;
281 }
282 }
283
284 if (!sel) {
285 node->flag &= ~SELECT;
286 }
287 }
288 }
289}
290
292{
293 node_tree.ensure_topology_cache();
294 for (const bke::bNodeZoneType *zone_type : bke::all_zone_types()) {
295 for (bNode *input_node : node_tree.nodes_by_type(zone_type->input_idname)) {
296 if (bNode *output_node = zone_type->get_corresponding_output(node_tree, *input_node)) {
297 if (input_node->flag & NODE_SELECT) {
298 output_node->flag |= NODE_SELECT;
299 }
300 if (output_node->flag & NODE_SELECT) {
301 input_node->flag |= NODE_SELECT;
302 }
303 }
304 }
305 }
306}
307
309{
310 VectorSet<bNode *> selected_nodes;
311 for (bNode *node : node_tree.all_nodes()) {
312 if (node->flag & NODE_SELECT) {
313 selected_nodes.add(node);
314 }
315 }
316 return selected_nodes;
317}
318
320
321/* -------------------------------------------------------------------- */
324
325/* Return true if we need redraw, otherwise false. */
326
327static bool node_select_grouped_type(bNodeTree &node_tree, bNode &node_act)
328{
329 bool changed = false;
330 for (bNode *node : node_tree.all_nodes()) {
331 if ((node->flag & SELECT) == 0) {
332 if (node->type_legacy == node_act.type_legacy) {
333 bke::node_set_selected(*node, true);
334 changed = true;
335 }
336 }
337 }
338 return changed;
339}
340
341static bool node_select_grouped_color(bNodeTree &node_tree, bNode &node_act)
342{
343 bool changed = false;
344 for (bNode *node : node_tree.all_nodes()) {
345 if ((node->flag & SELECT) == 0) {
346 if (compare_v3v3(node->color, node_act.color, 0.005f)) {
347 bke::node_set_selected(*node, true);
348 changed = true;
349 }
350 }
351 }
352 return changed;
353}
354
355static bool node_select_grouped_name(bNodeTree &node_tree, bNode &node_act, const bool from_right)
356{
357 bool changed = false;
358 const uint delims[] = {'.', '-', '_', '\0'};
359 size_t pref_len_act, pref_len_curr;
360 const char *sep, *suf_act, *suf_curr;
361
362 pref_len_act = BLI_str_partition_ex_utf8(
363 node_act.name, nullptr, delims, &sep, &suf_act, from_right);
364
365 /* NOTE: in case we are searching for suffix, and found none, use whole name as suffix. */
366 if (from_right && !(sep && suf_act)) {
367 pref_len_act = 0;
368 suf_act = node_act.name;
369 }
370
371 for (bNode *node : node_tree.all_nodes()) {
372 if (node->flag & SELECT) {
373 continue;
374 }
375 pref_len_curr = BLI_str_partition_ex_utf8(
376 node->name, nullptr, delims, &sep, &suf_curr, from_right);
377
378 /* Same as with active node name! */
379 if (from_right && !(sep && suf_curr)) {
380 pref_len_curr = 0;
381 suf_curr = node->name;
382 }
383
384 if ((from_right && STREQ(suf_act, suf_curr)) ||
385 (!from_right && (pref_len_act == pref_len_curr) &&
386 STREQLEN(node_act.name, node->name, pref_len_act)))
387 {
388 bke::node_set_selected(*node, true);
389 changed = true;
390 }
391 }
392
393 return changed;
394}
395
396enum {
401};
402
404{
405 SpaceNode &snode = *CTX_wm_space_node(C);
406 bNodeTree &node_tree = *snode.edittree;
407 bNode *node_act = bke::node_get_active(*snode.edittree);
408
409 if (node_act == nullptr) {
410 return OPERATOR_CANCELLED;
411 }
412
413 bool changed = false;
414 const bool extend = RNA_boolean_get(op->ptr, "extend");
415 const int type = RNA_enum_get(op->ptr, "type");
416
417 if (!extend) {
418 node_deselect_all(node_tree);
419 }
420 bke::node_set_selected(*node_act, true);
421
422 switch (type) {
424 changed = node_select_grouped_type(node_tree, *node_act);
425 break;
427 changed = node_select_grouped_color(node_tree, *node_act);
428 break;
430 changed = node_select_grouped_name(node_tree, *node_act, false);
431 break;
433 changed = node_select_grouped_name(node_tree, *node_act, true);
434 break;
435 default:
436 break;
437 }
438
439 if (changed) {
440 tree_draw_order_update(node_tree);
442 return OPERATOR_FINISHED;
443 }
444
445 return OPERATOR_CANCELLED;
446}
447
449{
450 PropertyRNA *prop;
451 static const EnumPropertyItem prop_select_grouped_types[] = {
452 {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""},
453 {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""},
454 {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""},
455 {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""},
456 {0, nullptr, 0, nullptr, nullptr},
457 };
458
459 /* identifiers */
460 ot->name = "Select Grouped";
461 ot->description = "Select nodes with similar properties";
462 ot->idname = "NODE_OT_select_grouped";
463
464 /* API callbacks. */
465 ot->invoke = WM_menu_invoke;
468
469 /* flags */
471
472 /* properties */
473 prop = RNA_def_boolean(ot->srna,
474 "extend",
475 false,
476 "Extend",
477 "Extend selection instead of deselecting everything first");
479 ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
480}
481
483
484/* -------------------------------------------------------------------- */
487
489{
490 Main *bmain = CTX_data_main(&C);
491 SpaceNode &snode = *CTX_wm_space_node(&C);
492 bNodeTree &node_tree = *snode.edittree;
493 const Object *ob = CTX_data_active_object(&C);
494 const Scene *scene = CTX_data_scene(&C);
496 bool active_texture_changed = false;
497
498 for (bNode *node_iter : node_tree.all_nodes()) {
499 if (node_iter != &node) {
500 bke::node_set_selected(*node_iter, false);
501 }
502 }
503 bke::node_set_selected(node, true);
504
505 ED_node_set_active(bmain, &snode, &node_tree, &node, &active_texture_changed);
507
508 tree_draw_order_update(node_tree);
509 if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) {
511 }
512
514}
515
517 wmOperator *op,
518 const int2 mval,
520{
521 Main &bmain = *CTX_data_main(C);
522 SpaceNode &snode = *CTX_wm_space_node(C);
523 bNodeTree &node_tree = *snode.edittree;
524 ARegion &region = *CTX_wm_region(C);
525 const Object *ob = CTX_data_active_object(C);
526 const Scene *scene = CTX_data_scene(C);
528 bNode *node = nullptr;
529 bNodeSocket *sock = nullptr;
530
531 /* Always do socket_select when extending selection. */
532 const bool socket_select = (params.sel_op == SEL_OP_XOR) ||
533 RNA_boolean_get(op->ptr, "socket_select");
534 bool changed = false;
535 bool found = false;
536 bool node_was_selected = false;
537
538 /* Get mouse coordinates in view2d space. */
539 float2 cursor;
540 UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &cursor.x, &cursor.y);
541
542 /* First do socket selection, these generally overlap with nodes. */
543 if (socket_select) {
544 /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */
545 const bool extend = (params.sel_op == SEL_OP_XOR);
546 sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN);
547 if (sock) {
548 node = &sock->owner_node();
549 found = true;
550 node_was_selected = node->flag & SELECT;
551
552 /* NOTE: SOCK_IN does not take into account the extend case...
553 * This feature is not really used anyway currently? */
554 node_socket_toggle(node, *sock, true);
555 changed = true;
556 }
557 if (!changed) {
558 sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT);
559 if (sock) {
560 node = &sock->owner_node();
561 found = true;
562 node_was_selected = node->flag & SELECT;
563
564 if (sock->flag & SELECT) {
565 if (extend) {
566 node_socket_deselect(node, *sock, true);
567 changed = true;
568 }
569 }
570 else {
571 /* Only allow one selected output per node, for sensible linking.
572 * Allow selecting outputs from different nodes though, if extend is true. */
573 for (bNodeSocket *tsock : node->output_sockets()) {
574 if (tsock == sock) {
575 continue;
576 }
577 node_socket_deselect(node, *tsock, true);
578 changed = true;
579 }
580 if (!extend) {
581 for (bNode *tnode : node_tree.all_nodes()) {
582 if (tnode == node) {
583 continue;
584 }
585 for (bNodeSocket *tsock : tnode->output_sockets()) {
586 node_socket_deselect(tnode, *tsock, true);
587 changed = true;
588 }
589 }
590 }
591 node_socket_select(node, *sock);
592 changed = true;
593 }
594 }
595 }
596 }
597
598 if (!sock) {
599
600 /* Find the closest visible node. */
601 node = node_under_mouse_select(snode, cursor);
602 found = (node != nullptr);
603 node_was_selected = node && (node->flag & SELECT);
604
605 if (params.sel_op == SEL_OP_SET) {
606 if ((found && params.select_passthrough) && (node->flag & SELECT)) {
607 found = false;
608 }
609 else if (found || params.deselect_all) {
610 /* Deselect everything. */
611 changed = node_deselect_all(node_tree);
612 }
613 }
614
615 if (found) {
616 switch (params.sel_op) {
617 case SEL_OP_ADD:
618 bke::node_set_selected(*node, true);
619 break;
620 case SEL_OP_SUB:
621 bke::node_set_selected(*node, false);
622 break;
623 case SEL_OP_XOR: {
624 /* Check active so clicking on an inactive node activates it. */
625 bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE);
626 bke::node_set_selected(*node, !is_selected);
627 break;
628 }
629 case SEL_OP_SET:
630 bke::node_set_selected(*node, true);
631 break;
632 case SEL_OP_AND:
633 /* Doesn't make sense for picking. */
635 break;
636 }
637
638 changed = true;
639 }
640 }
641
642 if (RNA_boolean_get(op->ptr, "clear_viewer")) {
643 if (node == nullptr) {
644 /* Disable existing active viewer. */
645 WorkSpace *workspace = CTX_wm_workspace(C);
646 if (const std::optional<viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
648 {
649 /* The object needs to be reevaluated, because the viewer path is changed which means that
650 * the object may generate different viewer geometry as a side effect. */
653 depsgraph, &parsed_path->object->id, ID_RECALC_GEOMETRY);
654 }
657 }
658 }
659
660 if (!(changed || found)) {
661 return false;
662 }
663
664 bool active_texture_changed = false;
665 bool viewer_node_changed = false;
666 if ((node != nullptr) && (node_was_selected == false || params.select_passthrough == false)) {
667 viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 &&
669 ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
670 }
671 else if (node != nullptr && node->type_legacy == GEO_NODE_VIEWER) {
672 viewer_path::activate_geometry_node(bmain, snode, *node);
673 }
675 tree_draw_order_update(node_tree);
676 if ((active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) ||
677 viewer_node_changed)
678 {
680 }
681
684
685 BKE_main_ensure_invariants(bmain, node_tree.id);
686
687 return true;
688}
689
691{
692 /* Get settings from RNA properties for operator. */
693 int2 mval;
694 RNA_int_get_array(op->ptr, "location", mval);
695
697
698 /* Perform the selection. */
699 const bool changed = node_mouse_select(C, op, mval, params);
700
701 if (changed) {
703 }
704 /* Nothing selected, just pass through. */
706}
707
709{
710 RNA_int_set_array(op->ptr, "location", event->mval);
711
712 const wmOperatorStatus retval = node_select_exec(C, op);
713
715}
716
718{
719 PropertyRNA *prop;
720
721 /* identifiers */
722 ot->name = "Select";
723 ot->idname = "NODE_OT_select";
724 ot->description = "Select the node under the cursor";
725
726 /* API callbacks. */
727 ot->exec = node_select_exec;
728 ot->invoke = node_select_invoke;
730 ot->get_name = ED_select_pick_get_name;
731
732 /* flags */
734
735 /* properties */
737
738 prop = RNA_def_int_vector(ot->srna,
739 "location",
740 2,
741 nullptr,
742 INT_MIN,
743 INT_MAX,
744 "Location",
745 "Mouse location",
746 INT_MIN,
747 INT_MAX);
749
750 RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
751
752 RNA_def_boolean(ot->srna,
753 "clear_viewer",
754 false,
755 "Clear Viewer",
756 "Deactivate geometry nodes viewer when clicking in empty space");
757}
758
760
761/* -------------------------------------------------------------------- */
764
766{
767 SpaceNode &snode = *CTX_wm_space_node(C);
768 bNodeTree &node_tree = *snode.edittree;
769 const ARegion &region = *CTX_wm_region(C);
770 rctf rectf;
771
773 UI_view2d_region_to_view_rctf(&region.v2d, &rectf, &rectf);
774
775 const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
776 const bool select = (sel_op != SEL_OP_SUB);
777 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
778 node_deselect_all(node_tree);
779 }
780
781 for (bNode *node : node_tree.all_nodes()) {
782 bool is_inside = false;
783
784 switch (node->type_legacy) {
785 case NODE_FRAME: {
786 /* Frame nodes are selectable by their borders (including their whole rect - as for other
787 * nodes - would prevent selection of other nodes inside that frame. */
788 const rctf frame_inside = node_frame_rect_inside(snode, *node);
789 if (BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr) &&
790 !BLI_rctf_inside_rctf(&frame_inside, &rectf))
791 {
793 is_inside = true;
794 }
795 break;
796 }
797 default: {
798 is_inside = BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr);
799 break;
800 }
801 }
802
803 if (is_inside) {
805 }
806 }
807
808 tree_draw_order_update(node_tree);
809
812
813 return OPERATOR_FINISHED;
814}
815
817{
818 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
819
820 if (tweak && is_event_over_node_or_socket(*C, *event)) {
822 }
823
824 return WM_gesture_box_invoke(C, op, event);
825}
826
828{
829 /* identifiers */
830 ot->name = "Box Select";
831 ot->idname = "NODE_OT_select_box";
832 ot->description = "Use box selection to select nodes";
833
834 /* API callbacks. */
835 ot->invoke = node_box_select_invoke;
836 ot->exec = node_box_select_exec;
837 ot->modal = WM_gesture_box_modal;
838 ot->cancel = WM_gesture_box_cancel;
839
841
842 /* flags */
844
845 /* properties */
846 RNA_def_boolean(ot->srna,
847 "tweak",
848 false,
849 "Tweak",
850 "Only activate when mouse is not over a node (useful for tweak gesture)");
851
854}
855
857
858/* -------------------------------------------------------------------- */
861
863{
864 SpaceNode *snode = CTX_wm_space_node(C);
865 ARegion *region = CTX_wm_region(C);
866 bNodeTree &node_tree = *snode->edittree;
867
868 int x, y, radius;
869 float2 offset;
870
871 float zoom = float(BLI_rcti_size_x(&region->winrct)) / BLI_rctf_size_x(&region->v2d.cur);
872
873 const eSelectOp sel_op = ED_select_op_modal(
874 (eSelectOp)RNA_enum_get(op->ptr, "mode"),
876 const bool select = (sel_op != SEL_OP_SUB);
877 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
878 node_deselect_all(node_tree);
879 }
880
881 /* get operator properties */
882 x = RNA_int_get(op->ptr, "x");
883 y = RNA_int_get(op->ptr, "y");
884 radius = RNA_int_get(op->ptr, "radius");
885
886 UI_view2d_region_to_view(&region->v2d, x, y, &offset.x, &offset.y);
887
888 for (bNode *node : node_tree.all_nodes()) {
889 switch (node->type_legacy) {
890 case NODE_FRAME: {
891 /* Frame nodes are selectable by their borders (including their whole rect - as for other
892 * nodes - would prevent selection of _only_ other nodes inside that frame. */
893 rctf frame_inside = node_frame_rect_inside(*snode, *node);
894 const float radius_adjusted = float(radius) / zoom;
895 BLI_rctf_pad(&frame_inside, -2.0f * radius_adjusted, -2.0f * radius_adjusted);
896 if (BLI_rctf_isect_circle(&node->runtime->draw_bounds, offset, radius_adjusted) &&
897 !BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted))
898 {
900 }
901 break;
902 }
903 default: {
904 if (BLI_rctf_isect_circle(&node->runtime->draw_bounds, offset, radius / zoom)) {
906 }
907 break;
908 }
909 }
910 }
911
914
915 return OPERATOR_FINISHED;
916}
917
919{
920 /* identifiers */
921 ot->name = "Circle Select";
922 ot->idname = "NODE_OT_select_circle";
923 ot->description = "Use circle selection to select nodes";
924
925 /* API callbacks. */
930 ot->get_name = ED_select_circle_get_name;
931
932 /* flags */
934
935 /* properties */
938}
939
941
942/* -------------------------------------------------------------------- */
945
947{
948 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
949
950 if (tweak && is_event_over_node_or_socket(*C, *event)) {
952 }
953
954 return WM_gesture_lasso_invoke(C, op, event);
955}
956
957static bool do_lasso_select_node(bContext *C, const Span<int2> mcoords, eSelectOp sel_op)
958{
959 SpaceNode *snode = CTX_wm_space_node(C);
960 bNodeTree &node_tree = *snode->edittree;
961
962 ARegion *region = CTX_wm_region(C);
963
964 rcti rect;
965 bool changed = false;
966
967 const bool select = (sel_op != SEL_OP_SUB);
968 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
969 node_deselect_all(node_tree);
970 changed = true;
971 }
972
973 /* Get rectangle from operator. */
974 BLI_lasso_boundbox(&rect, mcoords);
975
976 for (bNode *node : node_tree.all_nodes()) {
977 if (select && (node->flag & NODE_SELECT)) {
978 continue;
979 }
980
981 switch (node->type_legacy) {
982 case NODE_FRAME: {
983 /* Frame nodes are selectable by their borders (including their whole rect - as for other
984 * nodes - would prevent selection of other nodes inside that frame. */
985 rctf rectf;
986 BLI_rctf_rcti_copy(&rectf, &rect);
987 UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
988 const rctf frame_inside = node_frame_rect_inside(*snode, *node);
989 if (BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr) &&
990 !BLI_rctf_inside_rctf(&frame_inside, &rectf))
991 {
993 changed = true;
994 }
995 break;
996 }
997 default: {
998 int2 screen_co;
999 const float2 center = {BLI_rctf_cent_x(&node->runtime->draw_bounds),
1000 BLI_rctf_cent_y(&node->runtime->draw_bounds)};
1001
1002 /* marker in screen coords */
1004 &region->v2d, center.x, center.y, &screen_co.x, &screen_co.y) &&
1005 BLI_rcti_isect_pt(&rect, screen_co.x, screen_co.y) &&
1006 BLI_lasso_is_point_inside(mcoords, screen_co.x, screen_co.y, INT_MAX))
1007 {
1009 changed = true;
1010 }
1011 break;
1012 }
1013 }
1014 }
1015
1016 if (changed) {
1019 }
1020
1021 return changed;
1022}
1023
1025{
1026 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
1027
1028 if (mcoords.is_empty()) {
1029 return OPERATOR_PASS_THROUGH;
1030 }
1031
1032 const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
1033
1034 do_lasso_select_node(C, mcoords, sel_op);
1035
1036 return OPERATOR_FINISHED;
1037}
1038
1040{
1041 /* identifiers */
1042 ot->name = "Lasso Select";
1043 ot->description = "Select nodes using lasso selection";
1044 ot->idname = "NODE_OT_select_lasso";
1045
1046 /* API callbacks. */
1047 ot->invoke = node_lasso_select_invoke;
1048 ot->modal = WM_gesture_lasso_modal;
1049 ot->exec = node_lasso_select_exec;
1051 ot->cancel = WM_gesture_lasso_cancel;
1052
1053 /* flags */
1055
1056 /* properties */
1057 RNA_def_boolean(ot->srna,
1058 "tweak",
1059 false,
1060 "Tweak",
1061 "Only activate when mouse is not over a node (useful for tweak gesture)");
1062
1065}
1066
1068
1069/* -------------------------------------------------------------------- */
1072
1073static bool any_node_selected(const bNodeTree &node_tree)
1074{
1075 for (const bNode *node : node_tree.all_nodes()) {
1076 if (node->flag & NODE_SELECT) {
1077 return true;
1078 }
1079 }
1080 return false;
1081}
1082
1084{
1085 SpaceNode &snode = *CTX_wm_space_node(C);
1086 bNodeTree &node_tree = *snode.edittree;
1087
1088 node_tree.ensure_topology_cache();
1089
1090 int action = RNA_enum_get(op->ptr, "action");
1091 if (action == SEL_TOGGLE) {
1092 if (any_node_selected(node_tree)) {
1093 action = SEL_DESELECT;
1094 }
1095 else {
1096 action = SEL_SELECT;
1097 }
1098 }
1099
1100 switch (action) {
1101 case SEL_SELECT:
1102 for (bNode *node : node_tree.all_nodes()) {
1103 bke::node_set_selected(*node, true);
1104 }
1105 break;
1106 case SEL_DESELECT:
1107 node_deselect_all(node_tree);
1108 break;
1109 case SEL_INVERT:
1110 for (bNode *node : node_tree.all_nodes()) {
1111 bke::node_set_selected(*node, !(node->flag & SELECT));
1112 }
1113 break;
1114 }
1115
1116 tree_draw_order_update(node_tree);
1117
1120 return OPERATOR_FINISHED;
1121}
1122
1124{
1125 /* identifiers */
1126 ot->name = "(De)select All";
1127 ot->description = "(De)select all nodes";
1128 ot->idname = "NODE_OT_select_all";
1129
1130 /* API callbacks. */
1131 ot->exec = node_select_all_exec;
1133
1134 /* flags */
1135 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1136
1138}
1139
1141
1142/* -------------------------------------------------------------------- */
1145
1147{
1148 SpaceNode &snode = *CTX_wm_space_node(C);
1149 bNodeTree &node_tree = *snode.edittree;
1150
1151 node_tree.ensure_topology_cache();
1152
1153 VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
1154
1155 for (bNode *node : initial_selection) {
1156 for (bNodeSocket *output_socket : node->output_sockets()) {
1157 if (!output_socket->is_available()) {
1158 continue;
1159 }
1160 for (bNodeSocket *input_socket : output_socket->directly_linked_sockets()) {
1161 if (!input_socket->is_available()) {
1162 continue;
1163 }
1164 bke::node_set_selected(input_socket->owner_node(), true);
1165 }
1166 }
1167 }
1168
1169 tree_draw_order_update(node_tree);
1170
1172 return OPERATOR_FINISHED;
1173}
1174
1176{
1177 /* identifiers */
1178 ot->name = "Select Linked To";
1179 ot->description = "Select nodes linked to the selected ones";
1180 ot->idname = "NODE_OT_select_linked_to";
1181
1182 /* API callbacks. */
1185
1186 /* flags */
1187 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1188}
1189
1191
1192/* -------------------------------------------------------------------- */
1195
1197{
1198 SpaceNode &snode = *CTX_wm_space_node(C);
1199 bNodeTree &node_tree = *snode.edittree;
1200
1201 node_tree.ensure_topology_cache();
1202
1203 VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
1204
1205 for (bNode *node : initial_selection) {
1206 for (bNodeSocket *input_socket : node->input_sockets()) {
1207 if (!input_socket->is_available()) {
1208 continue;
1209 }
1210 for (bNodeSocket *output_socket : input_socket->directly_linked_sockets()) {
1211 if (!output_socket->is_available()) {
1212 continue;
1213 }
1214 bke::node_set_selected(output_socket->owner_node(), true);
1215 }
1216 }
1217 }
1218
1219 tree_draw_order_update(node_tree);
1220
1222 return OPERATOR_FINISHED;
1223}
1224
1226{
1227 /* identifiers */
1228 ot->name = "Select Linked From";
1229 ot->description = "Select nodes linked from the selected ones";
1230 ot->idname = "NODE_OT_select_linked_from";
1231
1232 /* API callbacks. */
1235
1236 /* flags */
1237 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1238}
1239
1241
1242/* -------------------------------------------------------------------- */
1245
1246static bool nodes_are_same_type_for_select(const bNode &a, const bNode &b)
1247{
1248 return a.type_legacy == b.type_legacy;
1249}
1250
1252{
1253 SpaceNode *snode = CTX_wm_space_node(C);
1254 ARegion *region = CTX_wm_region(C);
1255 const bool prev = RNA_boolean_get(op->ptr, "prev");
1256 bNode *active_node = bke::node_get_active(*snode->edittree);
1257
1258 if (active_node == nullptr) {
1259 return OPERATOR_CANCELLED;
1260 }
1261
1262 bNodeTree &node_tree = *snode->edittree;
1263 node_tree.ensure_topology_cache();
1264 if (node_tree.all_nodes().size() == 1) {
1265 return OPERATOR_CANCELLED;
1266 }
1267
1268 const Span<const bNode *> toposort = node_tree.toposort_left_to_right();
1269 const int index = toposort.first_index(active_node);
1270
1271 int new_index = index;
1272 while (true) {
1273 new_index += (prev ? -1 : 1);
1274 if (!toposort.index_range().contains(new_index)) {
1275 return OPERATOR_CANCELLED;
1276 }
1277 if (nodes_are_same_type_for_select(*toposort[new_index], *active_node)) {
1278 break;
1279 }
1280 }
1281
1282 bNode *new_active_node = node_tree.all_nodes()[toposort[new_index]->index()];
1283 if (new_active_node == active_node) {
1284 return OPERATOR_CANCELLED;
1285 }
1286
1287 node_select_single(*C, *new_active_node);
1288
1289 if (!BLI_rctf_inside_rctf(&region->v2d.cur, &new_active_node->runtime->draw_bounds)) {
1290 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1291 space_node_view_flag(*C, *snode, *region, NODE_SELECT, smooth_viewtx);
1292 }
1293
1294 return OPERATOR_FINISHED;
1295}
1296
1298{
1299 /* identifiers */
1300 ot->name = "Activate Same Type Next/Prev";
1301 ot->description = "Activate and view same node type, step by step";
1302 ot->idname = "NODE_OT_select_same_type_step";
1303
1304 /* API callbacks. */
1307
1308 /* flags */
1309 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1310
1311 RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
1312}
1313
1315
1316/* -------------------------------------------------------------------- */
1319
1320static std::string node_find_create_label(const bNodeTree &ntree, const bNode &node)
1321{
1322 std::string label = bke::node_label(ntree, node);
1323 if (label == node.name) {
1324 return label;
1325 }
1326 return fmt::format("{} ({})", label, node.name);
1327}
1328
1329/* Generic search invoke. */
1330static void node_find_update_fn(const bContext *C,
1331 void * /*arg*/,
1332 const char *str,
1333 uiSearchItems *items,
1334 const bool /*is_first*/)
1335{
1336 SpaceNode *snode = CTX_wm_space_node(C);
1337
1339
1340 const bNodeTree &ntree = *snode->edittree;
1341 for (bNode *node : snode->edittree->all_nodes()) {
1342 const std::string name = node_find_create_label(ntree, *node);
1343 search.add(name, node);
1344 }
1345
1346 const Vector<bNode *> filtered_nodes = search.query(str);
1347
1348 for (bNode *node : filtered_nodes) {
1349 const std::string name = node_find_create_label(ntree, *node);
1350 if (!UI_search_item_add(items, name, node, ICON_NONE, 0, 0)) {
1351 break;
1352 }
1353 }
1354}
1355
1356static void node_find_exec_fn(bContext *C, void * /*arg1*/, void *arg2)
1357{
1358 SpaceNode *snode = CTX_wm_space_node(C);
1359 bNode *active = (bNode *)arg2;
1360
1361 if (active) {
1362 ARegion *region = CTX_wm_region(C);
1364
1365 if (!BLI_rctf_inside_rctf(&region->v2d.cur, &active->runtime->draw_bounds)) {
1366 space_node_view_flag(*C, *snode, *region, NODE_SELECT, U.smooth_viewtx);
1367 }
1368 }
1369}
1370
1371static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_optype)
1372{
1373 static char search[256] = "";
1374 uiBlock *block;
1375 uiBut *but;
1376 wmOperatorType *optype = (wmOperatorType *)arg_optype;
1377
1378 block = UI_block_begin(C, region, "_popup", blender::ui::EmbossType::Emboss);
1381
1382 const int box_width = UI_searchbox_size_x_guess(C, node_find_update_fn, nullptr);
1383
1384 but = uiDefSearchBut(
1385 block, search, 0, ICON_VIEWZOOM, sizeof(search), 0, 0, box_width, UI_UNIT_Y, "");
1387 but, nullptr, node_find_update_fn, optype, false, nullptr, node_find_exec_fn, nullptr);
1389
1390 /* Fake button holds space for search items. */
1391 const int height = UI_searchbox_size_y() - UI_SEARCHBOX_BOUNDS;
1392 uiDefBut(
1393 block, UI_BTYPE_LABEL, 0, "", 0, -height, box_width, height, nullptr, 0, 0, std::nullopt);
1394
1395 /* Move it downwards, mouse over button. */
1396 std::array<int, 2> bounds_offset = {0, -UI_UNIT_Y};
1397 UI_block_bounds_set_popup(block, UI_SEARCHBOX_BOUNDS, bounds_offset.data());
1398
1399 return block;
1400}
1401
1403 wmOperator *op,
1404 const wmEvent * /*event*/)
1405{
1407 return OPERATOR_CANCELLED;
1408}
1409
1411{
1412 /* identifiers */
1413 ot->name = "Find Node";
1414 ot->description = "Search for a node by name and focus and select it";
1415 ot->idname = "NODE_OT_find_node";
1416
1417 /* API callbacks. */
1418 ot->invoke = node_find_node_invoke;
1420
1421 /* flags */
1422 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1423}
1424
1426
1427} // namespace blender::ed::space_node
WorkSpace * CTX_wm_workspace(const bContext *C)
SpaceNode * CTX_wm_space_node(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_FRAME
Definition BKE_node.hh:797
#define GEO_NODE_VIEWER
void BKE_viewer_path_clear(ViewerPath *viewer_path)
bScreen * BKE_workspace_active_screen_get(const WorkSpaceInstanceHook *hook) GETTER_ATTRS
Definition workspace.cc:612
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
#define LISTBASE_FOREACH(type, var, list)
MINLINE bool compare_v3v3(const float v1[3], const float v2[3], float limit) ATTR_WARN_UNUSED_RESULT
BLI_INLINE float BLI_rctf_cent_y(const struct rctf *rct)
Definition BLI_rect.h:189
bool BLI_rctf_isect(const struct rctf *src1, const struct rctf *src2, struct rctf *dest)
BLI_INLINE float BLI_rctf_cent_x(const struct rctf *rct)
Definition BLI_rect.h:185
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rctf_isect_circle(const struct rctf *rect, const float xy[2], float radius)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:194
void BLI_rctf_pad(struct rctf *rect, float pad_x, float pad_y)
Definition rct.cc:637
bool BLI_rctf_isect_pt(const struct rctf *rect, float x, float y)
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
BLI_INLINE float BLI_rctf_size_x(const struct rctf *rct)
Definition BLI_rect.h:202
bool BLI_rctf_inside_rctf(const rctf *rct_a, const rctf *rct_b)
Definition rct.cc:193
size_t size_t size_t BLI_str_partition_ex_utf8(const char *str, const char *end, const unsigned int delim[], const char **r_sep, const char **r_suf, bool from_right) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
unsigned int uint
#define STREQLEN(a, b, n)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_id_tag_update_for_side_effect_request(Depsgraph *depsgraph, ID *id, unsigned int flags)
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ SOCK_OUT
@ SOCK_IN
@ NODE_DO_OUTPUT
@ NODE_ACTIVE
@ NODE_SELECT
@ SPACE_VIEW3D
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void ED_node_set_active(Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
Definition node_edit.cc:772
void ED_node_set_active_viewer_key(SpaceNode *snode)
bool ED_operator_node_active(bContext *C)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
SelectPick_Params ED_select_pick_params_from_operator(PointerRNA *ptr) ATTR_NONNULL(1)
std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
bool ED_view3d_has_workbench_in_texture_color(const Scene *scene, const Object *ob, const View3D *v3d)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define UI_UNIT_Y
void UI_block_theme_style_set(uiBlock *block, char theme_style)
uiBlock * UI_block_begin(const bContext *C, ARegion *region, std::string name, blender::ui::EmbossType emboss)
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition interface.cc:641
uiBut * uiDefBut(uiBlock *block, int type, int retval, blender::StringRef str, int x, int y, short width, short height, void *poin, float min, float max, std::optional< blender::StringRef > tip)
void UI_popup_block_invoke(bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free)
void UI_but_func_search_set(uiBut *but, uiButSearchCreateFn search_create_fn, uiButSearchUpdateFn search_update_fn, void *arg, bool free_arg, uiFreeArgFunc search_arg_free_fn, uiButHandleFunc search_exec_fn, void *active)
uiBut * uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxncpy, int x, int y, short width, short height, std::optional< blender::StringRef > tip)
int UI_searchbox_size_x_guess(const bContext *C, const uiButSearchUpdateFn update_fn, void *arg)
@ UI_BLOCK_THEME_STYLE_POPUP
bool UI_search_item_add(uiSearchItems *items, blender::StringRef name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
#define UI_SEARCHBOX_BOUNDS
int UI_searchbox_size_y()
void UI_block_flag_enable(uiBlock *block, int flag)
@ UI_BTYPE_LABEL
@ UI_BUT_ACTIVATE_ON_INIT
@ UI_BLOCK_SEARCH_MENU
@ UI_BLOCK_LOOP
@ UI_BLOCK_MOVEMOUSE_QUIT
void UI_but_flag_enable(uiBut *but, int flag)
bool UI_view2d_view_to_region_clip(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1701
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1667
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1674
#define NC_NODE
Definition WM_types.hh:391
#define NC_VIEWER_PATH
Definition WM_types.hh:403
#define ND_NODE_GIZMO
Definition WM_types.hh:513
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_SELECTED
Definition WM_types.hh:586
#define U
BPy_StructRNA * depsgraph
bool is_empty() const
Definition BLI_array.hh:253
constexpr bool contains(int64_t value) const
constexpr int64_t first_index(const T &search_value) const
Definition BLI_span.hh:377
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
bool add(const Key &key)
void add(const StringRef str, T *user_data, const int weight=0)
Vector< T * > query(const StringRef query) const
#define SELECT
#define str(s)
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:772
#define input
#define active
#define select(A, B, C)
#define output
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:5242
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4957
Span< const bNodeZoneType * > all_zone_types()
bool node_set_selected(bNode &node, bool select)
Definition node.cc:4967
static bool has_workbench_in_texture_color(const wmWindowManager *wm, const Scene *scene, const Object *ob)
static uiBlock * node_find_menu(bContext *C, ARegion *region, void *arg_optype)
static bool node_select_grouped_type(bNodeTree &node_tree, bNode &node_act)
static std::string node_find_create_label(const bNodeTree &ntree, const bNode &node)
rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node)
void node_deselect_all_input_sockets(bNodeTree &node_tree, bool deselect_nodes)
static void node_find_exec_fn(bContext *C, void *, void *arg2)
void NODE_OT_select_circle(wmOperatorType *ot)
static bool is_position_over_node_or_socket(SpaceNode &snode, ARegion &region, const float2 &mouse)
void tree_draw_order_update(bNodeTree &ntree)
Definition node_draw.cc:321
static wmOperatorStatus node_find_node_invoke(bContext *C, wmOperator *op, const wmEvent *)
static wmOperatorStatus node_box_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool space_node_view_flag(bContext &C, SpaceNode &snode, ARegion &region, int node_flag, int smooth_viewtx)
Definition node_view.cc:76
static bool node_mouse_select(bContext *C, wmOperator *op, const int2 mval, const SelectPick_Params &params)
static wmOperatorStatus node_select_linked_from_exec(bContext *C, wmOperator *)
static bool do_lasso_select_node(bContext *C, const Span< int2 > mcoords, eSelectOp sel_op)
static wmOperatorStatus node_box_select_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_lasso_select_exec(bContext *C, wmOperator *op)
void node_select_paired(bNodeTree &node_tree)
static wmOperatorStatus node_select_grouped_exec(bContext *C, wmOperator *op)
void NODE_OT_find_node(wmOperatorType *ot)
static void node_find_update_fn(const bContext *C, void *, const char *str, uiSearchItems *items, const bool)
void NODE_OT_select_lasso(wmOperatorType *ot)
bool node_deselect_all(bNodeTree &node_tree)
void node_socket_select(bNode *node, bNodeSocket &sock)
bNodeSocket * node_find_indicated_socket(SpaceNode &snode, ARegion &region, const float2 &cursor, const eNodeSocketInOut in_out)
void NODE_OT_select(wmOperatorType *ot)
static bool node_select_grouped_name(bNodeTree &node_tree, bNode &node_act, const bool from_right)
Array< bNode * > tree_draw_order_calc_nodes_reversed(bNodeTree &ntree)
Definition node_draw.cc:345
static void node_socket_toggle(bNode *node, bNodeSocket &sock, bool deselect_node)
static bool nodes_are_same_type_for_select(const bNode &a, const bNode &b)
static wmOperatorStatus node_select_same_type_step_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void node_socket_deselect(bNode *node, bNodeSocket &sock, bool deselect_node)
void NODE_OT_select_grouped(wmOperatorType *ot)
VectorSet< bNode * > get_selected_nodes(bNodeTree &node_tree)
void NODE_OT_select_linked_from(wmOperatorType *ot)
void node_deselect_all_output_sockets(bNodeTree &node_tree, bool deselect_nodes)
static wmOperatorStatus node_select_exec(bContext *C, wmOperator *op)
static bNode * node_under_mouse_select(const SpaceNode &snode, const float2 mouse)
void NODE_OT_select_same_type_step(wmOperatorType *ot)
void node_select_single(bContext &C, bNode &node)
static bool node_select_grouped_color(bNodeTree &node_tree, bNode &node_act)
void NODE_OT_select_linked_to(wmOperatorType *ot)
static bool any_node_selected(const bNodeTree &node_tree)
bool node_or_socket_isect_event(const bContext &C, const wmEvent &event)
static wmOperatorStatus node_circleselect_exec(bContext *C, wmOperator *op)
static wmOperatorStatus node_select_all_exec(bContext *C, wmOperator *op)
static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event)
static bool node_frame_select_isect_mouse(const SpaceNode &snode, const bNode &node, const float2 &mouse)
static wmOperatorStatus node_select_linked_to_exec(bContext *C, wmOperator *)
void NODE_OT_select_box(wmOperatorType *ot)
void NODE_OT_select_all(wmOperatorType *ot)
static wmOperatorStatus node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
std::optional< ViewerPathForGeometryNodesViewer > parse_geometry_nodes_viewer(const ViewerPath &viewer_path)
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node)
T max(const T &a, const T &b)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define NODE_RESIZE_MARGIN
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_int_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
ViewerPath viewer_path
ListBase inputs
float color[3]
char name[64]
int16_t type_legacy
bNodeRuntimeHandle * runtime
ListBase outputs
ListBase areabase
int mval[2]
Definition WM_types.hh:760
struct wmOperatorType * type
struct PointerRNA * ptr
void WM_event_drag_start_mval(const wmEvent *event, const ARegion *region, int r_mval[2])
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4226
bool WM_gesture_is_modal_first(const wmGesture *gesture)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
int WM_operator_smooth_viewtx_get(const wmOperator *op)