Blender V4.3
interface_template_node_tree_interface.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_context.hh"
12
13#include "BLI_color.hh"
14#include "BLI_string.h"
15
16#include "BLT_translation.hh"
17
19
20#include "ED_node.hh"
21#include "ED_undo.hh"
22
23#include "RNA_access.hh"
24#include "RNA_prototypes.hh"
25
26#include "UI_interface.hh"
27#include "UI_resources.hh"
28#include "UI_tree_view.hh"
29
30#include "WM_api.hh"
31
33
34namespace blender::ui::nodes {
35
39
40namespace {
41
42class NodePanelViewItem;
43class NodeSocketViewItem;
44class NodeTreeInterfaceView;
45
46class NodeTreeInterfaceDragController : public AbstractViewItemDragController {
47 private:
49
50 public:
51 explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view,
53 virtual ~NodeTreeInterfaceDragController() = default;
54
55 eWM_DragDataType get_drag_type() const;
56
57 void *create_drag_data() const;
58};
59
60class NodeSocketDropTarget : public TreeViewItemDropTarget {
61 private:
63
64 public:
65 explicit NodeSocketDropTarget(NodeSocketViewItem &item, bNodeTreeInterfaceSocket &socket);
66
67 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
68 std::string drop_tooltip(const DragInfo &drag_info) const override;
69 bool on_drop(bContext * /*C*/, const DragInfo &drag_info) const override;
70
71 protected:
72 wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const;
73};
74
75class NodePanelDropTarget : public TreeViewItemDropTarget {
76 private:
78
79 public:
80 explicit NodePanelDropTarget(NodePanelViewItem &item, bNodeTreeInterfacePanel &panel);
81
82 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
83 std::string drop_tooltip(const DragInfo &drag_info) const override;
84 bool on_drop(bContext *C, const DragInfo &drag_info) const override;
85
86 protected:
87 wmDragNodeTreeInterface *get_drag_node_tree_declaration(const wmDrag &drag) const;
88};
89
90class NodeSocketViewItem : public BasicTreeViewItem {
91 private:
92 bNodeTree &nodetree_;
94
95 public:
96 NodeSocketViewItem(bNodeTree &nodetree,
97 bNodeTreeInterface &interface,
99 : BasicTreeViewItem(socket.name, ICON_NONE), nodetree_(nodetree), socket_(socket)
100 {
101 set_is_active_fn([interface, &socket]() { return interface.active_item() == &socket.item; });
102 set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
103 NodeSocketViewItem &self = static_cast<NodeSocketViewItem &>(new_active);
104 interface.active_item_set(&self.socket_.item);
105 });
106 }
107
108 void build_row(uiLayout &row) override
109 {
110 uiLayoutSetPropDecorate(&row, false);
111
112 uiLayout *input_socket_layout = uiLayoutRow(&row, true);
113 if (socket_.flag & NODE_INTERFACE_SOCKET_INPUT) {
114 /* XXX Socket template only draws in embossed layouts (Julian). */
115 uiLayoutSetEmboss(input_socket_layout, UI_EMBOSS);
116 /* Context is not used by the template function. */
117 uiTemplateNodeSocket(input_socket_layout, /*C*/ nullptr, socket_.socket_color());
118 }
119 else {
120 /* Blank item to align output socket labels with inputs. */
121 uiItemL(input_socket_layout, "", ICON_BLANK1);
122 }
123
124 this->add_label(row);
125
126 uiLayout *output_socket_layout = uiLayoutRow(&row, true);
127 if (socket_.flag & NODE_INTERFACE_SOCKET_OUTPUT) {
128 /* XXX Socket template only draws in embossed layouts (Julian). */
129 uiLayoutSetEmboss(output_socket_layout, UI_EMBOSS);
130 /* Context is not used by the template function. */
131 uiTemplateNodeSocket(output_socket_layout, /*C*/ nullptr, socket_.socket_color());
132 }
133 else {
134 /* Blank item to align input socket labels with outputs. */
135 uiItemL(output_socket_layout, "", ICON_BLANK1);
136 }
137 }
138
139 protected:
140 bool matches(const AbstractViewItem &other) const override
141 {
142 const NodeSocketViewItem *other_item = dynamic_cast<const NodeSocketViewItem *>(&other);
143 if (other_item == nullptr) {
144 return false;
145 }
146
147 return &socket_ == &other_item->socket_;
148 }
149
150 bool supports_renaming() const override
151 {
152 return true;
153 }
154 bool rename(const bContext &C, StringRefNull new_name) override
155 {
156 MEM_SAFE_FREE(socket_.name);
157
158 socket_.name = BLI_strdup(new_name.c_str());
159 nodetree_.tree_interface.tag_items_changed();
160 ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_);
161 ED_undo_push(&const_cast<bContext &>(C), new_name.c_str());
162 return true;
163 }
164 StringRef get_rename_string() const override
165 {
166 return socket_.name;
167 }
168
169 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override;
170 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override;
171};
172
173class NodePanelViewItem : public BasicTreeViewItem {
174 private:
175 bNodeTree &nodetree_;
177
178 public:
179 NodePanelViewItem(bNodeTree &nodetree,
180 bNodeTreeInterface &interface,
182 : BasicTreeViewItem(panel.name, ICON_NONE), nodetree_(nodetree), panel_(panel)
183 {
184 set_is_active_fn([interface, &panel]() { return interface.active_item() == &panel.item; });
185 set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
186 NodePanelViewItem &self = static_cast<NodePanelViewItem &>(new_active);
187 interface.active_item_set(&self.panel_.item);
188 });
189 }
190
191 void build_row(uiLayout &row) override
192 {
193 this->add_label(row);
194
195 uiLayout *sub = uiLayoutRow(&row, true);
196 uiLayoutSetPropDecorate(sub, false);
197 }
198
199 protected:
200 bool matches(const AbstractViewItem &other) const override
201 {
202 const NodePanelViewItem *other_item = dynamic_cast<const NodePanelViewItem *>(&other);
203 if (other_item == nullptr) {
204 return false;
205 }
206
207 return &panel_ == &other_item->panel_;
208 }
209
210 bool supports_renaming() const override
211 {
212 return true;
213 }
214 bool rename(const bContext &C, StringRefNull new_name) override
215 {
216 MEM_SAFE_FREE(panel_.name);
217
218 panel_.name = BLI_strdup(new_name.c_str());
219 nodetree_.tree_interface.tag_items_changed();
220 ED_node_tree_propagate_change(&C, CTX_data_main(&C), &nodetree_);
221 return true;
222 }
223 StringRef get_rename_string() const override
224 {
225 return panel_.name;
226 }
227
228 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override;
229 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override;
230};
231
232class NodeTreeInterfaceView : public AbstractTreeView {
233 private:
234 bNodeTree &nodetree_;
235 bNodeTreeInterface &interface_;
236
237 public:
238 explicit NodeTreeInterfaceView(bNodeTree &nodetree, bNodeTreeInterface &interface)
239 : nodetree_(nodetree), interface_(interface)
240 {
241 }
242
243 bNodeTree &nodetree()
244 {
245 return nodetree_;
246 }
247
248 bNodeTreeInterface &interface()
249 {
250 return interface_;
251 }
252
253 void build_tree() override
254 {
255 /* Draw root items */
256 this->add_items_for_panel_recursive(interface_.root_panel, *this);
257 }
258
259 protected:
260 void add_items_for_panel_recursive(bNodeTreeInterfacePanel &parent,
261 ui::TreeViewOrItem &parent_item)
262 {
263 for (bNodeTreeInterfaceItem *item : parent.items()) {
264 switch (item->item_type) {
267 item);
268 NodeSocketViewItem &socket_item = parent_item.add_tree_item<NodeSocketViewItem>(
269 nodetree_, interface_, *socket);
270 socket_item.uncollapse_by_default();
271 break;
272 }
275 item);
276 NodePanelViewItem &panel_item = parent_item.add_tree_item<NodePanelViewItem>(
277 nodetree_, interface_, *panel);
278 panel_item.uncollapse_by_default();
279 add_items_for_panel_recursive(*panel, panel_item);
280 break;
281 }
282 }
283 }
284 }
285};
286
287std::unique_ptr<AbstractViewItemDragController> NodeSocketViewItem::create_drag_controller() const
288{
289 return std::make_unique<NodeTreeInterfaceDragController>(
290 static_cast<NodeTreeInterfaceView &>(this->get_tree_view()), socket_.item);
291}
292
293std::unique_ptr<TreeViewItemDropTarget> NodeSocketViewItem::create_drop_target()
294{
295 return std::make_unique<NodeSocketDropTarget>(*this, socket_);
296}
297
298std::unique_ptr<AbstractViewItemDragController> NodePanelViewItem::create_drag_controller() const
299{
300 return std::make_unique<NodeTreeInterfaceDragController>(
301 static_cast<NodeTreeInterfaceView &>(this->get_tree_view()), panel_.item);
302}
303
304std::unique_ptr<TreeViewItemDropTarget> NodePanelViewItem::create_drop_target()
305{
306 return std::make_unique<NodePanelDropTarget>(*this, panel_);
307}
308
309NodeTreeInterfaceDragController::NodeTreeInterfaceDragController(NodeTreeInterfaceView &view,
311 : AbstractViewItemDragController(view), item_(item)
312{
313}
314
315eWM_DragDataType NodeTreeInterfaceDragController::get_drag_type() const
316{
318}
319
320void *NodeTreeInterfaceDragController::create_drag_data() const
321{
322 wmDragNodeTreeInterface *drag_data = MEM_cnew<wmDragNodeTreeInterface>(__func__);
323 drag_data->item = &item_;
324 return drag_data;
325}
326
327NodeSocketDropTarget::NodeSocketDropTarget(NodeSocketViewItem &item,
329 : TreeViewItemDropTarget(item, DropBehavior::Reorder), socket_(socket)
330{
331}
332
333bool NodeSocketDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const
334{
335 if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) {
336 return false;
337 }
338 wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag);
339
340 /* Can't drop an item onto its children. */
342 drag_data->item))
343 {
344 if (panel->contains(socket_.item)) {
345 return false;
346 }
347 }
348 return true;
349}
350
351std::string NodeSocketDropTarget::drop_tooltip(const DragInfo &drag_info) const
352{
353 switch (drag_info.drop_location) {
354 case DropLocation::Into:
355 return "";
356 case DropLocation::Before:
357 return TIP_("Insert before socket");
358 case DropLocation::After:
359 return TIP_("Insert after socket");
360 }
361 return "";
362}
363
364bool NodeSocketDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const
365{
366 wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data);
367 BLI_assert(drag_data != nullptr);
368 bNodeTreeInterfaceItem *drag_item = drag_data->item;
369 BLI_assert(drag_item != nullptr);
370
371 bNodeTree &nodetree = this->get_view<NodeTreeInterfaceView>().nodetree();
372 bNodeTreeInterface &interface = this->get_view<NodeTreeInterfaceView>().interface();
373
374 bNodeTreeInterfacePanel *parent = interface.find_item_parent(socket_.item, true);
375 int index = -1;
376
377 /* Insert into same panel as the target. */
378 BLI_assert(parent != nullptr);
379 switch (drag_info.drop_location) {
380 case DropLocation::Before:
381 index = parent->items().as_span().first_index_try(&socket_.item);
382 break;
383 case DropLocation::After:
384 index = parent->items().as_span().first_index_try(&socket_.item) + 1;
385 break;
386 default:
387 /* All valid cases should be handled above. */
389 break;
390 }
391 if (parent == nullptr || index < 0) {
392 return false;
393 }
394
395 interface.move_item_to_parent(*drag_item, parent, index);
396
397 /* General update */
399 ED_undo_push(C, "Insert node group item");
400 return true;
401}
402
403wmDragNodeTreeInterface *NodeSocketDropTarget::get_drag_node_tree_declaration(
404 const wmDrag &drag) const
405{
407 return static_cast<wmDragNodeTreeInterface *>(drag.poin);
408}
409
410NodePanelDropTarget::NodePanelDropTarget(NodePanelViewItem &item, bNodeTreeInterfacePanel &panel)
411 : TreeViewItemDropTarget(item, DropBehavior::ReorderAndInsert), panel_(panel)
412{
413}
414
415bool NodePanelDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const
416{
417 if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) {
418 return false;
419 }
420 wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag);
421
422 /* Can't drop an item onto its children. */
424 drag_data->item))
425 {
426 if (panel->contains(panel_.item)) {
427 return false;
428 }
429 }
430
431 return true;
432}
433
434std::string NodePanelDropTarget::drop_tooltip(const DragInfo &drag_info) const
435{
436 switch (drag_info.drop_location) {
437 case DropLocation::Into:
438 return TIP_("Insert into panel");
439 case DropLocation::Before:
440 return TIP_("Insert before panel");
441 case DropLocation::After:
442 return TIP_("Insert after panel");
443 }
444 return "";
445}
446
447bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const
448{
449 wmDragNodeTreeInterface *drag_data = get_drag_node_tree_declaration(drag_info.drag_data);
450 BLI_assert(drag_data != nullptr);
451 bNodeTreeInterfaceItem *drag_item = drag_data->item;
452 BLI_assert(drag_item != nullptr);
453
454 bNodeTree &nodetree = get_view<NodeTreeInterfaceView>().nodetree();
455 bNodeTreeInterface &interface = get_view<NodeTreeInterfaceView>().interface();
456
457 bNodeTreeInterfacePanel *parent = nullptr;
458 int index = -1;
459 switch (drag_info.drop_location) {
460 case DropLocation::Into: {
461 /* Insert into target */
462 parent = &panel_;
463 index = 0;
464 break;
465 }
466 case DropLocation::Before: {
467 /* Insert into same panel as the target. */
468 parent = interface.find_item_parent(panel_.item, true);
469 BLI_assert(parent != nullptr);
470 index = parent->items().as_span().first_index_try(&panel_.item);
471 break;
472 }
473 case DropLocation::After: {
474 /* Insert into same panel as the target. */
475 parent = interface.find_item_parent(panel_.item, true);
476 BLI_assert(parent != nullptr);
477 index = parent->items().as_span().first_index_try(&panel_.item) + 1;
478 break;
479 }
480 }
481 if (parent == nullptr || index < 0) {
482 return false;
483 }
484
485 interface.move_item_to_parent(*drag_item, parent, index);
486
487 /* General update */
489 ED_undo_push(C, "Insert node group item");
490 return true;
491}
492
493wmDragNodeTreeInterface *NodePanelDropTarget::get_drag_node_tree_declaration(
494 const wmDrag &drag) const
495{
497 return static_cast<wmDragNodeTreeInterface *>(drag.poin);
498}
499
500} // namespace
501
502} // namespace blender::ui::nodes
503
505{
506 if (!ptr->data) {
507 return;
508 }
509 if (!RNA_struct_is_a(ptr->type, &RNA_NodeTreeInterface)) {
510 return;
511 }
512 bNodeTree &nodetree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
513 bNodeTreeInterface &interface = *static_cast<bNodeTreeInterface *>(ptr->data);
514
515 uiBlock *block = uiLayoutGetBlock(layout);
516
518 *block,
519 "Node Tree Declaration Tree View",
520 std::make_unique<blender::ui::nodes::NodeTreeInterfaceView>(nodetree, interface));
521 tree_view->set_context_menu_title("Node Tree Interface");
522 tree_view->set_default_rows(3);
523
525}
Main * CTX_data_main(const bContext *C)
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define TIP_(msgid)
void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *ntree)
Definition node_edit.cc:492
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:104
#define MEM_SAFE_FREE(v)
blender::ui::AbstractGridView * UI_block_add_view(uiBlock &block, blender::StringRef idname, std::unique_ptr< blender::ui::AbstractGridView > grid_view)
void uiTemplateNodeTreeInterface(uiLayout *layout, PointerRNA *ptr)
@ UI_EMBOSS
void uiItemL(uiLayout *layout, const char *name, int icon)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiTemplateNodeSocket(uiLayout *layout, bContext *C, const float color[4])
void uiLayoutSetEmboss(uiLayout *layout, eUIEmbossType emboss)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
eWM_DragDataType
Definition WM_types.hh:1152
@ WM_DRAG_NODE_TREE_INTERFACE
Definition WM_types.hh:1175
PyObject * self
void set_default_rows(int default_rows)
Definition tree_view.cc:129
void set_context_menu_title(const std::string &title)
static void build_tree_view(AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, bool add_box=true)
Definition tree_view.cc:918
T & get_item_as(bNodeTreeInterfaceItem &item)
TreeViewItemContainer TreeViewOrItem
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
ID * owner_id
Definition RNA_types.hh:40
StructRNA * type
Definition RNA_types.hh:41
void * data
Definition RNA_types.hh:42
bNodeTreeInterfacePanel root_panel
bNodeTreeInterface tree_interface
eWM_DragDataType type
Definition WM_types.hh:1282
void * poin
Definition WM_types.hh:1283
PointerRNA * ptr
Definition wm_files.cc:4126