Blender V5.0
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
14#include "DNA_image_types.h"
15#include "DNA_material_types.h"
16#include "DNA_node_types.h"
17#include "DNA_object_types.h"
19
20#include "BLI_lasso_2d.hh"
21#include "BLI_listbase.h"
22#include "BLI_math_vector.h"
23#include "BLI_rect.h"
24#include "BLI_resource_scope.hh"
25#include "BLI_string.h"
26#include "BLI_string_utf8.h"
27#include "BLI_utildefines.h"
28
29#include "BKE_context.hh"
30#include "BKE_idtype.hh"
31#include "BKE_lib_id.hh"
32#include "BKE_main.hh"
34#include "BKE_node.hh"
36#include "BKE_node_runtime.hh"
38#include "BKE_screen.hh"
39#include "BKE_viewer_path.hh"
40#include "BKE_workspace.hh"
41
42#include "ED_node.hh" /* own include */
43#include "ED_screen.hh"
44#include "ED_select_utils.hh"
45#include "ED_view3d.hh"
46#include "ED_viewer_path.hh"
47
48#include "RNA_access.hh"
49#include "RNA_define.hh"
50
51#include "WM_api.hh"
52#include "WM_types.hh"
53
54#include "UI_resources.hh"
55#include "UI_string_search.hh"
56#include "UI_view2d.hh"
57
58#include "DEG_depsgraph.hh"
59
60#include "BLT_translation.hh"
61
62#include "node_intern.hh" /* own include */
63
65
66static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event);
67
78 const Scene *scene,
79 const Object *ob)
80{
81 LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
82 if (win->scene != scene) {
83 continue;
84 }
85 const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook);
86 LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
87 if (area->spacetype == SPACE_VIEW3D) {
88 const View3D *v3d = (const View3D *)area->spacedata.first;
89
90 if (ED_view3d_has_workbench_in_texture_color(scene, ob, v3d)) {
91 return true;
92 }
93 }
94 }
95 }
96 return false;
97}
98
99/* -------------------------------------------------------------------- */
102
103rctf node_frame_rect_inside(const SpaceNode &snode, const bNode &node)
104{
105 const float margin = 4.0f * NODE_RESIZE_MARGIN * math::max(snode.runtime->aspect, 1.0f);
106 rctf frame_inside = {
107 node.runtime->draw_bounds.xmin,
108 node.runtime->draw_bounds.xmax,
109 node.runtime->draw_bounds.ymin,
110 node.runtime->draw_bounds.ymax,
111 };
112
113 BLI_rctf_pad(&frame_inside, -margin, -margin);
114
115 return frame_inside;
116}
117
119{
120 return is_event_over_node_or_socket(C, event);
121}
122
124 const bNode &node,
125 const float2 &mouse)
126{
127 /* Frame nodes are selectable by their borders (including their whole rect - as for other nodes -
128 * would prevent e.g. box selection of nodes inside that frame). */
129 const rctf frame_inside = node_frame_rect_inside(snode, node);
130 if (BLI_rctf_isect_pt(&node.runtime->draw_bounds, mouse.x, mouse.y) &&
131 !BLI_rctf_isect_pt(&frame_inside, mouse.x, mouse.y))
132 {
133 return true;
134 }
135
136 return false;
137}
138
139bNode *node_under_mouse_get(const SpaceNode &snode, const float2 mouse)
140{
142 switch (node->type_legacy) {
143 case NODE_FRAME: {
144 if (node_frame_select_isect_mouse(snode, *node, mouse)) {
145 return node;
146 }
147 break;
148 }
149 default: {
150 if (BLI_rctf_isect_pt(&node->runtime->draw_bounds, int(mouse.x), int(mouse.y))) {
151 return node;
152 }
153 break;
154 }
155 }
156 }
157 return nullptr;
158}
159
160static bool is_position_over_node_or_socket(SpaceNode &snode, ARegion &region, const float2 &mouse)
161{
162 if (node_under_mouse_get(snode, mouse)) {
163 return true;
164 }
165 if (node_find_indicated_socket(snode, region, mouse, SOCK_IN | SOCK_OUT)) {
166 return true;
167 }
168 return false;
169}
170
171static bool is_event_over_node_or_socket(const bContext &C, const wmEvent &event)
172{
173 SpaceNode &snode = *CTX_wm_space_node(&C);
174 ARegion &region = *CTX_wm_region(&C);
175
176 int2 mval;
177 WM_event_drag_start_mval(&event, &region, mval);
178
179 float2 mouse;
180 UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &mouse.x, &mouse.y);
181 return is_position_over_node_or_socket(snode, region, mouse);
182}
183
185{
186 sock.flag |= SELECT;
187
188 /* select node too */
189 if (node) {
190 node->flag |= SELECT;
191 }
192}
193
194void node_socket_deselect(bNode *node, bNodeSocket &sock, const bool deselect_node)
195{
196 sock.flag &= ~SELECT;
197
198 if (node && deselect_node) {
199 bool sel = false;
200
201 /* if no selected sockets remain, also deselect the node */
203 if (input->flag & SELECT) {
204 sel = true;
205 break;
206 }
207 }
209 if (output->flag & SELECT) {
210 sel = true;
211 break;
212 }
213 }
214
215 if (!sel) {
216 node->flag &= ~SELECT;
217 }
218 }
219}
220
221static void node_socket_toggle(bNode *node, bNodeSocket &sock, bool deselect_node)
222{
223 if (sock.flag & SELECT) {
224 node_socket_deselect(node, sock, deselect_node);
225 }
226 else {
227 node_socket_select(node, sock);
228 }
229}
230
232{
233 bool changed = false;
234 for (bNode *node : node_tree.all_nodes()) {
235 changed |= bke::node_set_selected(*node, false);
236 }
237 return changed;
238}
239
240void node_deselect_all_input_sockets(bNodeTree &node_tree, const bool deselect_nodes)
241{
242 /* XXX not calling node_socket_deselect here each time, because this does iteration
243 * over all node sockets internally to check if the node stays selected.
244 * We can do that more efficiently here.
245 */
246
247 for (bNode *node : node_tree.all_nodes()) {
248 bool sel = false;
249
250 LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
251 socket->flag &= ~SELECT;
252 }
253
254 /* If no selected sockets remain, also deselect the node. */
255 if (deselect_nodes) {
256 LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
257 if (socket->flag & SELECT) {
258 sel = true;
259 break;
260 }
261 }
262
263 if (!sel) {
264 node->flag &= ~SELECT;
265 }
266 }
267 }
268}
269
270void node_deselect_all_output_sockets(bNodeTree &node_tree, const bool deselect_nodes)
271{
272 /* XXX not calling node_socket_deselect here each time, because this does iteration
273 * over all node sockets internally to check if the node stays selected.
274 * We can do that more efficiently here.
275 */
276
277 for (bNode *node : node_tree.all_nodes()) {
278 bool sel = false;
279
280 LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
281 socket->flag &= ~SELECT;
282 }
283
284 /* if no selected sockets remain, also deselect the node */
285 if (deselect_nodes) {
286 LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
287 if (socket->flag & SELECT) {
288 sel = true;
289 break;
290 }
291 }
292
293 if (!sel) {
294 node->flag &= ~SELECT;
295 }
296 }
297 }
298}
299
301{
302 node_tree.ensure_topology_cache();
303 for (const bke::bNodeZoneType *zone_type : bke::all_zone_types()) {
304 for (bNode *input_node : node_tree.nodes_by_type(zone_type->input_idname)) {
305 if (bNode *output_node = zone_type->get_corresponding_output(node_tree, *input_node)) {
306 if (input_node->flag & NODE_SELECT) {
307 output_node->flag |= NODE_SELECT;
308 }
309 if (output_node->flag & NODE_SELECT) {
310 input_node->flag |= NODE_SELECT;
311 }
312 }
313 }
314 }
315}
316
318{
319 VectorSet<bNode *> selected_nodes;
320 for (bNode *node : node_tree.all_nodes()) {
321 if (node->flag & NODE_SELECT) {
322 selected_nodes.add(node);
323 }
324 }
325 return selected_nodes;
326}
327
329
330/* -------------------------------------------------------------------- */
333
334/* Return true if we need redraw, otherwise false. */
335
336static bool node_select_grouped_type(bNodeTree &node_tree, bNode &node_act)
337{
338 bool changed = false;
339 for (bNode *node : node_tree.all_nodes()) {
340 if ((node->flag & SELECT) == 0) {
341 if (node->type_legacy == node_act.type_legacy) {
342 bke::node_set_selected(*node, true);
343 changed = true;
344 }
345 }
346 }
347 return changed;
348}
349
350static bool node_select_grouped_color(bNodeTree &node_tree, bNode &node_act)
351{
352 bool changed = false;
353 for (bNode *node : node_tree.all_nodes()) {
354 if ((node->flag & SELECT) == 0) {
355 if (compare_v3v3(node->color, node_act.color, 0.005f)) {
356 bke::node_set_selected(*node, true);
357 changed = true;
358 }
359 }
360 }
361 return changed;
362}
363
364static bool node_select_grouped_name(bNodeTree &node_tree, bNode &node_act, const bool from_right)
365{
366 bool changed = false;
367 const uint delims[] = {'.', '-', '_', '\0'};
368 size_t pref_len_act, pref_len_curr;
369 const char *sep, *suf_act, *suf_curr;
370
371 pref_len_act = BLI_str_partition_ex_utf8(
372 node_act.name, nullptr, delims, &sep, &suf_act, from_right);
373
374 /* NOTE: in case we are searching for suffix, and found none, use whole name as suffix. */
375 if (from_right && !(sep && suf_act)) {
376 pref_len_act = 0;
377 suf_act = node_act.name;
378 }
379
380 for (bNode *node : node_tree.all_nodes()) {
381 if (node->flag & SELECT) {
382 continue;
383 }
384 pref_len_curr = BLI_str_partition_ex_utf8(
385 node->name, nullptr, delims, &sep, &suf_curr, from_right);
386
387 /* Same as with active node name! */
388 if (from_right && !(sep && suf_curr)) {
389 pref_len_curr = 0;
390 suf_curr = node->name;
391 }
392
393 if ((from_right && STREQ(suf_act, suf_curr)) ||
394 (!from_right && (pref_len_act == pref_len_curr) &&
395 STREQLEN(node_act.name, node->name, pref_len_act)))
396 {
397 bke::node_set_selected(*node, true);
398 changed = true;
399 }
400 }
401
402 return changed;
403}
404
405enum {
410};
411
413{
414 SpaceNode &snode = *CTX_wm_space_node(C);
415 bNodeTree &node_tree = *snode.edittree;
416 bNode *node_act = bke::node_get_active(*snode.edittree);
417
418 if (node_act == nullptr) {
419 return OPERATOR_CANCELLED;
420 }
421
422 bool changed = false;
423 const bool extend = RNA_boolean_get(op->ptr, "extend");
424 const int type = RNA_enum_get(op->ptr, "type");
425
426 if (!extend) {
427 node_deselect_all(node_tree);
428 }
429 bke::node_set_selected(*node_act, true);
430
431 switch (type) {
433 changed = node_select_grouped_type(node_tree, *node_act);
434 break;
436 changed = node_select_grouped_color(node_tree, *node_act);
437 break;
439 changed = node_select_grouped_name(node_tree, *node_act, false);
440 break;
442 changed = node_select_grouped_name(node_tree, *node_act, true);
443 break;
444 default:
445 break;
446 }
447
448 if (changed) {
449 tree_draw_order_update(node_tree);
451 return OPERATOR_FINISHED;
452 }
453
454 return OPERATOR_CANCELLED;
455}
456
458{
459 PropertyRNA *prop;
460 static const EnumPropertyItem prop_select_grouped_types[] = {
461 {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""},
462 {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""},
463 {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""},
464 {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""},
465 {0, nullptr, 0, nullptr, nullptr},
466 };
467
468 /* identifiers */
469 ot->name = "Select Grouped";
470 ot->description = "Select nodes with similar properties";
471 ot->idname = "NODE_OT_select_grouped";
472
473 /* API callbacks. */
474 ot->invoke = WM_menu_invoke;
477
478 /* flags */
480
481 /* properties */
482 prop = RNA_def_boolean(ot->srna,
483 "extend",
484 false,
485 "Extend",
486 "Extend selection instead of deselecting everything first");
488 ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", "");
489}
490
492
493/* -------------------------------------------------------------------- */
496
498{
499 Main *bmain = CTX_data_main(&C);
500 SpaceNode &snode = *CTX_wm_space_node(&C);
501 bNodeTree &node_tree = *snode.edittree;
502 const Object *ob = CTX_data_active_object(&C);
503 const Scene *scene = CTX_data_scene(&C);
505 bool active_texture_changed = false;
506
507 for (bNode *node_iter : node_tree.all_nodes()) {
508 if (node_iter != &node) {
509 bke::node_set_selected(*node_iter, false);
510 }
511 }
512 bke::node_set_selected(node, true);
513
514 ED_node_set_active(bmain, &snode, &node_tree, &node, &active_texture_changed);
516
517 tree_draw_order_update(node_tree);
518 if (active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) {
520 }
521
523}
524
526 const float view_y)
527{
528 const bNodeSocket *best_socket = nullptr;
529 float best_distance = FLT_MAX;
530 for (const bNodeSocket *socket : sockets) {
531 if (!socket->is_icon_visible()) {
532 continue;
533 }
534 const float socket_y = socket->runtime->location.y;
535 const float distance = math::distance(socket_y, view_y);
536 if (distance < best_distance) {
537 best_distance = distance;
538 best_socket = socket;
539 }
540 }
541 return best_socket;
542}
543
545{
546 bNodeTreeInterfacePanel &io_panel = *tree.tree_interface.find_item_parent(io_socket.item, true);
547 bNodeTreeInterfaceItem *item_to_activate = nullptr;
548 if (io_panel.header_toggle_socket() == &io_socket) {
549 item_to_activate = &io_panel.item;
550 }
551 else {
552 item_to_activate = &io_socket.item;
553 }
554 tree.tree_interface.active_item_set(item_to_activate);
555}
556
558 const bNode &group_input_node,
559 const float2 &cursor)
560{
561
562 tree.ensure_topology_cache();
563 tree.ensure_interface_cache();
564 const bNodeSocket *indicated_socket = find_socket_at_mouse_y(
565 group_input_node.output_sockets().drop_back(1), cursor.y);
566 if (!indicated_socket) {
567 return;
568 }
569 const int group_input_i = indicated_socket->index();
570 bNodeTreeInterfaceSocket &io_socket = *tree.interface_inputs()[group_input_i];
572}
573
575 const bNode &group_output_node,
576 const float2 &cursor)
577{
578 tree.ensure_topology_cache();
579 tree.ensure_interface_cache();
580 const bNodeSocket *indicated_socket = find_socket_at_mouse_y(
581 group_output_node.input_sockets().drop_back(1), cursor.y);
582 if (!indicated_socket) {
583 return;
584 }
585 const int group_output_i = indicated_socket->index();
586 bNodeTreeInterfaceSocket &io_socket = *tree.interface_outputs()[group_output_i];
588}
589
591 wmOperator *op,
592 const int2 mval,
594{
595 Main &bmain = *CTX_data_main(C);
596 SpaceNode &snode = *CTX_wm_space_node(C);
597 bNodeTree &node_tree = *snode.edittree;
598 ARegion &region = *CTX_wm_region(C);
599 const Object *ob = CTX_data_active_object(C);
600 const Scene *scene = CTX_data_scene(C);
602 bNode *node = nullptr;
603 bNodeSocket *sock = nullptr;
604
605 /* Always do socket_select when extending selection. */
606 const bool socket_select = (params.sel_op == SEL_OP_XOR) ||
607 RNA_boolean_get(op->ptr, "socket_select");
608 bool changed = false;
609 bool found = false;
610 bool node_was_selected = false;
611
612 /* Get mouse coordinates in view2d space. */
613 float2 cursor;
614 UI_view2d_region_to_view(&region.v2d, mval.x, mval.y, &cursor.x, &cursor.y);
615
616 /* First do socket selection, these generally overlap with nodes. */
617 if (socket_select) {
618 /* NOTE: unlike nodes #SelectPick_Params isn't fully supported. */
619 const bool extend = (params.sel_op == SEL_OP_XOR);
620 sock = node_find_indicated_socket(snode, region, cursor, SOCK_IN);
621 if (sock) {
622 node = &sock->owner_node();
623 found = true;
624 node_was_selected = node->flag & SELECT;
625
626 /* NOTE: SOCK_IN does not take into account the extend case...
627 * This feature is not really used anyway currently? */
628 node_socket_toggle(node, *sock, true);
629 changed = true;
630 }
631 if (!changed) {
632 sock = node_find_indicated_socket(snode, region, cursor, SOCK_OUT);
633 if (sock) {
634 node = &sock->owner_node();
635 found = true;
636 node_was_selected = node->flag & SELECT;
637
638 if (sock->flag & SELECT) {
639 if (extend) {
640 node_socket_deselect(node, *sock, true);
641 changed = true;
642 }
643 }
644 else {
645 /* Only allow one selected output per node, for sensible linking.
646 * Allow selecting outputs from different nodes though, if extend is true. */
647 for (bNodeSocket *tsock : node->output_sockets()) {
648 if (tsock == sock) {
649 continue;
650 }
651 node_socket_deselect(node, *tsock, true);
652 changed = true;
653 }
654 if (!extend) {
655 for (bNode *tnode : node_tree.all_nodes()) {
656 if (tnode == node) {
657 continue;
658 }
659 for (bNodeSocket *tsock : tnode->output_sockets()) {
660 node_socket_deselect(tnode, *tsock, true);
661 changed = true;
662 }
663 }
664 }
665 node_socket_select(node, *sock);
666 changed = true;
667 }
668 }
669 }
670 }
671
672 if (!sock) {
673
674 /* Find the closest visible node. */
675 node = node_under_mouse_get(snode, cursor);
676 found = (node != nullptr);
677 node_was_selected = node && (node->flag & SELECT);
678
679 if (params.sel_op == SEL_OP_SET) {
680 if ((found && params.select_passthrough) && (node->flag & SELECT)) {
681 found = false;
682 }
683 else if (found || params.deselect_all) {
684 /* Deselect everything. */
685 changed = node_deselect_all(node_tree);
686 }
687 }
688
689 if (found) {
690 switch (params.sel_op) {
691 case SEL_OP_ADD:
692 bke::node_set_selected(*node, true);
693 break;
694 case SEL_OP_SUB:
695 bke::node_set_selected(*node, false);
696 break;
697 case SEL_OP_XOR: {
698 /* Check active so clicking on an inactive node activates it. */
699 bool is_selected = (node->flag & NODE_SELECT) && (node->flag & NODE_ACTIVE);
700 bke::node_set_selected(*node, !is_selected);
701 break;
702 }
703 case SEL_OP_SET:
704 bke::node_set_selected(*node, true);
705 break;
706 case SEL_OP_AND:
707 /* Doesn't make sense for picking. */
709 break;
710 }
711
712 if (node->is_group_input()) {
713 handle_group_input_node_selection(node_tree, *node, cursor);
714 }
715 if (node->is_group_output()) {
716 handle_group_output_node_selection(node_tree, *node, cursor);
717 }
718
719 changed = true;
720 }
721 }
722
723 if (RNA_boolean_get(op->ptr, "clear_viewer")) {
724 if (node == nullptr) {
725 /* Disable existing active viewer. */
726 WorkSpace *workspace = CTX_wm_workspace(C);
727 if (const std::optional<viewer_path::ViewerPathForGeometryNodesViewer> parsed_path =
729 {
730 /* The object needs to be reevaluated, because the viewer path is changed which means that
731 * the object may generate different viewer geometry as a side effect. */
734 depsgraph, &parsed_path->object->id, ID_RECALC_GEOMETRY);
735 }
738 }
739 }
740
741 if (!(changed || found)) {
742 return false;
743 }
744
745 bool active_texture_changed = false;
746 bool viewer_node_changed = false;
747 if ((node != nullptr) && (node_was_selected == false || params.select_passthrough == false)) {
748 viewer_node_changed = (node->flag & NODE_DO_OUTPUT) == 0 &&
750 ED_node_set_active(&bmain, &snode, snode.edittree, node, &active_texture_changed);
751 }
752 else if (node != nullptr && node->type_legacy == GEO_NODE_VIEWER) {
753 viewer_path::activate_geometry_node(bmain, snode, *node);
754 }
756 tree_draw_order_update(node_tree);
757 if ((active_texture_changed && has_workbench_in_texture_color(wm, scene, ob)) ||
758 viewer_node_changed)
759 {
761 }
762
765
766 BKE_main_ensure_invariants(bmain, node_tree.id);
767
768 return true;
769}
770
772{
773 /* Get settings from RNA properties for operator. */
774 int2 mval;
775 RNA_int_get_array(op->ptr, "location", mval);
776
778
779 /* Perform the selection. */
780 const bool changed = node_mouse_select(C, op, mval, params);
781
782 if (changed) {
784 }
785 /* Nothing selected, just pass through. */
787}
788
790{
791 RNA_int_set_array(op->ptr, "location", event->mval);
792
793 const wmOperatorStatus retval = node_select_exec(C, op);
794
796}
797
799{
800 PropertyRNA *prop;
801
802 /* identifiers */
803 ot->name = "Select";
804 ot->idname = "NODE_OT_select";
805 ot->description = "Select the node under the cursor";
806
807 /* API callbacks. */
808 ot->exec = node_select_exec;
809 ot->invoke = node_select_invoke;
811 ot->get_name = ED_select_pick_get_name;
812
813 /* flags */
815
816 /* properties */
818
819 prop = RNA_def_int_vector(ot->srna,
820 "location",
821 2,
822 nullptr,
823 INT_MIN,
824 INT_MAX,
825 "Location",
826 "Mouse location",
827 INT_MIN,
828 INT_MAX);
830
831 RNA_def_boolean(ot->srna, "socket_select", false, "Socket Select", "");
832
833 RNA_def_boolean(ot->srna,
834 "clear_viewer",
835 false,
836 "Clear Viewer",
837 "Deactivate geometry nodes viewer when clicking in empty space");
838}
839
841
842/* -------------------------------------------------------------------- */
845
847{
848 SpaceNode &snode = *CTX_wm_space_node(C);
849 bNodeTree &node_tree = *snode.edittree;
850 const ARegion &region = *CTX_wm_region(C);
851 rctf rectf;
852
854 UI_view2d_region_to_view_rctf(&region.v2d, &rectf, &rectf);
855
856 const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
857 const bool select = (sel_op != SEL_OP_SUB);
858 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
859 node_deselect_all(node_tree);
860 }
861
862 for (bNode *node : node_tree.all_nodes()) {
863 bool is_inside = false;
864
865 switch (node->type_legacy) {
866 case NODE_FRAME: {
867 /* Frame nodes are selectable by their borders (including their whole rect - as for other
868 * nodes - would prevent selection of other nodes inside that frame. */
869 const rctf frame_inside = node_frame_rect_inside(snode, *node);
870 if (BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr) &&
871 !BLI_rctf_inside_rctf(&frame_inside, &rectf))
872 {
874 is_inside = true;
875 }
876 break;
877 }
878 default: {
879 is_inside = BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr);
880 break;
881 }
882 }
883
884 if (is_inside) {
886 }
887 }
888
889 tree_draw_order_update(node_tree);
890
893
894 return OPERATOR_FINISHED;
895}
896
898{
899 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
900
901 if (tweak && is_event_over_node_or_socket(*C, *event)) {
903 }
904
905 return WM_gesture_box_invoke(C, op, event);
906}
907
909{
910 /* identifiers */
911 ot->name = "Box Select";
912 ot->idname = "NODE_OT_select_box";
913 ot->description = "Use box selection to select nodes";
914
915 /* API callbacks. */
916 ot->invoke = node_box_select_invoke;
917 ot->exec = node_box_select_exec;
918 ot->modal = WM_gesture_box_modal;
919 ot->cancel = WM_gesture_box_cancel;
920
922
923 /* flags */
925
926 /* properties */
927 RNA_def_boolean(ot->srna,
928 "tweak",
929 false,
930 "Tweak",
931 "Only activate when mouse is not over a node (useful for tweak gesture)");
932
935}
936
938
939/* -------------------------------------------------------------------- */
942
944{
945 SpaceNode *snode = CTX_wm_space_node(C);
946 ARegion *region = CTX_wm_region(C);
947 bNodeTree &node_tree = *snode->edittree;
948
949 int x, y, radius;
950 float2 offset;
951
952 float zoom = float(BLI_rcti_size_x(&region->winrct)) / BLI_rctf_size_x(&region->v2d.cur);
953
954 const eSelectOp sel_op = ED_select_op_modal(
955 (eSelectOp)RNA_enum_get(op->ptr, "mode"),
957 const bool select = (sel_op != SEL_OP_SUB);
958 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
959 node_deselect_all(node_tree);
960 }
961
962 /* get operator properties */
963 x = RNA_int_get(op->ptr, "x");
964 y = RNA_int_get(op->ptr, "y");
965 radius = RNA_int_get(op->ptr, "radius");
966
967 UI_view2d_region_to_view(&region->v2d, x, y, &offset.x, &offset.y);
968
969 for (bNode *node : node_tree.all_nodes()) {
970 switch (node->type_legacy) {
971 case NODE_FRAME: {
972 /* Frame nodes are selectable by their borders (including their whole rect - as for other
973 * nodes - would prevent selection of _only_ other nodes inside that frame. */
974 rctf frame_inside = node_frame_rect_inside(*snode, *node);
975 const float radius_adjusted = float(radius) / zoom;
976 BLI_rctf_pad(&frame_inside, -2.0f * radius_adjusted, -2.0f * radius_adjusted);
977 if (BLI_rctf_isect_circle(&node->runtime->draw_bounds, offset, radius_adjusted) &&
978 !BLI_rctf_isect_circle(&frame_inside, offset, radius_adjusted))
979 {
981 }
982 break;
983 }
984 default: {
985 if (BLI_rctf_isect_circle(&node->runtime->draw_bounds, offset, radius / zoom)) {
987 }
988 break;
989 }
990 }
991 }
992
995
996 return OPERATOR_FINISHED;
997}
998
1000{
1001 /* identifiers */
1002 ot->name = "Circle Select";
1003 ot->idname = "NODE_OT_select_circle";
1004 ot->description = "Use circle selection to select nodes";
1005
1006 /* API callbacks. */
1007 ot->invoke = WM_gesture_circle_invoke;
1008 ot->exec = node_circleselect_exec;
1009 ot->modal = WM_gesture_circle_modal;
1011 ot->get_name = ED_select_circle_get_name;
1012
1013 /* flags */
1014 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1015
1016 /* properties */
1019}
1020
1022
1023/* -------------------------------------------------------------------- */
1026
1028{
1029 const bool tweak = RNA_boolean_get(op->ptr, "tweak");
1030
1031 if (tweak && is_event_over_node_or_socket(*C, *event)) {
1033 }
1034
1035 return WM_gesture_lasso_invoke(C, op, event);
1036}
1037
1038static bool do_lasso_select_node(bContext *C, const Span<int2> mcoords, eSelectOp sel_op)
1039{
1040 SpaceNode *snode = CTX_wm_space_node(C);
1041 bNodeTree &node_tree = *snode->edittree;
1042
1043 ARegion *region = CTX_wm_region(C);
1044
1045 rcti rect;
1046 bool changed = false;
1047
1048 const bool select = (sel_op != SEL_OP_SUB);
1049 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1050 node_deselect_all(node_tree);
1051 changed = true;
1052 }
1053
1054 /* Get rectangle from operator. */
1055 BLI_lasso_boundbox(&rect, mcoords);
1056
1057 for (bNode *node : node_tree.all_nodes()) {
1058 if (select && (node->flag & NODE_SELECT)) {
1059 continue;
1060 }
1061
1062 switch (node->type_legacy) {
1063 case NODE_FRAME: {
1064 /* Frame nodes are selectable by their borders (including their whole rect - as for other
1065 * nodes - would prevent selection of other nodes inside that frame. */
1066 rctf rectf;
1067 BLI_rctf_rcti_copy(&rectf, &rect);
1068 UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
1069 const rctf frame_inside = node_frame_rect_inside(*snode, *node);
1070 if (BLI_rctf_isect(&rectf, &node->runtime->draw_bounds, nullptr) &&
1071 !BLI_rctf_inside_rctf(&frame_inside, &rectf))
1072 {
1074 changed = true;
1075 }
1076 break;
1077 }
1078 default: {
1079 int2 screen_co;
1080 const float2 center = {BLI_rctf_cent_x(&node->runtime->draw_bounds),
1081 BLI_rctf_cent_y(&node->runtime->draw_bounds)};
1082
1083 /* marker in screen coords */
1085 &region->v2d, center.x, center.y, &screen_co.x, &screen_co.y) &&
1086 BLI_rcti_isect_pt(&rect, screen_co.x, screen_co.y) &&
1087 BLI_lasso_is_point_inside(mcoords, screen_co.x, screen_co.y, INT_MAX))
1088 {
1090 changed = true;
1091 }
1092 break;
1093 }
1094 }
1095 }
1096
1097 if (changed) {
1100 }
1101
1102 return changed;
1103}
1104
1106{
1107 const Array<int2> mcoords = WM_gesture_lasso_path_to_array(C, op);
1108
1109 if (mcoords.is_empty()) {
1110 return OPERATOR_PASS_THROUGH;
1111 }
1112
1113 const eSelectOp sel_op = (eSelectOp)RNA_enum_get(op->ptr, "mode");
1114
1115 do_lasso_select_node(C, mcoords, sel_op);
1116
1117 return OPERATOR_FINISHED;
1118}
1119
1121{
1122 /* identifiers */
1123 ot->name = "Lasso Select";
1124 ot->description = "Select nodes using lasso selection";
1125 ot->idname = "NODE_OT_select_lasso";
1126
1127 /* API callbacks. */
1128 ot->invoke = node_lasso_select_invoke;
1129 ot->modal = WM_gesture_lasso_modal;
1130 ot->exec = node_lasso_select_exec;
1132 ot->cancel = WM_gesture_lasso_cancel;
1133
1134 /* flags */
1136
1137 /* properties */
1138 RNA_def_boolean(ot->srna,
1139 "tweak",
1140 false,
1141 "Tweak",
1142 "Only activate when mouse is not over a node (useful for tweak gesture)");
1143
1146}
1147
1149
1150/* -------------------------------------------------------------------- */
1153
1154static bool any_node_selected(const bNodeTree &node_tree)
1155{
1156 for (const bNode *node : node_tree.all_nodes()) {
1157 if (node->flag & NODE_SELECT) {
1158 return true;
1159 }
1160 }
1161 return false;
1162}
1163
1165{
1166 SpaceNode &snode = *CTX_wm_space_node(C);
1167 bNodeTree &node_tree = *snode.edittree;
1168
1169 node_tree.ensure_topology_cache();
1170
1171 int action = RNA_enum_get(op->ptr, "action");
1172 if (action == SEL_TOGGLE) {
1173 if (any_node_selected(node_tree)) {
1174 action = SEL_DESELECT;
1175 }
1176 else {
1177 action = SEL_SELECT;
1178 }
1179 }
1180
1181 switch (action) {
1182 case SEL_SELECT:
1183 for (bNode *node : node_tree.all_nodes()) {
1184 bke::node_set_selected(*node, true);
1185 }
1186 break;
1187 case SEL_DESELECT:
1188 node_deselect_all(node_tree);
1189 break;
1190 case SEL_INVERT:
1191 for (bNode *node : node_tree.all_nodes()) {
1192 bke::node_set_selected(*node, !(node->flag & SELECT));
1193 }
1194 break;
1195 }
1196
1197 tree_draw_order_update(node_tree);
1198
1201 return OPERATOR_FINISHED;
1202}
1203
1205{
1206 /* identifiers */
1207 ot->name = "(De)select All";
1208 ot->description = "(De)select all nodes";
1209 ot->idname = "NODE_OT_select_all";
1210
1211 /* API callbacks. */
1212 ot->exec = node_select_all_exec;
1214
1215 /* flags */
1216 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1217
1219}
1220
1222
1223/* -------------------------------------------------------------------- */
1226
1228{
1229 SpaceNode &snode = *CTX_wm_space_node(C);
1230 bNodeTree &node_tree = *snode.edittree;
1231
1232 node_tree.ensure_topology_cache();
1233
1234 VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
1235
1236 for (bNode *node : initial_selection) {
1237 for (bNodeSocket *output_socket : node->output_sockets()) {
1238 if (!output_socket->is_available()) {
1239 continue;
1240 }
1241 for (bNodeSocket *input_socket : output_socket->directly_linked_sockets()) {
1242 if (!input_socket->is_available()) {
1243 continue;
1244 }
1245 bke::node_set_selected(input_socket->owner_node(), true);
1246 }
1247 }
1248 }
1249
1250 tree_draw_order_update(node_tree);
1251
1253 return OPERATOR_FINISHED;
1254}
1255
1257{
1258 /* identifiers */
1259 ot->name = "Select Linked To";
1260 ot->description = "Select nodes linked to the selected ones";
1261 ot->idname = "NODE_OT_select_linked_to";
1262
1263 /* API callbacks. */
1266
1267 /* flags */
1268 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1269}
1270
1272
1273/* -------------------------------------------------------------------- */
1276
1278{
1279 SpaceNode &snode = *CTX_wm_space_node(C);
1280 bNodeTree &node_tree = *snode.edittree;
1281
1282 node_tree.ensure_topology_cache();
1283
1284 VectorSet<bNode *> initial_selection = get_selected_nodes(node_tree);
1285
1286 for (bNode *node : initial_selection) {
1287 for (bNodeSocket *input_socket : node->input_sockets()) {
1288 if (!input_socket->is_available()) {
1289 continue;
1290 }
1291 for (bNodeSocket *output_socket : input_socket->directly_linked_sockets()) {
1292 if (!output_socket->is_available()) {
1293 continue;
1294 }
1295 bke::node_set_selected(output_socket->owner_node(), true);
1296 }
1297 }
1298 }
1299
1300 tree_draw_order_update(node_tree);
1301
1303 return OPERATOR_FINISHED;
1304}
1305
1307{
1308 /* identifiers */
1309 ot->name = "Select Linked From";
1310 ot->description = "Select nodes linked from the selected ones";
1311 ot->idname = "NODE_OT_select_linked_from";
1312
1313 /* API callbacks. */
1316
1317 /* flags */
1318 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1319}
1320
1322
1323/* -------------------------------------------------------------------- */
1326
1327static bool nodes_are_same_type_for_select(const bNode &a, const bNode &b)
1328{
1329 return a.type_legacy == b.type_legacy;
1330}
1331
1333{
1334 SpaceNode *snode = CTX_wm_space_node(C);
1335 ARegion *region = CTX_wm_region(C);
1336 const bool prev = RNA_boolean_get(op->ptr, "prev");
1337 bNode *active_node = bke::node_get_active(*snode->edittree);
1338
1339 if (active_node == nullptr) {
1340 return OPERATOR_CANCELLED;
1341 }
1342
1343 bNodeTree &node_tree = *snode->edittree;
1344 node_tree.ensure_topology_cache();
1345 if (node_tree.all_nodes().size() == 1) {
1346 return OPERATOR_CANCELLED;
1347 }
1348
1349 const Span<const bNode *> toposort = node_tree.toposort_left_to_right();
1350 const int index = toposort.first_index(active_node);
1351
1352 int new_index = index;
1353 while (true) {
1354 new_index += (prev ? -1 : 1);
1355 if (!toposort.index_range().contains(new_index)) {
1356 return OPERATOR_CANCELLED;
1357 }
1358 if (nodes_are_same_type_for_select(*toposort[new_index], *active_node)) {
1359 break;
1360 }
1361 }
1362
1363 bNode *new_active_node = node_tree.all_nodes()[toposort[new_index]->index()];
1364 if (new_active_node == active_node) {
1365 return OPERATOR_CANCELLED;
1366 }
1367
1368 node_select_single(*C, *new_active_node);
1369
1370 if (!BLI_rctf_inside_rctf(&region->v2d.cur, &new_active_node->runtime->draw_bounds)) {
1371 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
1372 space_node_view_flag(*C, *snode, *region, NODE_SELECT, smooth_viewtx);
1373 }
1374
1375 return OPERATOR_FINISHED;
1376}
1377
1379{
1380 /* identifiers */
1381 ot->name = "Activate Same Type Next/Prev";
1382 ot->description = "Activate and view same node type, step by step";
1383 ot->idname = "NODE_OT_select_same_type_step";
1384
1385 /* API callbacks. */
1388
1389 /* flags */
1390 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1391
1392 RNA_def_boolean(ot->srna, "prev", false, "Previous", "");
1393}
1394
1396
1397/* -------------------------------------------------------------------- */
1400
1401static std::string node_find_create_node_label(const bNodeTree &ntree, const bNode &node)
1402{
1403 std::string label = bke::node_label(ntree, node);
1404 if (label == node.name) {
1405 return label;
1406 }
1407 return fmt::format("{} ({})", label, node.name);
1408}
1409
1410static std::string node_find_create_group_input_label(const bNode &node, const bNodeSocket &socket)
1411{
1412 return fmt::format("{}: \"{}\" ({})", TIP_("Input"), socket.name, node.name);
1413}
1414
1415static std::string node_find_create_string_value(const bNode &node, const StringRef str)
1416{
1417 return fmt::format("{}: \"{}\" ({})", TIP_("String"), str, node.name);
1418}
1419
1420static std::string node_find_create_data_block_value(const bNode &node, const ID &id)
1421{
1422 const IDTypeInfo *type = BKE_idtype_get_info_from_id(&id);
1423 BLI_assert(type);
1424 StringRef type_name = TIP_(type->name);
1425 if (GS(id.name) == ID_NT) {
1426 type_name = TIP_("Node Group");
1427 }
1428 return fmt::format("{}: \"{}\" ({})", type_name, BKE_id_name(id), node.name);
1429}
1430
1431/* Generic search invoke. */
1432static void node_find_update_fn(const bContext *C,
1433 void * /*arg*/,
1434 const char *str,
1435 uiSearchItems *items,
1436 const bool /*is_first*/)
1437{
1438 SpaceNode *snode = CTX_wm_space_node(C);
1439
1440 struct Item {
1441 bNode *node;
1442 std::string search_str;
1443 };
1444
1447
1448 auto add_data_block_item = [&](bNode &node, const ID *id) {
1449 if (!id) {
1450 return;
1451 }
1452 const StringRef search_str = scope.add_value(node_find_create_data_block_value(node, *id));
1453 search.add(search_str, &scope.construct<Item>(Item{&node, search_str}));
1454 };
1455
1456 const bNodeTree &ntree = *snode->edittree;
1457 ntree.ensure_topology_cache();
1458 for (bNode *node : snode->edittree->all_nodes()) {
1459 const StringRef name = scope.add_value(node_find_create_node_label(ntree, *node));
1460 search.add(name, &scope.construct<Item>(Item{node, name}));
1461
1462 if (node->is_type("FunctionNodeInputString")) {
1463 const auto *storage = static_cast<const NodeInputString *>(node->storage);
1464 const StringRef value_str = storage->string;
1465 if (!value_str.is_empty()) {
1466 const StringRef search_str = scope.add_value(
1467 node_find_create_string_value(*node, value_str));
1468 search.add(search_str, &scope.construct<Item>(Item{node, search_str}));
1469 }
1470 }
1471 if (node->is_group_input()) {
1472 for (const bNodeSocket *socket : node->output_sockets().drop_back(1)) {
1473 if (!socket->is_directly_linked()) {
1474 continue;
1475 }
1476 const StringRef search_str = scope.add_value(
1477 node_find_create_group_input_label(*node, *socket));
1478 search.add(search_str, &scope.construct<Item>(Item{node, search_str}));
1479 }
1480 }
1481 if (node->id) {
1482 /* Avoid showing referenced node group data-blocks twice. */
1483 const bool skip_data_block =
1484 node->is_group() &&
1485 StringRef(bke::node_label(ntree, *node)).find(BKE_id_name(*node->id)) !=
1487 if (!skip_data_block) {
1488 add_data_block_item(*node, node->id);
1489 }
1490 }
1491
1492 for (const bNodeSocket *socket : node->input_sockets()) {
1493 switch (socket->type) {
1494 case SOCK_STRING: {
1495 if (socket->is_logically_linked()) {
1496 continue;
1497 }
1498 const bNodeSocketValueString *value =
1499 socket->default_value_typed<bNodeSocketValueString>();
1500 const StringRef value_str = value->value;
1501 if (!value_str.is_empty()) {
1502 const StringRef search_str = scope.add_value(
1503 node_find_create_string_value(*node, value_str));
1504 search.add(search_str, &scope.construct<Item>(Item{node, search_str}));
1505 }
1506 break;
1507 }
1508 case SOCK_OBJECT: {
1509 add_data_block_item(
1510 *node, id_cast<ID *>(socket->default_value_typed<bNodeSocketValueObject>()->value));
1511 break;
1512 }
1513 case SOCK_MATERIAL: {
1514 add_data_block_item(
1515 *node,
1516 id_cast<ID *>(socket->default_value_typed<bNodeSocketValueMaterial>()->value));
1517 break;
1518 }
1519 case SOCK_COLLECTION: {
1520 add_data_block_item(
1521 *node,
1522 id_cast<ID *>(socket->default_value_typed<bNodeSocketValueCollection>()->value));
1523 break;
1524 }
1525 case SOCK_IMAGE: {
1526 add_data_block_item(
1527 *node, id_cast<ID *>(socket->default_value_typed<bNodeSocketValueImage>()->value));
1528 break;
1529 }
1530 }
1531 }
1532 }
1533
1534 const Vector<Item *> filtered_items = search.query(str);
1535 for (const Item *item : filtered_items) {
1536 if (!UI_search_item_add(items, item->search_str, item->node, ICON_NONE, 0, 0)) {
1537 break;
1538 }
1539 }
1540}
1541
1542static void node_find_exec_fn(bContext *C, void * /*arg1*/, void *arg2)
1543{
1544 SpaceNode *snode = CTX_wm_space_node(C);
1545 bNode *active = (bNode *)arg2;
1546
1547 if (active) {
1548 ARegion *region = CTX_wm_region(C);
1550
1551 if (!BLI_rctf_inside_rctf(&region->v2d.cur, &active->runtime->draw_bounds)) {
1552 space_node_view_flag(*C, *snode, *region, NODE_SELECT, U.smooth_viewtx);
1553 }
1554 }
1555}
1556
1557static uiBlock *node_find_menu(bContext *C, ARegion *region, void *arg_optype)
1558{
1559 static char search[256] = "";
1560 uiBlock *block;
1561 uiBut *but;
1562 wmOperatorType *optype = (wmOperatorType *)arg_optype;
1563
1564 block = UI_block_begin(C, region, "_popup", ui::EmbossType::Emboss);
1567
1568 const int box_width = UI_searchbox_size_x_guess(C, node_find_update_fn, nullptr);
1569
1570 but = uiDefSearchBut(
1571 block, search, 0, ICON_VIEWZOOM, sizeof(search), 0, 0, box_width, UI_UNIT_Y, "");
1573 but, nullptr, node_find_update_fn, optype, false, nullptr, node_find_exec_fn, nullptr);
1575
1576 /* Fake button holds space for search items. */
1577 const int height = UI_searchbox_size_y() - UI_SEARCHBOX_BOUNDS;
1578 uiDefBut(
1579 block, ButType::Label, 0, "", 0, -height, box_width, height, nullptr, 0, 0, std::nullopt);
1580
1581 /* Move it downwards, mouse over button. */
1582 std::array<int, 2> bounds_offset = {0, -UI_UNIT_Y};
1583 UI_block_bounds_set_popup(block, UI_SEARCHBOX_BOUNDS, bounds_offset.data());
1584
1585 return block;
1586}
1587
1589 wmOperator *op,
1590 const wmEvent * /*event*/)
1591{
1593 return OPERATOR_CANCELLED;
1594}
1595
1597{
1598 /* identifiers */
1599 ot->name = "Find Node";
1600 ot->description = "Search for a node by name and focus and select it";
1601 ot->idname = "NODE_OT_find_node";
1602
1603 /* API callbacks. */
1604 ot->invoke = node_find_node_invoke;
1606
1607 /* flags */
1608 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1609}
1610
1612
1613} // 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)
const IDTypeInfo * BKE_idtype_get_info_from_id(const ID *id)
Definition idtype.cc:146
const char * BKE_id_name(const ID &id)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define NODE_FRAME
Definition BKE_node.hh:812
#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:614
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
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)
#define TIP_(msgid)
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:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_NT
Object groups, one object can be in many groups at once.
@ NODE_DO_OUTPUT
@ NODE_ACTIVE
@ NODE_SELECT
@ SOCK_OUT
@ SOCK_IN
@ SOCK_MATERIAL
@ SOCK_IMAGE
@ SOCK_COLLECTION
@ SOCK_OBJECT
@ SOCK_STRING
Object is a sort of wrapper for general info.
@ 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:728
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:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define UI_UNIT_Y
@ UI_BLOCK_SEARCH_MENU
@ UI_BLOCK_LOOP
@ UI_BLOCK_MOVEMOUSE_QUIT
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)
@ UI_BUT_ACTIVATE_ON_INIT
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition interface.cc:653
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)
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()
@ UI_BLOCK_THEME_STYLE_POPUP
void UI_block_flag_enable(uiBlock *block, int flag)
uiBut * uiDefBut(uiBlock *block, uiButTypeWithPointerType but_and_ptr_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_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:1702
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:1668
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1675
#define NC_NODE
Definition WM_types.hh:394
#define NC_VIEWER_PATH
Definition WM_types.hh:406
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_NODE_GIZMO
Definition WM_types.hh:516
#define NA_SELECTED
Definition WM_types.hh:589
#define U
BPy_StructRNA * depsgraph
bool is_empty() const
Definition BLI_array.hh:264
constexpr bool contains(int64_t value) const
T & construct(Args &&...args)
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
static constexpr int64_t not_found
constexpr int64_t find(char c, int64_t pos=0) const
constexpr bool is_empty() const
bool add(const Key &key)
void add(const StringRef str, T *user_data, const int weight=0)
Vector< T * > query(const StringRef query) const
nullptr float
#define SELECT
KDTree_3d * tree
#define str(s)
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:764
#define GS(x)
#define input
#define active
#define output
#define select(A, B, C)
float distance(VecOp< float, D >, VecOp< float, D >) RET
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
std::string node_label(const bNodeTree &ntree, const bNode &node)
Definition node.cc:4970
bNode * node_get_active(bNodeTree &ntree)
Definition node.cc:4685
Span< const bNodeZoneType * > all_zone_types()
bool node_set_selected(bNode &node, bool select)
Definition node.cc:4695
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)
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:316
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 std::string node_find_create_node_label(const bNodeTree &ntree, const bNode &node)
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:340
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)
static void activate_interface_socket(bNodeTree &tree, bNodeTreeInterfaceSocket &io_socket)
bNode * node_under_mouse_get(const SpaceNode &snode, const float2 mouse)
static void handle_group_output_node_selection(bNodeTree &tree, const bNode &group_output_node, const float2 &cursor)
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)
void NODE_OT_select_same_type_step(wmOperatorType *ot)
void node_select_single(bContext &C, bNode &node)
static std::string node_find_create_string_value(const bNode &node, const StringRef str)
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 *)
static std::string node_find_create_group_input_label(const bNode &node, const bNodeSocket &socket)
void NODE_OT_select_box(wmOperatorType *ot)
void NODE_OT_select_all(wmOperatorType *ot)
static std::string node_find_create_data_block_value(const bNode &node, const ID &id)
static wmOperatorStatus node_lasso_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static const bNodeSocket * find_socket_at_mouse_y(const Span< const bNodeSocket * > sockets, const float view_y)
static void handle_group_input_node_selection(bNodeTree &tree, const bNode &group_input_node, const float2 &cursor)
void activate_geometry_node(Main &bmain, SpaceNode &snode, bNode &node, std::optional< int > item_identifier=std::nullopt)
std::optional< ViewerPathForGeometryNodesViewer > parse_geometry_nodes_viewer(const ViewerPath &viewer_path)
T distance(const T &a, const T &b)
T max(const T &a, const T &b)
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
#define NODE_RESIZE_MARGIN
const char * name
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)
#define FLT_MAX
Definition stdcycles.h:14
const char * name
Definition DNA_ID.h:414
SpaceNode_Runtime * runtime
struct bNodeTree * edittree
ViewerPath viewer_path
struct Collection * value
ListBase inputs
struct ID * id
float color[3]
char name[64]
int16_t type_legacy
bNodeRuntimeHandle * runtime
void * storage
ListBase outputs
ListBase areabase
int mval[2]
Definition WM_types.hh:763
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:4237
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)