From d8d09cdadbcdfd66a32bcc692795357f3fabb82d Mon Sep 17 00:00:00 2001 From: Habib Gahbiche Date: Fri, 14 Mar 2025 11:26:57 +0100 Subject: [PATCH] Geometry Nodes: shortcuts for viewer nodes Implement shortcuts for viewer nodes. Viewer nodes are now activated using the operator `bpy.ops.node.activate_viewer()` instead of activating the viewer by setting the node to active. This also unifies the behavior with viewer shortcuts in the compositor (see attachment in the original PR). Pull Request: https://projects.blender.org/blender/blender/pulls/134555 --- scripts/startup/bl_operators/node.py | 18 +++------ source/blender/blenkernel/intern/node.cc | 17 +++++--- .../blender/editors/space_node/node_draw.cc | 19 ++++++++- .../blender/makesrna/intern/rna_nodetree.cc | 40 ++++++++++++++----- source/blender/nodes/NOD_rna_define.hh | 2 + .../nodes/geometry/nodes/node_geo_viewer.cc | 9 +++++ 6 files changed, 76 insertions(+), 29 deletions(-) diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 6cd5baf5605..937e0e3f7fc 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -565,7 +565,7 @@ class NODE_OT_viewer_shortcut_set(Operator): (space is not None) and space.type == 'NODE_EDITOR' and space.node_tree is not None and - space.tree_type == 'CompositorNodeTree' + space.tree_type in {'CompositorNodeTree', 'GeometryNodeTree'} ) def execute(self, context): @@ -580,7 +580,6 @@ class NODE_OT_viewer_shortcut_set(Operator): # Only viewer nodes can be set to favorites. However, the user can # create a new favorite viewer by selecting any node and pressing ctrl+1. - old_active = nodes.active if fav_node.type == 'VIEWER': viewer_node = fav_node else: @@ -600,10 +599,8 @@ class NODE_OT_viewer_shortcut_set(Operator): ) return {'CANCELLED'} - # Use the node active status to enable this viewer node and disable others. - nodes.active = viewer_node - if old_active.type != 'VIEWER': - nodes.active = old_active + with bpy.context.temp_override(node=viewer_node): + bpy.ops.node.activate_viewer() viewer_node.ui_shortcut = self.viewer_index self.report({'INFO'}, "Assigned shortcut {:d} to {:s}".format(self.viewer_index, viewer_node.name)) @@ -629,7 +626,7 @@ class NODE_OT_viewer_shortcut_get(Operator): (space is not None) and space.type == 'NODE_EDITOR' and space.node_tree is not None and - space.tree_type == 'CompositorNodeTree' + space.tree_type in {'CompositorNodeTree', 'GeometryNodeTree'} ) def execute(self, context): @@ -645,11 +642,8 @@ class NODE_OT_viewer_shortcut_get(Operator): self.report({'INFO'}, "Shortcut {:d} is not assigned to a Viewer node yet".format(self.viewer_index)) return {'CANCELLED'} - # Use the node active status to enable this viewer node and disable others. - old_active = nodes.active - nodes.active = viewer_node - if old_active.type != "VIEWER": - nodes.active = old_active + with bpy.context.temp_override(node=viewer_node): + bpy.ops.node.activate_viewer() return {'FINISHED'} diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index 4c171dfbce4..2d0f387e94f 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -3608,14 +3608,16 @@ void node_tree_free_local_tree(bNodeTree *ntree) void node_tree_set_output(bNodeTree &ntree) { const bool is_compositor = ntree.type == NTREE_COMPOSIT; + const bool is_geometry = ntree.type == NTREE_GEOMETRY; /* find the active outputs, might become tree type dependent handler */ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { if (node->typeinfo->nclass == NODE_CLASS_OUTPUT) { /* we need a check for which output node should be tagged like this, below an exception */ - if (ELEM(node->type_legacy, CMP_NODE_OUTPUT_FILE, GEO_NODE_VIEWER)) { + if (node->is_type("CompositorNodeOutputFile")) { continue; } - const bool node_is_output = node->type_legacy == CMP_NODE_VIEWER; + const bool node_is_output = node->is_type("CompositorNodeViewer") || + node->is_type("GeometryNodeViewer"); int output = 0; /* there is more types having output class, each one is checked */ @@ -3626,12 +3628,15 @@ void node_tree_set_output(bNodeTree &ntree) } /* same type, exception for viewer */ - const bool tnode_is_output = tnode->type_legacy == CMP_NODE_VIEWER; - const bool compositor_case = is_compositor && tnode_is_output && node_is_output; - const bool has_same_shortcut = compositor_case && node != tnode && + const bool tnode_is_output = tnode->is_type("CompositorNodeViewer") || + tnode->is_type("GeometryNodeViewer"); + const bool viewer_case = (is_compositor || is_geometry) && tnode_is_output && + node_is_output; + const bool has_same_shortcut = viewer_case && node != tnode && tnode->custom1 == node->custom1 && tnode->custom1 != NODE_VIEWER_SHORTCUT_NONE; - if (tnode->type_legacy == node->type_legacy || compositor_case) { + + if (tnode->type_legacy == node->type_legacy || viewer_case) { if (tnode->flag & NODE_DO_OUTPUT) { output++; if (output > 1) { diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 4b0a0e67f4a..4eb81c4ed0b 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -3317,7 +3317,7 @@ static void node_draw_extra_info_panel(const bContext &C, static short get_viewer_shortcut_icon(const bNode &node) { - BLI_assert(node.is_type("CompositorNodeViewer")); + BLI_assert(node.is_type("CompositorNodeViewer") || node.is_type("GeometryNodeViewer")); switch (node.custom1) { case NODE_VIEWER_SHORTCUT_NONE: /* No change by default. */ @@ -3532,9 +3532,24 @@ static void node_draw_basis(const bContext &C, 0, ""); /* Selection implicitly activates the node. */ - const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : "NODE_OT_select"; + const char *operator_idname = is_active ? "NODE_OT_deactivate_viewer" : + "NODE_OT_activate_viewer"; UI_but_func_set( but, node_toggle_button_cb, POINTER_FROM_INT(node.identifier), (void *)operator_idname); + + short shortcut_icon = get_viewer_shortcut_icon(node); + uiDefIconBut(&block, + UI_BTYPE_BUT, + 0, + shortcut_icon, + iconofs - 1.2 * iconbutw, + rct.ymax - NODE_DY, + iconbutw, + UI_UNIT_Y, + nullptr, + 0, + 0, + ""); UI_block_emboss_set(&block, UI_EMBOSS); } /* Viewer node shortcuts. */ diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 4af7a782883..737d1351d5c 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -1344,18 +1344,40 @@ static void rna_NodeTree_active_node_set(PointerRNA *ptr, } } -static void rna_Node_shortcut_node_set(PointerRNA *ptr, int value) +static void node_viewer_set_shortcut_fn(bNode *node, bNodeTree &ntree, int value) +{ + /* Avoid having two nodes with the same shortcut. */ + for (bNode *other_node : ntree.all_nodes()) { + if ((other_node->is_type("CompositorNodeViewer") || + other_node->is_type("GeometryNodeViewer")) && + other_node->custom1 == value) + { + other_node->custom1 = NODE_VIEWER_SHORTCUT_NONE; + } + } + node->custom1 = value; +} + +void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, PropertyRNA * /*prop*/, int value) { bNode *curr_node = static_cast(ptr->data); bNodeTree &ntree = curr_node->owner_tree(); - /* Avoid having two nodes with the same shortcut. */ - for (bNode *node : ntree.all_nodes()) { - if (node->is_type("CompositorNodeViewer") && node->custom1 == value) { - node->custom1 = NODE_VIEWER_SHORTCUT_NONE; - } - } - curr_node->custom1 = value; + node_viewer_set_shortcut_fn(curr_node, ntree, value); +} + +int rna_Node_Viewer_shortcut_node_get(PointerRNA *ptr, PropertyRNA * /*prop*/) +{ + bNode *curr_node = static_cast(ptr->data); + return curr_node->custom1; +} + +void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, int value) +{ + bNode *curr_node = static_cast(ptr->data); + bNodeTree &ntree = curr_node->owner_tree(); + + node_viewer_set_shortcut_fn(curr_node, ntree, value); } static bNodeLink *rna_NodeTree_link_new(bNodeTree *ntree, @@ -9164,7 +9186,7 @@ static void def_cmp_viewer(BlenderRNA * /*brna*/, StructRNA *srna) prop = RNA_def_property(srna, "ui_shortcut", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, nullptr, "custom1"); - RNA_def_property_int_funcs(prop, nullptr, "rna_Node_shortcut_node_set", nullptr); + RNA_def_property_int_funcs(prop, nullptr, "rna_Node_Viewer_shortcut_node_set", nullptr); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); RNA_def_property_int_default(prop, NODE_VIEWER_SHORTCUT_NONE); diff --git a/source/blender/nodes/NOD_rna_define.hh b/source/blender/nodes/NOD_rna_define.hh index 33307573750..ec272aeb390 100644 --- a/source/blender/nodes/NOD_rna_define.hh +++ b/source/blender/nodes/NOD_rna_define.hh @@ -15,6 +15,8 @@ void rna_Node_update(Main *bmain, Scene *scene, PointerRNA *ptr); void rna_Node_socket_update(Main *bmain, Scene *scene, PointerRNA *ptr); void rna_Node_update_relations(Main *bmain, Scene *scne, PointerRNA *ptr); +void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, PropertyRNA *prop, int value); +int rna_Node_Viewer_shortcut_node_get(PointerRNA *ptr, PropertyRNA *prop); namespace blender::nodes { diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 8c142f1f360..5e0a59a0ec7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -138,6 +138,15 @@ static void node_rna(StructRNA *srna) rna_enum_attribute_domain_with_auto_items, NOD_storage_enum_accessors(domain), int(AttrDomain::Point)); + + PropertyRNA *prop; + prop = RNA_def_property(srna, "ui_shortcut", PROP_INT, PROP_NONE); + RNA_def_property_int_funcs_runtime( + prop, rna_Node_Viewer_shortcut_node_get, rna_Node_Viewer_shortcut_node_set, nullptr); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_IGNORE); + RNA_def_property_int_default(prop, NODE_VIEWER_SHORTCUT_NONE); + RNA_def_property_update_notifier(prop, NC_NODE | ND_DISPLAY); } static void node_register()