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
This commit is contained in:
Habib Gahbiche
2025-03-14 11:26:57 +01:00
parent 18c4df0243
commit d8d09cdadb
6 changed files with 76 additions and 29 deletions

View File

@@ -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'}

View File

@@ -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) {

View File

@@ -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. */

View File

@@ -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<bNode *>(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<bNode *>(ptr->data);
return curr_node->custom1;
}
void rna_Node_Viewer_shortcut_node_set(PointerRNA *ptr, int value)
{
bNode *curr_node = static_cast<bNode *>(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);

View File

@@ -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 {

View File

@@ -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()