diff --git a/scripts/startup/bl_operators/node.py b/scripts/startup/bl_operators/node.py index 6a240f21bb4..531fca6b339 100644 --- a/scripts/startup/bl_operators/node.py +++ b/scripts/startup/bl_operators/node.py @@ -27,6 +27,8 @@ from bpy.app.translations import ( pgettext_data as data_, ) +from nodeitems_builtins import node_tree_group_type + class NodeSetting(PropertyGroup): value: StringProperty( @@ -151,6 +153,12 @@ class NODE_OT_add_node(NodeAddOperator, Operator): @classmethod def description(cls, _context, properties): nodetype = properties["type"] + if nodetype in node_tree_group_type.values(): + for setting in properties.settings: + if setting.name == "node_tree": + node_group = eval(setting.value) + if node_group.description: + return node_group.description bl_rna = bpy.types.Node.bl_rna_get_subclass(nodetype) if bl_rna is not None: return tip_(bl_rna.description) diff --git a/scripts/startup/bl_ui/space_node.py b/scripts/startup/bl_ui/space_node.py index d5361491dfb..3f1fcdb2a9e 100644 --- a/scripts/startup/bl_ui/space_node.py +++ b/scripts/startup/bl_ui/space_node.py @@ -990,6 +990,11 @@ class NODE_PT_node_tree_properties(Panel): col.prop(group, "is_modifier") col.prop(group, "is_tool") + if group.asset_data: + layout.prop(group.asset_data, "description", text="Group Description") + else: + layout.prop(group, "description", text="Group Description") + # Grease Pencil properties class NODE_PT_annotation(AnnotationDataPanel, Panel): diff --git a/source/blender/blenkernel/BKE_asset.hh b/source/blender/blenkernel/BKE_asset.hh index 0b6eda3345c..56529bbf738 100644 --- a/source/blender/blenkernel/BKE_asset.hh +++ b/source/blender/blenkernel/BKE_asset.hh @@ -24,6 +24,7 @@ struct PreviewImage; using PreSaveFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); using OnMarkAssetFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); +using OnClearAssetDataFn = void (*)(void *asset_ptr, AssetMetaData *asset_data); struct AssetTypeInfo { /** @@ -32,6 +33,11 @@ struct AssetTypeInfo { */ PreSaveFn pre_save_fn; OnMarkAssetFn on_mark_asset_fn; + /** + * Should be called whenever a local asset gets cleared of its asset data but stays available + * otherwise, i.e. when an asset data-block is turned back into a normal data-block. + */ + OnClearAssetDataFn on_clear_asset_fn; }; AssetMetaData *BKE_asset_metadata_create(); diff --git a/source/blender/blenkernel/BKE_node.hh b/source/blender/blenkernel/BKE_node.hh index ebb72b975d3..609ef7a7093 100644 --- a/source/blender/blenkernel/BKE_node.hh +++ b/source/blender/blenkernel/BKE_node.hh @@ -236,6 +236,8 @@ struct bNodeType { /** Optional override for node class, used for drawing node header. */ int (*ui_class)(const bNode *node); + /** Optional dynamic description of what the node group does. */ + std::string (*ui_description_fn)(const bNode &node); /** Called when the node is updated in the editor. */ void (*updatefunc)(bNodeTree *ntree, bNode *node); diff --git a/source/blender/blenkernel/intern/action.cc b/source/blender/blenkernel/intern/action.cc index e4e1b6565bd..463b15edf8c 100644 --- a/source/blender/blenkernel/intern/action.cc +++ b/source/blender/blenkernel/intern/action.cc @@ -265,6 +265,7 @@ static void action_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_d static AssetTypeInfo AssetType_AC = { /*pre_save_fn*/ action_asset_metadata_ensure, /*on_mark_asset_fn*/ action_asset_metadata_ensure, + /*on_clear_asset_fn*/ nullptr, }; IDTypeInfo IDType_ID_AC = { diff --git a/source/blender/blenkernel/intern/lib_id.cc b/source/blender/blenkernel/intern/lib_id.cc index fc71bb22f5d..f41801a16fc 100644 --- a/source/blender/blenkernel/intern/lib_id.cc +++ b/source/blender/blenkernel/intern/lib_id.cc @@ -219,6 +219,12 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags) if (ID_IS_ASSET(id)) { if ((flags & LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR) != 0) { + const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_id(id); + if (idtype_info && idtype_info->asset_type_info && + idtype_info->asset_type_info->on_clear_asset_fn) + { + idtype_info->asset_type_info->on_clear_asset_fn(id, id->asset_data); + } BKE_asset_metadata_free(&id->asset_data); } else { diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index c26e8e96e6e..0f65d183e27 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -755,6 +755,7 @@ static void write_node_socket(BlendWriter *writer, const bNodeSocket *sock) void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) { BKE_id_blend_write(writer, &ntree->id); + BLO_write_string(writer, ntree->description); for (bNode *node : ntree->all_nodes()) { if (ntree->type == NTREE_SHADER && node->type == SH_NODE_BSDF_HAIR_PRINCIPLED) { @@ -1044,6 +1045,8 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree) ntree->runtime = MEM_new(__func__); BKE_ntree_update_tag_missing_runtime_data(ntree); + BLO_read_string(reader, &ntree->description); + BLO_read_struct_list(reader, bNode, &ntree->nodes); int i; LISTBASE_FOREACH_INDEX (bNode *, node, &ntree->nodes, i) { @@ -1288,12 +1291,32 @@ void node_update_asset_metadata(bNodeTree &node_tree) static void node_tree_asset_pre_save(void *asset_ptr, AssetMetaData * /*asset_data*/) { - node_update_asset_metadata(*static_cast(asset_ptr)); + bNodeTree &ntree = *static_cast(asset_ptr); + node_update_asset_metadata(ntree); } -static void node_tree_asset_on_mark_asset(void *asset_ptr, AssetMetaData * /*asset_data*/) +static void node_tree_asset_on_mark_asset(void *asset_ptr, AssetMetaData *asset_data) { - node_update_asset_metadata(*static_cast(asset_ptr)); + bNodeTree &ntree = *static_cast(asset_ptr); + node_update_asset_metadata(ntree); + + /* Copy node tree description to asset description so that the user does not have to write it + * again. */ + if (!asset_data->description) { + asset_data->description = BLI_strdup_null(ntree.description); + } +} + +static void node_tree_asset_on_clear_asset(void *asset_ptr, AssetMetaData *asset_data) +{ + bNodeTree &ntree = *static_cast(asset_ptr); + + /* Copy asset description to node tree description so that it is not lost when the asset data is + * removed. */ + if (asset_data->description) { + MEM_SAFE_FREE(ntree.description); + ntree.description = BLI_strdup_null(asset_data->description); + } } } // namespace blender::bke @@ -1301,6 +1324,7 @@ static void node_tree_asset_on_mark_asset(void *asset_ptr, AssetMetaData * /*ass static AssetTypeInfo AssetType_NT = { /*pre_save_fn*/ blender::bke::node_tree_asset_pre_save, /*on_mark_asset_fn*/ blender::bke::node_tree_asset_on_mark_asset, + /*on_clear_asset_fn*/ blender::bke::node_tree_asset_on_clear_asset, }; IDTypeInfo IDType_ID_NT = { diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 9dc7d91281e..49bb0c0bea0 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -1064,6 +1064,7 @@ static void object_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_d static AssetTypeInfo AssetType_OB = { /*pre_save_fn*/ object_asset_metadata_ensure, /*on_mark_asset_fn*/ object_asset_metadata_ensure, + /*on_clear_asset_fn*/ nullptr, }; IDTypeInfo IDType_ID_OB = { diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index 9fcfc8e7a5e..18478bd32f2 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -69,6 +69,14 @@ bool clear_id(ID *id) if (!id->asset_data) { return false; } + + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); + if (AssetTypeInfo *type_info = id_type_info->asset_type_info) { + if (type_info->on_clear_asset_fn) { + type_info->on_clear_asset_fn(id, id->asset_data); + } + } + BKE_asset_metadata_free(&id->asset_data); id_fake_user_clear(id); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 66286e0894f..e3668b2f364 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -3448,6 +3448,17 @@ static void node_draw_basis(const bContext &C, 0, 0, TIP_(node.typeinfo->ui_description)); + UI_but_func_tooltip_set( + but, + [](bContext * /*C*/, void *arg, const char *tip) -> std::string { + const bNode &node = *static_cast(arg); + if (node.typeinfo->ui_description_fn) { + return node.typeinfo->ui_description_fn(node); + } + return StringRef(tip); + }, + const_cast(&node), + nullptr); if (node.flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 0f4afd05b4f..9008852b8cd 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -663,6 +663,8 @@ typedef struct bNodeTree { struct bNodeTreeType *typeinfo; /** Runtime type identifier. */ char idname[64]; + /** User-defined description of the node tree. */ + char *description; /** Grease pencil data. */ struct bGPdata *gpd; diff --git a/source/blender/makesrna/intern/rna_nodetree.cc b/source/blender/makesrna/intern/rna_nodetree.cc index 5a5279a5aa0..8b22ca75fc7 100644 --- a/source/blender/makesrna/intern/rna_nodetree.cc +++ b/source/blender/makesrna/intern/rna_nodetree.cc @@ -10372,6 +10372,9 @@ static void rna_def_nodetree(BlenderRNA *brna) prop, "", "The current location (offset) of the view for this Node Tree"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_ui_text(prop, "Description", "Description of the node tree"); + /* AnimData */ rna_def_animdata_common(srna); diff --git a/source/blender/nodes/composite/nodes/node_composite_common.cc b/source/blender/nodes/composite/nodes/node_composite_common.cc index d326f115f01..af124159c0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.cc +++ b/source/blender/nodes/composite/nodes/node_composite_common.cc @@ -27,6 +27,7 @@ void register_node_type_cmp_group() ntype.poll = cmp_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; + ntype.ui_description_fn = node_group_ui_description; ntype.rna_ext.srna = RNA_struct_find("CompositorNodeGroup"); BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_common.cc b/source/blender/nodes/geometry/nodes/node_geo_common.cc index 84d6c693e4f..c6c846fafae 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_common.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_common.cc @@ -24,6 +24,7 @@ static void register_node_type_geo_group() ntype.poll = geo_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; + ntype.ui_description_fn = node_group_ui_description; ntype.rna_ext.srna = RNA_struct_find("GeometryNodeGroup"); BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); diff --git a/source/blender/nodes/intern/node_common.cc b/source/blender/nodes/intern/node_common.cc index 3674c1d68c1..0c0548d4833 100644 --- a/source/blender/nodes/intern/node_common.cc +++ b/source/blender/nodes/intern/node_common.cc @@ -9,6 +9,7 @@ #include #include +#include "DNA_asset_types.h" #include "DNA_node_types.h" #include "BLI_listbase.h" @@ -93,6 +94,23 @@ bool node_group_poll_instance(const bNode *node, return nodeGroupPoll(nodetree, grouptree, disabled_hint); } +std::string node_group_ui_description(const bNode &node) +{ + if (!node.id) { + return ""; + } + const bNodeTree *group = reinterpret_cast(node.id); + if (group->id.asset_data) { + if (group->id.asset_data->description) { + return group->id.asset_data->description; + } + } + if (!group->description) { + return ""; + } + return group->description; +} + bool nodeGroupPoll(const bNodeTree *nodetree, const bNodeTree *grouptree, const char **r_disabled_hint) diff --git a/source/blender/nodes/intern/node_common.h b/source/blender/nodes/intern/node_common.h index 0779f0132f7..92a039dd157 100644 --- a/source/blender/nodes/intern/node_common.h +++ b/source/blender/nodes/intern/node_common.h @@ -33,4 +33,8 @@ void ntree_update_reroute_nodes(struct bNodeTree *ntree); #ifdef __cplusplus } + +# include + +std::string node_group_ui_description(const bNode &node); #endif diff --git a/source/blender/nodes/shader/nodes/node_shader_common.cc b/source/blender/nodes/shader/nodes/node_shader_common.cc index c21b6daf0be..cff6980934c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_common.cc +++ b/source/blender/nodes/shader/nodes/node_shader_common.cc @@ -91,6 +91,7 @@ void register_node_type_sh_group() ntype.poll = sh_node_poll_default; ntype.poll_instance = node_group_poll_instance; ntype.insert_link = node_insert_link_default; + ntype.ui_description_fn = node_group_ui_description; ntype.rna_ext.srna = RNA_struct_find("ShaderNodeGroup"); BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype);