Nodes: Add dropdown to select group socket subtype #105614

Merged
Hans Goudey merged 5 commits from HooglyBoogly/blender:node-group-io-subtype into main 2023-04-03 18:23:42 +02:00
11 changed files with 248 additions and 7 deletions

View File

@ -889,6 +889,23 @@ class NodeTreeInterfacePanel(Panel):
)
props.in_out = in_out
with context.temp_override(interface_socket=active_socket):
if bpy.ops.node.tree_socket_change_subtype.poll():
layout_row = layout.row(align=True)
layout_split = layout_row.split(factor=0.4, align=True)
label_column = layout_split.column(align=True)
label_column.alignment = 'RIGHT'
label_column.label(text="Subtype")
property_row = layout_split.row(align=True)
property_row.context_pointer_set("interface_socket", active_socket)
props = property_row.operator_menu_enum(
"node.tree_socket_change_subtype",
"socket_subtype",
text=active_socket.bl_subtype_label if active_socket.bl_subtype_label else active_socket.bl_idname
)
layout.use_property_split = True
layout.use_property_decorate = False

View File

@ -158,6 +158,8 @@ typedef struct bNodeSocketType {
char idname[64];
/* Type label */
char label[64];
/* Subtype label */
char subtype_label[64];
void (*draw)(struct bContext *C,
struct uiLayout *layout,
@ -634,6 +636,7 @@ bool nodeIsStaticSocketType(const struct bNodeSocketType *stype);
const char *nodeStaticSocketType(int type, int subtype);
const char *nodeStaticSocketInterfaceType(int type, int subtype);
const char *nodeStaticSocketLabel(int type, int subtype);
const char *nodeSocketSubTypeLabel(int subtype);
/* Helper macros for iterating over node types. */
#define NODE_SOCKET_TYPES_BEGIN(stype) \

View File

@ -778,6 +778,11 @@ inline const bNodeSocket *bNodeSocket::internal_link_input() const
return this->runtime->internal_link_input;
}
template<typename T> T *bNodeSocket::default_value_typed()
{
return static_cast<T *>(this->default_value);
}
template<typename T> const T *bNodeSocket::default_value_typed() const
{
return static_cast<const T *>(this->default_value);

View File

@ -68,6 +68,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
#include "RNA_prototypes.h"
#include "NOD_common.h"
@ -1466,6 +1467,15 @@ const char *nodeSocketTypeLabel(const bNodeSocketType *stype)
return stype->label[0] != '\0' ? stype->label : RNA_struct_ui_name(stype->ext_socket.srna);
}
const char *nodeSocketSubTypeLabel(int subtype)
{
const char *name;
if (RNA_enum_name(rna_enum_property_subtype_items, subtype, &name)) {
return name;
}
return "";
}
bNodeSocket *nodeFindSocket(const bNode *node,
const eNodeSocketInOut in_out,
const char *identifier)
@ -1673,9 +1683,47 @@ void nodeModifySocketType(bNodeTree *ntree,
}
if (sock->default_value) {
socket_id_user_decrement(sock);
MEM_freeN(sock->default_value);
sock->default_value = nullptr;
if (sock->type != socktype->type) {
/* Only reallocate the default value if the type changed so that UI data like min and max
* isn't removed. This assumes that the default value is stored in the same format for all
* socket types with the same #eNodeSocketDatatype. */
socket_id_user_decrement(sock);
MEM_freeN(sock->default_value);
sock->default_value = nullptr;
}
else {
/* Update the socket subtype when the storage isn't freed and recreated. */
switch (eNodeSocketDatatype(sock->type)) {
case SOCK_FLOAT: {
sock->default_value_typed<bNodeSocketValueFloat>()->subtype = socktype->subtype;
break;
}
case SOCK_VECTOR: {
sock->default_value_typed<bNodeSocketValueVector>()->subtype = socktype->subtype;
break;
}
case SOCK_INT: {
sock->default_value_typed<bNodeSocketValueInt>()->subtype = socktype->subtype;
break;
}
case SOCK_STRING: {
sock->default_value_typed<bNodeSocketValueString>()->subtype = socktype->subtype;
break;
}
case SOCK_RGBA:
case SOCK_SHADER:
case SOCK_BOOLEAN:
case SOCK_CUSTOM:
case __SOCK_MESH:
case SOCK_OBJECT:
case SOCK_IMAGE:
case SOCK_GEOMETRY:
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
break;
}
}
}
BLI_strncpy(sock->idname, idname, sizeof(sock->idname));

View File

@ -1658,8 +1658,11 @@ void uiItemsFullEnumO(uiLayout *layout,
#endif
}
else {
RNA_property_enum_items_gettexted(
static_cast<bContext *>(block->evil_C), &ptr, prop, &item_array, &totitem, &free);
bContext *C = static_cast<bContext *>(block->evil_C);
bContextStore *previous_ctx = CTX_store_get(C);
CTX_store_set(C, layout->context);
RNA_property_enum_items_gettexted(C, &ptr, prop, &item_array, &totitem, &free);
CTX_store_set(C, previous_ctx);
}
/* add items */

View File

@ -30,6 +30,7 @@
#include "BKE_scene.h"
#include "BKE_workspace.h"
#include "BLI_set.hh"
#include "BLT_translation.h"
#include "DEG_depsgraph.h"
@ -2109,7 +2110,7 @@ void NODE_OT_node_copy_color(wmOperatorType *ot)
/** \name Node-Tree Add Interface Socket Operator
* \{ */
static bNodeSocket *ntree_get_active_interface_socket(ListBase *lb)
static bNodeSocket *ntree_get_active_interface_socket(const ListBase *lb)
{
LISTBASE_FOREACH (bNodeSocket *, socket, lb) {
if (socket->flag & SELECT) {
@ -2254,7 +2255,6 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
/* Don't handle sub-types for now. */
nodeModifySocketType(ntree, nullptr, iosock, socket_type->idname);
/* Need the extra update here because the loop above does not check for valid links in the node
@ -2333,6 +2333,155 @@ void NODE_OT_tree_socket_change_type(wmOperatorType *ot)
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Change Interface Socket Subtype Operator
* \{ */
static int ntree_socket_change_subtype_exec(bContext *C, wmOperator *op)
{
Main *main = CTX_data_main(C);
const int socket_subtype = RNA_enum_get(op->ptr, "socket_subtype");
PointerRNA io_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
bNodeSocket *io_socket = static_cast<bNodeSocket *>(io_socket_ptr.data);
if (!io_socket) {
return OPERATOR_CANCELLED;
}
bNodeTree &node_tree = *reinterpret_cast<bNodeTree *>(io_socket_ptr.owner_id);
ListBase *sockets;
if (node_tree.interface_inputs().contains(io_socket)) {
sockets = &node_tree.inputs;
}
else if (node_tree.interface_outputs().contains(io_socket)) {
sockets = &node_tree.outputs;
}
else {
/* The interface socket should be in the inputs or outputs. */
BLI_assert_unreachable();
return OPERATOR_CANCELLED;
}
nodeModifySocketTypeStatic(&node_tree, nullptr, io_socket, io_socket->type, socket_subtype);
/* Deactivate sockets. */
LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) {
socket_iter->flag &= ~SELECT;
}
/* Make the new socket active. */
io_socket->flag |= SELECT;
BKE_ntree_update_tag_interface(&node_tree);
ED_node_tree_propagate_change(C, main, &node_tree);
return OPERATOR_FINISHED;
}
static Set<int> socket_type_get_subtypes(const eNodeSocketDatatype type)
{
switch (type) {
case SOCK_FLOAT:
return {PROP_PERCENTAGE,
PROP_FACTOR,
PROP_ANGLE,
PROP_TIME,
PROP_TIME_ABSOLUTE,
PROP_DISTANCE,
PROP_NONE};
case SOCK_INT:
return {PROP_PERCENTAGE, PROP_FACTOR, PROP_NONE};
case SOCK_VECTOR:
return {PROP_TRANSLATION,
/* Direction doesn't seem to work. */
// PROP_DIRECTION,
PROP_VELOCITY,
PROP_ACCELERATION,
PROP_EULER,
PROP_XYZ,
PROP_NONE};
default:
return {};
}
}
static const EnumPropertyItem *socket_change_subtype_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) {
return DummyRNA_NULL_items;
}
SpaceNode *snode = CTX_wm_space_node(C);
if (!snode || !snode->edittree) {
return DummyRNA_NULL_items;
}
PointerRNA active_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
const bNodeSocket *active_socket = static_cast<const bNodeSocket *>(active_socket_ptr.data);
if (!active_socket) {
return DummyRNA_NULL_items;
}
const Set<int> subtypes = socket_type_get_subtypes(eNodeSocketDatatype(active_socket->type));
if (subtypes.is_empty()) {
return DummyRNA_NULL_items;
}
EnumPropertyItem *items = NULL;
int items_count = 0;
for (const EnumPropertyItem *item = rna_enum_property_subtype_items; item->name != NULL;
item++) {
if (subtypes.contains(item->value)) {
RNA_enum_item_add(&items, &items_count, item);
}
}
if (items_count == 0) {
return DummyRNA_NULL_items;
}
RNA_enum_item_end(&items, &items_count);
*r_free = true;
return items;
}
static bool ntree_socket_change_subtype_poll(bContext *C)
{
if (!ED_operator_node_editable(C)) {
return false;
}
PointerRNA io_socket_ptr = CTX_data_pointer_get_type(
C, "interface_socket", &RNA_NodeSocketInterface);
const bNodeSocket *io_socket = static_cast<const bNodeSocket *>(io_socket_ptr.data);
if (!io_socket) {
return false;
}
return !socket_type_get_subtypes(eNodeSocketDatatype(io_socket->type)).is_empty();
}
void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot)
{
ot->name = "Change Node Tree Socket Subtype";
ot->description = "Change the subtype of a socket of the active node tree";
ot->idname = "NODE_OT_tree_socket_change_subtype";
ot->invoke = WM_menu_invoke;
ot->exec = ntree_socket_change_subtype_exec;
ot->poll = ntree_socket_change_subtype_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(
ot->srna, "socket_subtype", DummyRNA_DEFAULT_items, 0, "Socket Subtype", "");
RNA_def_enum_funcs(ot->prop, socket_change_subtype_itemf);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node-Tree Move Interface Socket Operator
* \{ */

View File

@ -356,6 +356,7 @@ void NODE_OT_clipboard_paste(wmOperatorType *ot);
void NODE_OT_tree_socket_add(wmOperatorType *ot);
void NODE_OT_tree_socket_remove(wmOperatorType *ot);
void NODE_OT_tree_socket_change_type(wmOperatorType *ot);
void NODE_OT_tree_socket_change_subtype(wmOperatorType *ot);
void NODE_OT_tree_socket_move(wmOperatorType *ot);
void NODE_OT_shader_script_update(wmOperatorType *ot);

View File

@ -108,6 +108,7 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_tree_socket_add);
WM_operatortype_append(NODE_OT_tree_socket_remove);
WM_operatortype_append(NODE_OT_tree_socket_change_type);
WM_operatortype_append(NODE_OT_tree_socket_change_subtype);
WM_operatortype_append(NODE_OT_tree_socket_move);
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);

View File

@ -176,6 +176,7 @@ typedef struct bNodeSocket {
bool is_output() const;
/** Utility to access the value of the socket. */
template<typename T> T *default_value_typed();
template<typename T> const T *default_value_typed() const;
/* The following methods are only available when #bNodeTree.ensure_topology_cache has been

View File

@ -11216,6 +11216,12 @@ static void rna_def_node_socket(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Type Label", "Label to display for the socket type in the UI");
prop = RNA_def_property(srna, "bl_subtype_label", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "typeinfo->subtype_label");
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(
prop, "Subtype Label", "Label to display for the socket subtype in the UI");
/* draw socket */
func = RNA_def_function(srna, "draw", NULL);
RNA_def_function_ui_description(func, "Draw socket");
@ -11333,6 +11339,11 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Type Label", "Label to display for the socket type in the UI");
prop = RNA_def_property(srna, "bl_subtype_label", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "typeinfo->subtype_label");
RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL);
RNA_def_property_ui_text(prop, "Subtype Label", "Label to display for the socket subtype in the UI");
func = RNA_def_function(srna, "draw", NULL);
RNA_def_function_ui_description(func, "Draw template settings");
RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);

View File

@ -537,6 +537,7 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype)
const char *socket_idname = nodeStaticSocketType(type, subtype);
const char *interface_idname = nodeStaticSocketInterfaceType(type, subtype);
const char *socket_label = nodeStaticSocketLabel(type, subtype);
const char *socket_subtype_label = nodeSocketSubTypeLabel(subtype);
bNodeSocketType *stype;
StructRNA *srna;
@ -544,6 +545,7 @@ static bNodeSocketType *make_standard_socket_type(int type, int subtype)
stype->free_self = (void (*)(bNodeSocketType * stype)) MEM_freeN;
BLI_strncpy(stype->idname, socket_idname, sizeof(stype->idname));
BLI_strncpy(stype->label, socket_label, sizeof(stype->label));
BLI_strncpy(stype->subtype_label, socket_subtype_label, sizeof(stype->subtype_label));
/* set the RNA type
* uses the exact same identifier as the socket type idname */