Blender  V3.3
link_drag_search.cc
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
3 #include "BLI_listbase.h"
4 #include "BLI_string_search.h"
5 
6 #include "DNA_space_types.h"
7 
8 #include "BKE_context.h"
9 
11 
12 #include "BLT_translation.h"
13 
14 #include "RNA_access.h"
15 
16 #include "WM_api.h"
17 
18 #include "ED_node.h"
19 
20 #include "node_intern.hh"
21 
23 
24 namespace blender::ed::space_node {
25 
31  char search[256];
32 
34  {
35  return static_cast<eNodeSocketInOut>(from_socket.in_out);
36  }
37 };
38 
40 {
41  bNode &reroute = params.add_node("NodeReroute");
42  if (params.socket.in_out == SOCK_IN) {
43  nodeAddLink(&params.node_tree,
44  &reroute,
45  static_cast<bNodeSocket *>(reroute.outputs.first),
46  &params.node,
47  &params.socket);
48  }
49  else {
50  nodeAddLink(&params.node_tree,
51  &params.node,
52  &params.socket,
53  &reroute,
54  static_cast<bNodeSocket *>(reroute.inputs.first));
55  }
56 }
57 
59 {
60  /* Add a group input based on the connected socket, and add a new group input node. */
62  &params.node_tree, &params.node, &params.socket);
63  const int group_input_index = BLI_findindex(&params.node_tree.inputs, interface_socket);
64 
65  bNode &group_input = params.add_node("NodeGroupInput");
66 
67  /* This is necessary to create the new sockets in the other input nodes. */
69 
70  /* Hide the new input in all other group input nodes, to avoid making them taller. */
71  LISTBASE_FOREACH (bNode *, node, &params.node_tree.nodes) {
72  if (node->type == NODE_GROUP_INPUT) {
73  bNodeSocket *new_group_input_socket = (bNodeSocket *)BLI_findlink(&node->outputs,
74  group_input_index);
75  new_group_input_socket->flag |= SOCK_HIDDEN;
76  }
77  }
78 
79  /* Hide all existing inputs in the new group input node, to only display the new one. */
80  LISTBASE_FOREACH (bNodeSocket *, socket, &group_input.outputs) {
81  socket->flag |= SOCK_HIDDEN;
82  }
83 
84  bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index);
85  if (socket == nullptr) {
86  /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */
87  return;
88  }
89  /* Unhide the socket for the new input in the new node and make a connection to it. */
90  socket->flag &= ~SOCK_HIDDEN;
91  nodeAddLink(&params.node_tree, &group_input, socket, &params.node, &params.socket);
92 }
93 
95  const bNodeSocket &interface_socket)
96 {
97  const int group_input_index = BLI_findindex(&params.node_tree.inputs, &interface_socket);
98  bNode &group_input = params.add_node("NodeGroupInput");
99 
100  LISTBASE_FOREACH (bNodeSocket *, socket, &group_input.outputs) {
101  socket->flag |= SOCK_HIDDEN;
102  }
103 
104  bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&group_input.outputs, group_input_index);
105  if (socket == nullptr) {
106  /* Adding sockets can fail in some cases. There's no good reason not to be safe here. */
107  return;
108  }
109 
110  socket->flag &= ~SOCK_HIDDEN;
111  nodeAddLink(&params.node_tree, &group_input, socket, &params.node, &params.socket);
112 }
113 
120  const bNodeSocket &socket,
121  Vector<SocketLinkOperation> &search_link_ops)
122 {
123  NODE_TYPES_BEGIN (node_type) {
124  const char *disabled_hint;
125  if (!(node_type->poll && node_type->poll(node_type, &node_tree, &disabled_hint))) {
126  continue;
127  }
128 
129  if (node_type->gather_link_search_ops) {
130  nodes::GatherLinkSearchOpParams params{*node_type, node_tree, socket, search_link_ops};
131  node_type->gather_link_search_ops(params);
132  }
133  }
135 
136  search_link_ops.append({IFACE_("Reroute"), add_reroute_node_fn});
137 
138  const bool is_node_group = !(node_tree.id.flag & LIB_EMBEDDED_DATA);
139 
140  if (is_node_group && socket.in_out == SOCK_IN) {
141  search_link_ops.append({IFACE_("Group Input"), add_group_input_node_fn});
142 
143  int weight = -1;
144  LISTBASE_FOREACH (const bNodeSocket *, interface_socket, &node_tree.inputs) {
145  eNodeSocketDatatype from = (eNodeSocketDatatype)interface_socket->type;
147  if (node_tree.typeinfo->validate_link && !node_tree.typeinfo->validate_link(from, to)) {
148  continue;
149  }
150  search_link_ops.append(
151  {std::string(IFACE_("Group Input ")) + UI_MENU_ARROW_SEP + interface_socket->name,
152  [interface_socket](nodes::LinkSearchOpParams &params) {
153  add_existing_group_input_fn(params, *interface_socket);
154  },
155  weight});
156  weight--;
157  }
158  }
159 }
160 
162  void *arg,
163  const char *str,
164  uiSearchItems *items,
165  const bool is_first)
166 {
167  LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg);
168 
170 
171  for (SocketLinkOperation &op : storage.search_link_ops) {
172  BLI_string_search_add(search, op.name.c_str(), &op, op.weight);
173  }
174 
175  /* Don't filter when the menu is first opened, but still run the search
176  * so the items are in the same order they will appear in while searching. */
177  const char *string = is_first ? "" : str;
178  SocketLinkOperation **filtered_items;
179  const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items);
180 
181  for (const int i : IndexRange(filtered_amount)) {
182  SocketLinkOperation &item = *filtered_items[i];
183  if (!UI_search_item_add(items, item.name.c_str(), &item, ICON_NONE, 0, 0)) {
184  break;
185  }
186  }
187 
188  MEM_freeN(filtered_items);
189  BLI_string_search_free(search);
190 }
191 
192 static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
193 {
194  Main &bmain = *CTX_data_main(C);
195  SpaceNode &snode = *CTX_wm_space_node(C);
196  LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg1);
197  SocketLinkOperation *item = static_cast<SocketLinkOperation *>(arg2);
198  if (item == nullptr) {
199  return;
200  }
201 
202  node_deselect_all(snode);
203 
204  Vector<bNode *> new_nodes;
206  *C, *snode.edittree, storage.from_node, storage.from_socket, new_nodes};
207  item->fn(params);
208  if (new_nodes.is_empty()) {
209  return;
210  }
211 
212  /* For now, assume that only one node is created by the callback. */
213  BLI_assert(new_nodes.size() == 1);
214  bNode *new_node = new_nodes.first();
215 
216  new_node->locx = storage.cursor.x / UI_DPI_FAC;
217  new_node->locy = storage.cursor.y / UI_DPI_FAC + 20 * UI_DPI_FAC;
218  if (storage.in_out() == SOCK_IN) {
219  new_node->locx -= new_node->width;
220  }
221 
222  nodeSetSelected(new_node, true);
223  nodeSetActive(snode.edittree, new_node);
224 
225  /* Ideally it would be possible to tag the node tree in some way so it updates only after the
226  * translate operation is finished, but normally moving nodes around doesn't cause updates. */
227  ED_node_tree_propagate_change(C, &bmain, snode.edittree);
228 
229  /* Start translation operator with the new node. */
230  wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true);
231  BLI_assert(ot);
232  PointerRNA ptr;
234  RNA_boolean_set(&ptr, "view2d_edge_pan", true);
237 }
238 
239 static void link_drag_search_free_fn(void *arg)
240 {
241  LinkDragSearchStorage *storage = static_cast<LinkDragSearchStorage *>(arg);
242  delete storage;
243 }
244 
245 static uiBlock *create_search_popup_block(bContext *C, ARegion *region, void *arg_op)
246 {
247  LinkDragSearchStorage &storage = *(LinkDragSearchStorage *)arg_op;
248 
251 
252  uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
255 
256  uiBut *but = uiDefSearchBut(block,
257  storage.search,
258  0,
259  ICON_VIEWZOOM,
260  sizeof(storage.search),
261  storage.in_out() == SOCK_OUT ? 10 : 10 - UI_searchbox_size_x(),
262  10,
264  UI_UNIT_Y,
265  0,
266  0,
267  "");
270  nullptr,
272  &storage,
273  false,
276  nullptr);
278 
279  /* Fake button to hold space for the search items. */
280  uiDefBut(block,
282  0,
283  "",
284  storage.in_out() == SOCK_OUT ? 10 : 10 - UI_searchbox_size_x(),
285  10 - UI_searchbox_size_y(),
288  nullptr,
289  0,
290  0,
291  0,
292  0,
293  nullptr);
294 
295  const int offset[2] = {0, -UI_UNIT_Y};
296  UI_block_bounds_set_popup(block, 0.3f * U.widget_unit, offset);
297  return block;
298 }
299 
301  bNode &node,
302  bNodeSocket &socket,
303  const float2 &cursor)
304 {
305  LinkDragSearchStorage *storage = new LinkDragSearchStorage{node, socket, cursor};
306  /* Use the "_ex" variant with `can_refresh` false to avoid a double free when closing Blender. */
307  UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false);
308 }
309 
310 } // namespace blender::ed::space_node
struct SpaceNode * CTX_wm_space_node(const bContext *C)
Definition: context.c:878
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
#define NODE_TYPES_BEGIN(ntype)
Definition: BKE_node.h:567
struct bNodeSocket * ntreeAddSocketInterfaceFromSocket(struct bNodeTree *ntree, struct bNode *from_node, struct bNodeSocket *from_sock)
Definition: node.cc:3382
struct bNodeLink * nodeAddLink(struct bNodeTree *ntree, struct bNode *fromnode, struct bNodeSocket *fromsock, struct bNode *tonode, struct bNodeSocket *tosock)
Definition: node.cc:2296
void nodeSetSelected(struct bNode *node, bool select)
Definition: node.cc:3615
#define NODE_GROUP_INPUT
Definition: BKE_node.h:987
#define NODE_TYPES_END
Definition: BKE_node.h:574
void nodeSetActive(struct bNodeTree *ntree, struct bNode *node)
Definition: node.cc:3644
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
int BLI_findindex(const struct ListBase *listbase, const void *vlink) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_string_search_add(StringSearch *search, const char *str, void *user_data, int weight)
void BLI_string_search_free(StringSearch *search)
StringSearch * BLI_string_search_new(void)
int BLI_string_search_query(StringSearch *search, const char *query, void ***r_data)
#define UNUSED(x)
#define IFACE_(msgid)
@ LIB_EMBEDDED_DATA
Definition: DNA_ID.h:635
eNodeSocketInOut
@ SOCK_OUT
@ SOCK_IN
@ SOCK_HIDDEN
eNodeSocketDatatype
void ED_node_tree_propagate_change(const struct bContext *C, struct Main *bmain, struct bNodeTree *ntree)
#define C
Definition: RandGen.cpp:25
#define UI_UNIT_Y
int UI_searchbox_size_x(void)
@ UI_BUT_ACTIVATE_ON_INIT
Definition: UI_interface.h:219
@ UI_EMBOSS
Definition: UI_interface.h:108
void UI_block_theme_style_set(uiBlock *block, char theme_style)
Definition: interface.cc:3634
int UI_searchbox_size_y(void)
void UI_popup_block_invoke_ex(struct bContext *C, uiBlockCreateFunc func, void *arg, uiFreeArgFunc arg_free, bool can_refresh)
uiBut * uiDefBut(uiBlock *block, int type, int retval, const char *str, int x, int y, short width, short height, void *poin, float min, float max, float a1, float a2, const char *tip)
Definition: interface.cc:4806
void UI_block_bounds_set_popup(uiBlock *block, int addval, const int bounds_offset[2])
Definition: interface.cc:598
@ UI_BLOCK_THEME_STYLE_POPUP
Definition: UI_interface.h:770
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)
Definition: interface.cc:6242
bool UI_search_item_add(uiSearchItems *items, const char *name, void *poin, int iconid, int but_flag, uint8_t name_prefix_offset)
#define UI_DPI_FAC
Definition: UI_interface.h:305
void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string)
Definition: interface.cc:6308
uiBlock * UI_block_begin(const struct bContext *C, struct ARegion *region, const char *name, eUIEmbossType emboss)
uiBut * uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxlen, int x, int y, short width, short height, float a1, float a2, const char *tip)
Definition: interface.cc:6217
void UI_block_flag_enable(uiBlock *block, int flag)
Definition: interface.cc:5848
@ UI_BTYPE_LABEL
Definition: UI_interface.h:354
void UI_but_flag_enable(uiBut *but, int flag)
Definition: interface.cc:5858
@ UI_BLOCK_SEARCH_MENU
Definition: UI_interface.h:147
@ UI_BLOCK_LOOP
Definition: UI_interface.h:135
@ UI_BLOCK_MOVEMOUSE_QUIT
Definition: UI_interface.h:143
@ WM_OP_INVOKE_DEFAULT
Definition: WM_types.h:201
unsigned int U
Definition: btGjkEpa3.h:78
int64_t size() const
Definition: BLI_vector.hh:694
void append(const T &value)
Definition: BLI_vector.hh:433
bool is_empty() const
Definition: BLI_vector.hh:706
const T & first() const
Definition: BLI_vector.hh:680
OperationNode * node
StackEntry * from
#define str(s)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void invoke_node_link_drag_add_menu(bContext &C, bNode &node, bNodeSocket &socket, const float2 &cursor)
static void link_drag_search_update_fn(const bContext *UNUSED(C), void *arg, const char *str, uiSearchItems *items, const bool is_first)
static void add_reroute_node_fn(nodes::LinkSearchOpParams &params)
static uiBlock * create_search_popup_block(bContext *C, ARegion *region, void *arg_op)
static void add_existing_group_input_fn(nodes::LinkSearchOpParams &params, const bNodeSocket &interface_socket)
void node_deselect_all(SpaceNode &snode)
Definition: node_select.cc:250
static void link_drag_search_free_fn(void *arg)
static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
static void gather_socket_link_operations(bNodeTree &node_tree, const bNodeSocket &socket, Vector< SocketLinkOperation > &search_link_ops)
struct node_tree node_tree
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
#define UI_MENU_ARROW_SEP
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
struct bNodeTree * edittree
float locy
float width
ListBase inputs
float locx
ListBase outputs
Vector< SocketLinkOperation > search_link_ops
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
PointerRNA * ptr
Definition: wm_files.c:3480
wmOperatorType * ot
Definition: wm_files.c:3479
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
Definition: wm_operators.c:661
void WM_operator_properties_free(PointerRNA *ptr)
Definition: wm_operators.c:783