diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index e119ce3790f..95b48f86b49 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -1016,92 +1016,12 @@ class NODE_MT_node_tree_interface_context_menu(Menu): layout.operator("node.interface_item_unlink_panel_toggle") -class NODE_PT_node_tree_interface(Panel): - bl_space_type = 'NODE_EDITOR' - bl_region_type = 'UI' - bl_category = "Group" - bl_label = "Group Sockets" - - @classmethod - def poll(cls, context): - snode = context.space_data - if snode is None: - return False - tree = snode.edit_tree - if tree is None: - return False - if tree.is_embedded_data: - return False - if not tree.bl_use_group_interface: - return False - return True - - def draw(self, context): - layout = self.layout - snode = context.space_data - tree = snode.edit_tree - - split = layout.row() - - split.template_node_tree_interface(tree.interface) - - ops_col = split.column(align=True) - ops_col.enabled = tree.library is None - ops_col.operator_menu_enum("node.interface_item_new", "item_type", icon='ADD', text="") - ops_col.operator("node.interface_item_remove", icon='REMOVE', text="") - ops_col.separator() - ops_col.menu("NODE_MT_node_tree_interface_context_menu", icon='DOWNARROW_HLT', text="") - - ops_col.separator() - - active_item = tree.interface.active - if active_item is not None: - layout.use_property_split = True - layout.use_property_decorate = False - - if active_item.item_type == 'SOCKET': - layout.prop(active_item, "socket_type", text="Type") - layout.prop(active_item, "description") - # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. - if tree.type == 'GEOMETRY': - field_socket_types = { - "NodeSocketInt", - "NodeSocketColor", - "NodeSocketVector", - "NodeSocketBool", - "NodeSocketFloat", - } - if active_item.socket_type in field_socket_types: - if 'OUTPUT' in active_item.in_out: - layout.prop(active_item, "attribute_domain") - layout.prop(active_item, "default_attribute_name") - if hasattr(active_item, "draw"): - active_item.draw(context, layout) - - if active_item.item_type == 'PANEL': - layout.prop(active_item, "description") - layout.prop(active_item, "default_closed", text="Closed by Default") - - if active_item.item_type == 'PANEL' and len( - active_item.interface_items) > 0 and getattr( - active_item.interface_items[0], "is_panel_toggle", False): - panel_toggle_item = active_item.interface_items[0] - header, body = layout.panel("panel_toggle", default_closed=False) - header.label(text="Panel Toggle") - if body: - body.prop(panel_toggle_item, "default_value", text="Default") - col = body.column() - col.prop(panel_toggle_item, "hide_in_modifier") - col.prop(panel_toggle_item, "force_non_field") - - layout.use_property_split = False - - class NODE_PT_node_tree_properties(Panel): bl_space_type = 'NODE_EDITOR' bl_region_type = 'UI' bl_category = "Group" bl_label = "Group" + bl_order = 0 @classmethod def poll(cls, context): @@ -1156,6 +1076,7 @@ class NODE_PT_node_tree_animation(Panel): bl_category = "Group" bl_label = "Animation" bl_options = {'DEFAULT_CLOSED'} + bl_order = 20 @classmethod def poll(cls, context): @@ -1249,7 +1170,6 @@ classes = ( NODE_PT_node_color_presets, NODE_PT_node_tree_properties, NODE_MT_node_tree_interface_context_menu, - NODE_PT_node_tree_interface, NODE_PT_node_tree_animation, NODE_PT_active_node_generic, NODE_PT_active_node_color, diff --git a/source/blender/editors/include/UI_interface_c.hh b/source/blender/editors/include/UI_interface_c.hh index 074a16c5414..51378118605 100644 --- a/source/blender/editors/include/UI_interface_c.hh +++ b/source/blender/editors/include/UI_interface_c.hh @@ -2666,7 +2666,7 @@ void uiTemplateLightLinkingCollection(uiLayout *layout, void uiTemplateBoneCollectionTree(uiLayout *layout, bContext *C); void uiTemplateGreasePencilLayerTree(uiLayout *layout, bContext *C); -void uiTemplateNodeTreeInterface(uiLayout *layout, bContext *C, PointerRNA *ptr); +void uiTemplateNodeTreeInterface(uiLayout *layout, const bContext *C, PointerRNA *ptr); /** * Draw all node buttons and socket default values with the same panel structure used by the node. */ diff --git a/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc b/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc index 6036dd7ebff..29bf7906274 100644 --- a/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc +++ b/source/blender/editors/interface/templates/interface_template_node_tree_interface.cc @@ -560,7 +560,7 @@ bool NodePanelDropTarget::on_drop(bContext *C, const DragInfo &drag_info) const } // namespace blender::ui::nodes -void uiTemplateNodeTreeInterface(uiLayout *layout, bContext *C, PointerRNA *ptr) +void uiTemplateNodeTreeInterface(uiLayout *layout, const bContext *C, PointerRNA *ptr) { if (!ptr->data) { return; diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 8b241a7aaf2..2c98fdb1763 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC node_socket_tooltip.cc node_sync_sockets.cc node_templates.cc + node_tree_interface_ui.cc node_view.cc space_node.cc diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index c9560a6fffc..e7d774116c0 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -475,4 +475,8 @@ void build_socket_tooltip(uiTooltipData &tip_data, const bNodeTree &tree, const bNodeSocket &socket); +/** node_tree_interface_ui.cc */ + +void node_tree_interface_panel_register(ARegionType *art); + } // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_tree_interface_ui.cc b/source/blender/editors/space_node/node_tree_interface_ui.cc new file mode 100644 index 00000000000..13a3d7d21eb --- /dev/null +++ b/source/blender/editors/space_node/node_tree_interface_ui.cc @@ -0,0 +1,124 @@ +/* SPDX-FileCopyrightText: 2025 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BKE_library.hh" +#include "BKE_screen.hh" +#include "BLI_listbase.h" +#include "BLI_string_utf8.h" +#include "BLT_translation.hh" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "NOD_socket.hh" +#include "RNA_access.hh" +#include "RNA_prototypes.hh" +#include "UI_interface_layout.hh" +#include "UI_resources.hh" + +#include "node_intern.hh" + +namespace blender::ed::space_node { + +static bool node_tree_interface_panel_poll(const bContext *C, PanelType * /*pt*/) +{ + SpaceNode *snode = CTX_wm_space_node(C); + if (!snode) { + return false; + } + bNodeTree *ntree = snode->edittree; + if (!ntree) { + return false; + } + if (ntree->flag & ID_FLAG_EMBEDDED_DATA) { + return false; + } + if (ntree->typeinfo->no_group_interface) { + return false; + } + return true; +} + +static void node_tree_interface_panel_draw(const bContext *C, Panel *panel) +{ + SpaceNode &snode = *CTX_wm_space_node(C); + bNodeTree &tree = *snode.edittree; + uiLayout &layout = *panel->layout; + + PointerRNA tree_ptr = RNA_pointer_create_discrete(&tree.id, &RNA_NodeTree, &tree); + PointerRNA interface_ptr = RNA_pointer_get(&tree_ptr, "interface"); + + { + uiLayout &row = layout.row(false); + uiTemplateNodeTreeInterface(&row, C, &interface_ptr); + + uiLayout &col = row.column(true); + col.enabled_set(ID_IS_EDITABLE(&tree.id)); + col.op_menu_enum(C, "node.interface_item_new", "item_type", "", ICON_ADD); + col.op("node.interface_item_remove", "", ICON_REMOVE); + col.separator(); + col.menu("NODE_MT_node_tree_interface_context_menu", "", ICON_DOWNARROW_HLT); + } + + bNodeTreeInterfaceItem *active_item = tree.tree_interface.active_item(); + if (!active_item) { + return; + } + PointerRNA active_item_ptr = RNA_pointer_get(&interface_ptr, "active"); + + layout.use_property_split_set(true); + layout.use_property_decorate_set(false); + + if (active_item->item_type == NODE_INTERFACE_SOCKET) { + bNodeTreeInterfaceSocket *socket = reinterpret_cast(active_item); + const bke::bNodeSocketType *stype = socket->socket_typeinfo(); + layout.prop(&active_item_ptr, "socket_type", UI_ITEM_NONE, IFACE_("Type"), ICON_NONE); + layout.prop(&active_item_ptr, "description", UI_ITEM_NONE, std::nullopt, ICON_NONE); + if (tree.type == NTREE_GEOMETRY) { + if (nodes::socket_type_supports_fields(stype->type) && stype->type != SOCK_MENU) { + if (socket->flag & NODE_INTERFACE_SOCKET_OUTPUT) { + layout.prop(&active_item_ptr, "attribute_domain", UI_ITEM_NONE, std::nullopt, ICON_NONE); + } + layout.prop( + &active_item_ptr, "default_attribute_name", UI_ITEM_NONE, std::nullopt, ICON_NONE); + } + } + if (stype->interface_draw) { + stype->interface_draw(&tree.id, socket, const_cast(C), &layout); + } + } + if (active_item->item_type == NODE_INTERFACE_PANEL) { + bNodeTreeInterfacePanel *panel_item = reinterpret_cast(active_item); + layout.prop(&active_item_ptr, "description", UI_ITEM_NONE, std::nullopt, ICON_NONE); + layout.prop( + &active_item_ptr, "default_closed", UI_ITEM_NONE, IFACE_("Closed by Default"), ICON_NONE); + + if (bNodeTreeInterfaceSocket *panel_toggle_socket = panel_item->header_toggle_socket()) { + if (uiLayout *panel = layout.panel(C, "panel_toggle", false, IFACE_("Panel Toggle"))) { + PointerRNA panel_toggle_socket_ptr = RNA_pointer_create_discrete( + &tree.id, &RNA_NodeTreeInterfaceSocket, panel_toggle_socket); + panel->prop( + &panel_toggle_socket_ptr, "default_value", UI_ITEM_NONE, IFACE_("Default"), ICON_NONE); + uiLayout &col = panel->column(false); + col.prop( + &panel_toggle_socket_ptr, "hide_in_modifier", UI_ITEM_NONE, std::nullopt, ICON_NONE); + col.prop( + &panel_toggle_socket_ptr, "force_non_field", UI_ITEM_NONE, std::nullopt, ICON_NONE); + } + } + } +} + +void node_tree_interface_panel_register(ARegionType *art) +{ + PanelType *pt = MEM_callocN("NODE_PT_node_tree_interface"); + STRNCPY_UTF8(pt->idname, "NODE_PT_node_tree_interface"); + STRNCPY_UTF8(pt->label, N_("Group Sockets")); + STRNCPY_UTF8(pt->category, "Group"); + STRNCPY_UTF8(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->draw = node_tree_interface_panel_draw; + pt->poll = node_tree_interface_panel_poll; + pt->order = 10; + BLI_addtail(&art->paneltypes, pt); +} + +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc index 60be672c387..017c303a090 100644 --- a/source/blender/editors/space_node/space_node.cc +++ b/source/blender/editors/space_node/space_node.cc @@ -1876,6 +1876,8 @@ void ED_spacetype_node() art->draw = node_buttons_region_draw; BLI_addhead(&st->regiontypes, art); + node_tree_interface_panel_register(art); + /* regions: toolbar */ art = MEM_callocN("spacetype view3d tools region"); art->regionid = RGN_TYPE_TOOLS;