From 25c84e90dfaa6ee1d41fe02f7a517ed4417db0a5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Sat, 27 Sep 2025 16:47:54 +0200 Subject: [PATCH] Nodes: add button in switch nodes to add inputs This is a small usability feature to simplify adding additional inputs in the Index Switch and Menu Switch nodes without having to go to the sidebar. Technically, something similar can be done for other extend-sockets. However, here it works best, because one does not have to choose the socket type. For other inputs we could use a quick popup when pressing the button, but that seems outside of the scope of this patch. Later we might also want add the ability to remove existing inputs. But just adding an icon for that doesn't work so well for socket types that have an input property. Co-authored-by: Brady Johnston <36021261+BradyAJohnston@users.noreply.github.com> Pull Request: https://projects.blender.org/blender/blender/pulls/145915 --- source/blender/editors/space_node/drawnode.cc | 2 +- source/blender/nodes/NOD_socket_items_ops.hh | 71 +++++++++++++++---- .../geometry/nodes/node_geo_index_switch.cc | 8 ++- .../geometry/nodes/node_geo_menu_switch.cc | 9 ++- 4 files changed, 72 insertions(+), 18 deletions(-) diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index 5a573340f91..ba6e1c37810 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -1458,7 +1458,7 @@ void ED_init_standard_node_socket_type(blender::bke::bNodeSocketType *stype) void ED_init_node_socket_type_virtual(blender::bke::bNodeSocketType *stype) { using namespace blender::ed::space_node; - stype->draw = node_socket_button_label; + stype->draw = std_node_socket_draw; stype->draw_color = node_socket_virtual_draw_color; stype->draw_color_simple = node_socket_virtual_draw_color_simple; } diff --git a/source/blender/nodes/NOD_socket_items_ops.hh b/source/blender/nodes/NOD_socket_items_ops.hh index 7cb0f1cc7b0..0851b3e21b2 100644 --- a/source/blender/nodes/NOD_socket_items_ops.hh +++ b/source/blender/nodes/NOD_socket_items_ops.hh @@ -24,7 +24,9 @@ namespace blender::nodes::socket_items::ops { -inline PointerRNA get_active_node_to_operate_on(bContext *C, const StringRef node_idname) +inline PointerRNA get_active_node_to_operate_on(bContext *C, + wmOperator *op, + const StringRef node_idname) { SpaceNode *snode = CTX_wm_space_node(C); if (!snode) { @@ -40,20 +42,28 @@ inline PointerRNA get_active_node_to_operate_on(bContext *C, const StringRef nod if (!zones) { return PointerRNA_NULL; } - bNode *active_node = bke::node_get_active(*snode->edittree); - if (!active_node) { + + bNode *node = nullptr; + if (RNA_struct_property_is_set(op->ptr, "node_identifier")) { + const int id = RNA_int_get(op->ptr, "node_identifier"); + node = snode->edittree->node_by_id(id); + } + else { + node = bke::node_get_active(*snode->edittree); + } + if (!node) { return PointerRNA_NULL; } - if (const bke::bNodeTreeZone *zone = zones->get_zone_by_node(active_node->identifier)) { - if (zone->input_node() == active_node) { + if (const bke::bNodeTreeZone *zone = zones->get_zone_by_node(node->identifier)) { + if (zone->input_node() == node) { /* Assume the data is generally stored on the output and not the input node. */ - active_node = const_cast(zone->output_node()); + node = const_cast(zone->output_node()); } } - if (active_node->idname != node_idname) { + if (node->idname != node_idname) { return PointerRNA_NULL; } - return RNA_pointer_create_discrete(&snode->edittree->id, &RNA_Node, active_node); + return RNA_pointer_create_discrete(&snode->edittree->id, &RNA_Node, node); } inline void update_after_node_change(bContext *C, const PointerRNA node_ptr) @@ -68,7 +78,30 @@ inline void update_after_node_change(bContext *C, const PointerRNA node_ptr) template inline bool editable_node_active_poll(bContext *C) { - return get_active_node_to_operate_on(C, Accessor::node_idname).data != nullptr; + SpaceNode *snode = CTX_wm_space_node(C); + if (!snode) { + return false; + } + if (!snode->edittree) { + return false; + } + if (!ID_IS_EDITABLE(snode->edittree)) { + return false; + } + return true; +} + +inline void add_node_identifier_property(wmOperatorType *ot) +{ + RNA_def_int(ot->srna, + "node_identifier", + 0, + 0, + INT32_MAX, + "Node Identifier", + "Optional identifier of the node to operate on", + 0, + INT32_MAX); } template @@ -82,8 +115,8 @@ inline void remove_active_item(wmOperatorType *ot, ot->description = description; ot->poll = editable_node_active_poll; - ot->exec = [](bContext *C, wmOperator * /*op*/) -> wmOperatorStatus { - PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_idname); + ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus { + PointerRNA node_ptr = get_active_node_to_operate_on(C, op, Accessor::node_idname); bNode &node = *static_cast(node_ptr.data); SocketItemsRef ref = Accessor::get_items_from_node(node); if (*ref.items_num > 0) { @@ -93,6 +126,8 @@ inline void remove_active_item(wmOperatorType *ot, } return OPERATOR_FINISHED; }; + + add_node_identifier_property(ot); } template @@ -107,7 +142,7 @@ inline void remove_item_by_index(wmOperatorType *ot, ot->poll = editable_node_active_poll; ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus { - PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_idname); + PointerRNA node_ptr = get_active_node_to_operate_on(C, op, Accessor::node_idname); bNode &node = *static_cast(node_ptr.data); const int index_to_remove = RNA_int_get(op->ptr, "index"); SocketItemsRef ref = Accessor::get_items_from_node(node); @@ -132,8 +167,11 @@ inline void add_item(wmOperatorType *ot, ot->description = description; ot->poll = editable_node_active_poll; - ot->exec = [](bContext *C, wmOperator * /*op*/) -> wmOperatorStatus { - PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_idname); + ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus { + PointerRNA node_ptr = get_active_node_to_operate_on(C, op, Accessor::node_idname); + if (node_ptr.data == nullptr) { + return OPERATOR_CANCELLED; + } bNode &node = *static_cast(node_ptr.data); SocketItemsRef ref = Accessor::get_items_from_node(node); const typename Accessor::ItemT *active_item = nullptr; @@ -180,6 +218,8 @@ inline void add_item(wmOperatorType *ot, update_after_node_change(C, node_ptr); return OPERATOR_FINISHED; }; + + add_node_identifier_property(ot); } enum class MoveDirection { @@ -199,7 +239,7 @@ inline void move_active_item(wmOperatorType *ot, ot->poll = editable_node_active_poll; ot->exec = [](bContext *C, wmOperator *op) -> wmOperatorStatus { - PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_idname); + PointerRNA node_ptr = get_active_node_to_operate_on(C, op, Accessor::node_idname); bNode &node = *static_cast(node_ptr.data); const MoveDirection direction = MoveDirection(RNA_enum_get(op->ptr, "direction")); @@ -225,6 +265,7 @@ inline void move_active_item(wmOperatorType *ot, }; RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Move direction"); + add_node_identifier_property(ot); } /** diff --git a/source/blender/nodes/geometry/nodes/node_geo_index_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_index_switch.cc index a38953b33bd..600c6c91399 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_index_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_index_switch.cc @@ -2,6 +2,7 @@ * * SPDX-License-Identifier: GPL-2.0-or-later */ +#include "UI_interface_c.hh" #include "node_geometry_util.hh" #include "UI_interface_layout.hh" @@ -65,7 +66,12 @@ static void node_declare(NodeDeclarationBuilder &b) } output.structure_type(structure_type); - b.add_input("", "__extend__"); + b.add_input("", "__extend__").custom_draw([](CustomSocketDrawParams ¶ms) { + uiLayout &layout = params.layout; + layout.emboss_set(ui::EmbossType::None); + PointerRNA op_ptr = layout.op("node.index_switch_item_add", IFACE_(""), ICON_ADD); + RNA_int_set(&op_ptr, "node_identifier", params.node.identifier); + }); } static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_menu_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_menu_switch.cc index 26d2a93c3fe..c4f18ec2c05 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_menu_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_menu_switch.cc @@ -103,7 +103,14 @@ static void node_declare(blender::nodes::NodeDeclarationBuilder &b) } } - b.add_input("", "__extend__").structure_type(StructureType::Dynamic); + b.add_input("", "__extend__") + .structure_type(StructureType::Dynamic) + .custom_draw([](CustomSocketDrawParams ¶ms) { + uiLayout &layout = params.layout; + layout.emboss_set(ui::EmbossType::None); + PointerRNA op_ptr = layout.op("node.enum_definition_item_add", "", ICON_ADD); + RNA_int_set(&op_ptr, "node_identifier", params.node.identifier); + }); } static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)