Blender V4.5
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
8
9#include "BKE_context.hh"
12
13#include "BLI_string.h"
14
15#include "BLT_translation.hh"
16
18
19#include "ED_node.hh"
20#include "ED_undo.hh"
21
22#include "RNA_access.hh"
23#include "RNA_prototypes.hh"
24
25#include "UI_interface.hh"
26#include "UI_resources.hh"
27#include "UI_tree_view.hh"
28
29#include "WM_api.hh"
30
32
33namespace blender::ui::nodes {
34
35namespace {
36
37using node_interface::bNodeTreeInterfaceItemReference;
38
39class NodePanelViewItem;
40class NodeSocketViewItem;
41class NodeTreeInterfaceView;
42
43class NodeTreeInterfaceDragController : public AbstractViewItemDragController {
44 private:
46 bNodeTree &tree_;
47
48 public:
49 explicit NodeTreeInterfaceDragController(NodeTreeInterfaceView &view,
52 ~NodeTreeInterfaceDragController() override = default;
53
54 eWM_DragDataType get_drag_type() const override;
55
56 void *create_drag_data() const override;
57};
58
59class NodeSocketDropTarget : public TreeViewItemDropTarget {
60 private:
62
63 public:
64 explicit NodeSocketDropTarget(NodeSocketViewItem &item, bNodeTreeInterfaceSocket &socket);
65
66 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
67 std::string drop_tooltip(const DragInfo &drag_info) const override;
68 bool on_drop(bContext * /*C*/, const DragInfo &drag_info) const override;
69};
70
71class NodePanelDropTarget : public TreeViewItemDropTarget {
72 private:
74
75 public:
76 explicit NodePanelDropTarget(NodePanelViewItem &item, bNodeTreeInterfacePanel &panel);
77
78 bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override;
79 std::string drop_tooltip(const DragInfo &drag_info) const override;
80 bool on_drop(bContext *C, const DragInfo &drag_info) const override;
81};
82
83class NodeSocketViewItem : public BasicTreeViewItem {
84 private:
85 bNodeTree &nodetree_;
87
88 public:
89 NodeSocketViewItem(bNodeTree &nodetree,
90 bNodeTreeInterface &interface,
92 : BasicTreeViewItem(socket.name, ICON_NONE), nodetree_(nodetree), socket_(socket)
93 {
94 set_is_active_fn([interface, &socket]() { return interface.active_item() == &socket.item; });
95 set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
96 NodeSocketViewItem &self = static_cast<NodeSocketViewItem &>(new_active);
97 interface.active_item_set(&self.socket_.item);
98 });
99 }
100
101 void build_row(uiLayout &row) override
102 {
103 if (ID_IS_LINKED(&nodetree_)) {
104 uiLayoutSetEnabled(&row, false);
105 }
106
107 uiLayoutSetPropDecorate(&row, false);
108
109 uiLayout *input_socket_layout = &row.row(true);
110 if (socket_.flag & NODE_INTERFACE_SOCKET_INPUT) {
111 /* XXX Socket template only draws in embossed layouts (Julian). */
113 /* Context is not used by the template function. */
114 uiTemplateNodeSocket(input_socket_layout, /*C*/ nullptr, socket_.socket_color());
115 }
116 else {
117 /* Blank item to align output socket labels with inputs. */
118 input_socket_layout->label("", ICON_BLANK1);
119 }
120
121 this->add_label(row);
122
123 uiLayout *output_socket_layout = &row.row(true);
124 if (socket_.flag & NODE_INTERFACE_SOCKET_OUTPUT) {
125 /* XXX Socket template only draws in embossed layouts (Julian). */
127 /* Context is not used by the template function. */
128 uiTemplateNodeSocket(output_socket_layout, /*C*/ nullptr, socket_.socket_color());
129 }
130 else {
131 /* Blank item to align input socket labels with outputs. */
132 output_socket_layout->label("", ICON_BLANK1);
133 }
134 }
135
136 protected:
137 bool matches(const AbstractViewItem &other) const override
138 {
139 const NodeSocketViewItem *other_item = dynamic_cast<const NodeSocketViewItem *>(&other);
140 if (other_item == nullptr) {
141 return false;
142 }
143
144 return &socket_ == &other_item->socket_;
145 }
146
147 bool supports_renaming() const override
148 {
149 return !ID_IS_LINKED(&nodetree_);
150 }
151 bool rename(const bContext &C, StringRefNull new_name) override
152 {
153 MEM_SAFE_FREE(socket_.name);
154
155 socket_.name = BLI_strdup(new_name.c_str());
156 nodetree_.tree_interface.tag_item_property_changed();
158 ED_undo_push(&const_cast<bContext &>(C), new_name.c_str());
159 return true;
160 }
161 StringRef get_rename_string() const override
162 {
163 return socket_.name;
164 }
165
166 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override;
167 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override;
168};
169
170class NodePanelViewItem : public BasicTreeViewItem {
171 private:
172 bNodeTree &nodetree_;
174 const bNodeTreeInterfaceSocket *toggle_ = nullptr;
175
176 public:
177 NodePanelViewItem(bNodeTree &nodetree,
178 bNodeTreeInterface &interface,
180 : BasicTreeViewItem(panel.name, ICON_NONE), nodetree_(nodetree), panel_(panel)
181 {
182 set_is_active_fn([interface, &panel]() { return interface.active_item() == &panel.item; });
183 set_on_activate_fn([&interface](bContext & /*C*/, BasicTreeViewItem &new_active) {
184 NodePanelViewItem &self = static_cast<NodePanelViewItem &>(new_active);
185 interface.active_item_set(&self.panel_.item);
186 });
187 toggle_ = panel.header_toggle_socket();
188 is_always_collapsible_ = true;
189 }
190
191 void build_row(uiLayout &row) override
192 {
193 if (ID_IS_LINKED(&nodetree_)) {
194 uiLayoutSetEnabled(&row, false);
195 }
196 /* Add boolean socket if panel has a toggle. */
197 if (toggle_ != nullptr) {
198 uiLayout *toggle_layout = &row.row(true);
199 /* XXX Socket template only draws in embossed layouts (Julian). */
201 /* Context is not used by the template function. */
202 uiTemplateNodeSocket(toggle_layout, /*C*/ nullptr, toggle_->socket_color());
203 }
204
205 this->add_label(row);
206
207 uiLayout *sub = &row.row(true);
208 uiLayoutSetPropDecorate(sub, false);
209 }
210
211 protected:
212 bool matches(const AbstractViewItem &other) const override
213 {
214 const NodePanelViewItem *other_item = dynamic_cast<const NodePanelViewItem *>(&other);
215 if (other_item == nullptr) {
216 return false;
217 }
218
219 return &panel_ == &other_item->panel_;
220 }
221
222 bool supports_renaming() const override
223 {
224 return !ID_IS_LINKED(&nodetree_);
225 }
226 bool rename(const bContext &C, StringRefNull new_name) override
227 {
228 PointerRNA panel_ptr = RNA_pointer_create_discrete(
229 &nodetree_.id, &RNA_NodeTreeInterfacePanel, &panel_);
230 PropertyRNA *name_prop = RNA_struct_find_property(&panel_ptr, "name");
231 RNA_property_string_set(&panel_ptr, name_prop, new_name.c_str());
232 RNA_property_update(const_cast<bContext *>(&C), &panel_ptr, name_prop);
233 return true;
234 }
235 StringRef get_rename_string() const override
236 {
237 return panel_.name;
238 }
239
240 std::unique_ptr<AbstractViewItemDragController> create_drag_controller() const override;
241 std::unique_ptr<TreeViewItemDropTarget> create_drop_target() override;
242};
243
244class NodeTreeInterfaceView : public AbstractTreeView {
245 private:
246 bNodeTree &nodetree_;
247 bNodeTreeInterface &interface_;
248
249 public:
250 explicit NodeTreeInterfaceView(bNodeTree &nodetree, bNodeTreeInterface &interface)
251 : nodetree_(nodetree), interface_(interface)
252 {
253 }
254
255 bNodeTree &nodetree()
256 {
257 return nodetree_;
258 }
259
261 {
262 return interface_;
263 }
264
265 void build_tree() override
266 {
267 /* Draw root items */
268 this->add_items_for_panel_recursive(interface_.root_panel, *this);
269 }
270
271 protected:
272 void add_items_for_panel_recursive(bNodeTreeInterfacePanel &parent,
273 ui::TreeViewOrItem &parent_item,
274 const bNodeTreeInterfaceItem *skip_item = nullptr)
275 {
276 for (bNodeTreeInterfaceItem *item : parent.items()) {
277 if (item == skip_item) {
278 continue;
279 }
280 switch (NodeTreeInterfaceItemType(item->item_type)) {
283 item);
284 NodeSocketViewItem &socket_item = parent_item.add_tree_item<NodeSocketViewItem>(
285 nodetree_, interface_, *socket);
286 socket_item.uncollapse_by_default();
287 break;
288 }
291 item);
292 NodePanelViewItem &panel_item = parent_item.add_tree_item<NodePanelViewItem>(
293 nodetree_, interface_, *panel);
294 panel_item.uncollapse_by_default();
295 /* Skip over sockets which are a panel toggle. */
296 const bNodeTreeInterfaceSocket *skip_item = panel->header_toggle_socket();
297 add_items_for_panel_recursive(
298 *panel, panel_item, reinterpret_cast<const bNodeTreeInterfaceItem *>(skip_item));
299 break;
300 }
301 }
302 }
303 }
304};
305
306std::unique_ptr<AbstractViewItemDragController> NodeSocketViewItem::create_drag_controller() const
307{
308 return std::make_unique<NodeTreeInterfaceDragController>(
309 static_cast<NodeTreeInterfaceView &>(this->get_tree_view()), socket_.item, nodetree_);
310}
311
312std::unique_ptr<TreeViewItemDropTarget> NodeSocketViewItem::create_drop_target()
313{
314 return std::make_unique<NodeSocketDropTarget>(*this, socket_);
315}
316
317std::unique_ptr<AbstractViewItemDragController> NodePanelViewItem::create_drag_controller() const
318{
319 return std::make_unique<NodeTreeInterfaceDragController>(
320 static_cast<NodeTreeInterfaceView &>(this->get_tree_view()), panel_.item, nodetree_);
321}
322
323std::unique_ptr<TreeViewItemDropTarget> NodePanelViewItem::create_drop_target()
324{
325 return std::make_unique<NodePanelDropTarget>(*this, panel_);
326}
327
328NodeTreeInterfaceDragController::NodeTreeInterfaceDragController(NodeTreeInterfaceView &view,
331 : AbstractViewItemDragController(view), item_(item), tree_(tree)
332{
333}
334
335eWM_DragDataType NodeTreeInterfaceDragController::get_drag_type() const
336{
338}
339
340void *NodeTreeInterfaceDragController::create_drag_data() const
341{
343 __func__);
344 drag_data->item = &item_;
345 drag_data->tree = &tree_;
346 return drag_data;
347}
348
349bNodeTreeInterfaceItemReference *get_drag_node_tree_declaration(const wmDrag &drag)
350{
352 return static_cast<bNodeTreeInterfaceItemReference *>(drag.poin);
353}
354
355bool is_dragging_parent_panel(const wmDrag &drag, const bNodeTreeInterfaceItem &drop_target_item)
356{
357 if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) {
358 return false;
359 }
360 bNodeTreeInterfaceItemReference *drag_data = get_drag_node_tree_declaration(drag);
362 drag_data->item))
363 {
364 if (panel->contains(drop_target_item)) {
365 return true;
366 }
367 }
368 return false;
369}
370
371NodeSocketDropTarget::NodeSocketDropTarget(NodeSocketViewItem &item,
373 : TreeViewItemDropTarget(item, DropBehavior::Reorder), socket_(socket)
374{
375}
376
377bool NodeSocketDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const
378{
379 if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) {
380 return false;
381 }
382 if (is_dragging_parent_panel(drag, socket_.item)) {
383 return false;
384 }
385 return true;
386}
387
388std::string NodeSocketDropTarget::drop_tooltip(const DragInfo &drag_info) const
389{
390 switch (drag_info.drop_location) {
391 case DropLocation::Into:
392 return "";
393 case DropLocation::Before:
394 return TIP_("Insert before socket");
395 case DropLocation::After:
396 return TIP_("Insert after socket");
397 }
398 return "";
399}
400
401bool on_drop_flat_item(bContext *C,
402 const DragInfo &drag_info,
403 bNodeTree &ntree,
404 bNodeTreeInterfaceItem &drop_target_item)
405{
406 bNodeTreeInterfaceItemReference *drag_data = get_drag_node_tree_declaration(drag_info.drag_data);
407 BLI_assert(drag_data != nullptr);
408 bNodeTreeInterfaceItem *drag_item = drag_data->item;
409 BLI_assert(drag_item != nullptr);
410
411 bNodeTreeInterface &interface = ntree.tree_interface;
412
413 bNodeTreeInterfacePanel *parent = interface.find_item_parent(drop_target_item, true);
414 int index = -1;
415
416 /* Insert into same panel as the target. */
417 BLI_assert(parent != nullptr);
418 switch (drag_info.drop_location) {
419 case DropLocation::Before:
420 index = parent->items().as_span().first_index_try(&drop_target_item);
421 break;
422 case DropLocation::After:
423 index = parent->items().as_span().first_index_try(&drop_target_item) + 1;
424 break;
425 default:
426 /* All valid cases should be handled above. */
428 break;
429 }
430 if (parent == nullptr || index < 0) {
431 return false;
432 }
433
434 interface.move_item_to_parent(*drag_item, parent, index);
435
436 /* General update */
438 ED_undo_push(C, "Insert node group item");
439 return true;
440}
441
442bool NodeSocketDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const
443{
444 bNodeTree &nodetree = this->get_view<NodeTreeInterfaceView>().nodetree();
445 return on_drop_flat_item(C, drag_info, nodetree, socket_.item);
446}
447
448NodePanelDropTarget::NodePanelDropTarget(NodePanelViewItem &item, bNodeTreeInterfacePanel &panel)
449 : TreeViewItemDropTarget(item, DropBehavior::ReorderAndInsert), panel_(panel)
450{
451}
452
453bool NodePanelDropTarget::can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const
454{
455 if (drag.type != WM_DRAG_NODE_TREE_INTERFACE) {
456 return false;
457 }
458 bNodeTreeInterfaceItemReference *drag_data = get_drag_node_tree_declaration(drag);
459
460 /* Can't drop an item onto its children. */
462 drag_data->item))
463 {
464 if (panel->contains(panel_.item)) {
465 return false;
466 }
467 }
468
469 return true;
470}
471
472std::string NodePanelDropTarget::drop_tooltip(const DragInfo &drag_info) const
473{
474 switch (drag_info.drop_location) {
475 case DropLocation::Into:
476 return TIP_("Insert into panel");
477 case DropLocation::Before:
478 return TIP_("Insert before panel");
479 case DropLocation::After:
480 return TIP_("Insert after panel");
481 }
482 return "";
483}
484
485bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const
486{
487 bNodeTreeInterfaceItemReference *drag_data = get_drag_node_tree_declaration(drag_info.drag_data);
488 BLI_assert(drag_data != nullptr);
489 bNodeTreeInterfaceItem *drag_item = drag_data->item;
490 BLI_assert(drag_item != nullptr);
491
492 bNodeTree &nodetree = get_view<NodeTreeInterfaceView>().nodetree();
493 bNodeTreeInterface &interface = get_view<NodeTreeInterfaceView>().interface();
494
495 bNodeTreeInterfacePanel *parent = nullptr;
496 int index = -1;
497 switch (drag_info.drop_location) {
498 case DropLocation::Into: {
499 /* Insert into target */
500 parent = &panel_;
501 const bool has_toggle_socket = panel_.header_toggle_socket() != nullptr;
502 index = has_toggle_socket ? 1 : 0;
503 break;
504 }
505 case DropLocation::Before: {
506 /* Insert into same panel as the target. */
507 parent = interface.find_item_parent(panel_.item, true);
508 BLI_assert(parent != nullptr);
509 index = parent->items().as_span().first_index_try(&panel_.item);
510 break;
511 }
512 case DropLocation::After: {
513 /* Insert into same panel as the target. */
514 parent = interface.find_item_parent(panel_.item, true);
515 BLI_assert(parent != nullptr);
516 index = parent->items().as_span().first_index_try(&panel_.item) + 1;
517 break;
518 }
519 }
520 if (parent == nullptr || index < 0) {
521 return false;
522 }
523
524 interface.move_item_to_parent(*drag_item, parent, index);
525
526 /* General update */
528 ED_undo_push(C, "Insert node group item");
529 return true;
530}
531
532} // namespace
533
534} // namespace blender::ui::nodes
535
537{
538 if (!ptr->data) {
539 return;
540 }
541 if (!RNA_struct_is_a(ptr->type, &RNA_NodeTreeInterface)) {
542 return;
543 }
544 bNodeTree &nodetree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
545 bNodeTreeInterface &interface = *static_cast<bNodeTreeInterface *>(ptr->data);
546
547 uiBlock *block = uiLayoutGetBlock(layout);
548
550 *block,
551 "Node Tree Declaration Tree View",
552 std::make_unique<blender::ui::nodes::NodeTreeInterfaceView>(nodetree, interface));
553 tree_view->set_context_menu_title("Node Tree Interface");
554 tree_view->set_default_rows(5);
555
557}
Main * CTX_data_main(const bContext *C)
void BKE_main_ensure_invariants(Main &bmain, std::optional< blender::Span< ID * > > modified_ids=std::nullopt)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define TIP_(msgid)
struct bNodeTreeInterface bNodeTreeInterface
struct bNodeTreeInterfaceSocket bNodeTreeInterfaceSocket
struct bNodeTreeInterfacePanel bNodeTreeInterfacePanel
struct bNodeTreeInterfaceItem bNodeTreeInterfaceItem
struct bNodeTree bNodeTree
void ED_undo_push(bContext *C, const char *str)
Definition ed_undo.cc:99
static AppView * view
#define C
Definition RandGen.cpp:29
blender::ui::AbstractGridView * UI_block_add_view(uiBlock &block, blender::StringRef idname, std::unique_ptr< blender::ui::AbstractGridView > grid_view)
void uiTemplateNodeSocket(uiLayout *layout, bContext *C, const float color[4])
void uiLayoutSetEnabled(uiLayout *layout, bool enabled)
uiBlock * uiLayoutGetBlock(uiLayout *layout)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
void uiLayoutSetEmboss(uiLayout *layout, blender::ui::EmbossType emboss)
eWM_DragDataType
Definition WM_types.hh:1197
@ WM_DRAG_NODE_TREE_INTERFACE
Definition WM_types.hh:1220
PyObject * self
constexpr const char * c_str() const
void set_default_rows(int default_rows)
Definition tree_view.cc:139
void set_context_menu_title(const std::string &title)
static void build_tree_view(const bContext &C, AbstractTreeView &tree_view, uiLayout &layout, std::optional< StringRef > search_string={}, bool add_box=true)
Definition tree_view.cc:980
KDTree_3d * tree
#define interface
#define MEM_SAFE_FREE(v)
#define ID_IS_LINKED(_id)
void uiTemplateNodeTreeInterface(uiLayout *layout, bContext *C, PointerRNA *ptr)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
T & get_item_as(bNodeTreeInterfaceItem &item)
TreeViewItemContainer TreeViewOrItem
bool RNA_struct_is_a(const StructRNA *type, const StructRNA *srna)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
void label(blender::StringRef name, int icon)
uiLayout & row(bool align)
eWM_DragDataType type
Definition WM_types.hh:1327
void * poin
Definition WM_types.hh:1328
PointerRNA * ptr
Definition wm_files.cc:4226