diff options
-rw-r--r-- | source/blender/editors/include/UI_interface.h | 19 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_layout.c | 35 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_query.cc | 2 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_region_tooltip.c | 6 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_draw.cc | 106 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.hh | 2 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_templates.cc | 16 |
7 files changed, 150 insertions, 36 deletions
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index c14cce28052..3f58aab3e53 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -595,6 +595,7 @@ typedef void (*uiMenuHandleFunc)(struct bContext *C, void *arg, int event); */ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1); +typedef void *(*uiCopyArgFunc)(const void *arg); typedef void (*uiFreeArgFunc)(void *arg); /* interface_query.c */ @@ -2065,6 +2066,24 @@ void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv); void uiLayoutSetContextPointer(uiLayout *layout, const char *name, struct PointerRNA *ptr); struct bContextStore *uiLayoutGetContextStore(uiLayout *layout); void uiLayoutContextCopy(uiLayout *layout, struct bContextStore *context); + +/** + * Set tooltip function for all buttons in the layout. + * func, arg and free_arg are passed on to UI_but_func_tooltip_set, so their meaning is the same. + * + * \param func: The callback function that gets called to get tooltip content + * \param arg: An optional opaque pointer that gets passed to func + * \param free_arg: An optional callback for freeing arg (can be set to e.g. MEM_freeN) + * \param copy_arg: An optional callback for duplicating arg in case UI_but_func_tooltip_set + * is being called on multiple buttons (can be set to e.g. MEM_dupallocN). If set to NULL, arg will + * be passed as-is to all buttons. + */ +void uiLayoutSetTooltipFunc(uiLayout *layout, + uiButToolTipFunc func, + void *arg, + uiCopyArgFunc copy_arg, + uiFreeArgFunc free_arg); + /** * This is a bit of a hack but best keep it in one place at least. */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index ac807f06891..3e8bae6ea6b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -5681,6 +5681,41 @@ void uiLayoutContextCopy(uiLayout *layout, bContextStore *context) layout->context = CTX_store_add_all(&block->contexts, context); } +void uiLayoutSetTooltipFunc(uiLayout *layout, + uiButToolTipFunc func, + void *arg, + uiCopyArgFunc copy_arg, + uiFreeArgFunc free_arg) +{ + bool arg_used = false; + + LISTBASE_FOREACH (uiItem *, item, &layout->items) { + /* Each button will call free_arg for "its" argument, so we need to + * duplicate the allocation for each button after the first. */ + if (copy_arg != NULL && arg_used) { + arg = copy_arg(arg); + } + arg_used = true; + + if (item->type == ITEM_BUTTON) { + uiButtonItem *bitem = (uiButtonItem *)item; + if (bitem->but->type == UI_BTYPE_DECORATOR) { + continue; + } + UI_but_func_tooltip_set(bitem->but, func, arg, free_arg); + } + else { + uiLayoutSetTooltipFunc( + (uiLayout *)item, func, arg, copy_arg, free_arg); + } + } + + if (!arg_used) { + /* Free the original copy of arg in case the layout is empty. */ + free_arg(arg); + } +} + void uiLayoutSetContextFromBut(uiLayout *layout, uiBut *but) { if (but->opptr) { diff --git a/source/blender/editors/interface/interface_query.cc b/source/blender/editors/interface/interface_query.cc index 2767a184619..337b2852d57 100644 --- a/source/blender/editors/interface/interface_query.cc +++ b/source/blender/editors/interface/interface_query.cc @@ -61,7 +61,7 @@ bool ui_but_is_toggle(const uiBut *but) bool ui_but_is_interactive(const uiBut *but, const bool labeledit) { /* NOTE: #UI_BTYPE_LABEL is included for highlights, this allows drags. */ - if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr) { + if ((but->type == UI_BTYPE_LABEL) && but->dragpoin == nullptr && but->tip_func == nullptr) { return false; } if (ELEM(but->type, UI_BTYPE_ROUNDBOX, UI_BTYPE_SEPR, UI_BTYPE_SEPR_LINE, UI_BTYPE_LISTBOX)) { diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index 29553ff65d1..ca18ec14a54 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -788,8 +788,10 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, } /* Tip Label (only for buttons not already showing the label). - * Check prefix instead of comparing because the button may include the shortcut. */ - if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo)) { + * Check prefix instead of comparing because the button may include the shortcut. + * Buttons with dynamic tooltips also don't get their default label here since they + * can already provide more accurate and specific tooltip content. */ + if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_HEADER, diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 88ab7617932..013413adfe9 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -26,12 +26,10 @@ #include "BLI_span.hh" #include "BLI_string_ref.hh" #include "BLI_vector.hh" -#include "BLI_vector_set.hh" #include "BLT_translation.h" #include "BKE_context.h" -#include "BKE_geometry_set.hh" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -75,8 +73,6 @@ #include "node_intern.hh" /* own include */ using blender::GPointer; -using blender::fn::FieldCPPType; -using blender::fn::FieldInput; using blender::fn::GField; namespace geo_log = blender::nodes::geometry_nodes_eval_log; @@ -374,6 +370,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + node_socket_add_tooltip(&ntree, &node, nsock, row); + UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -504,6 +502,8 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node, const char *socket_label = nodeSocketLabel(nsock); nsock->typeinfo->draw((bContext *)&C, row, &sockptr, &nodeptr, IFACE_(socket_label)); + node_socket_add_tooltip(&ntree, &node, nsock, row); + UI_block_align_end(&block); UI_block_layout_resolve(&block, nullptr, &buty); @@ -943,6 +943,10 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, bNodeSocket &socket) { SpaceNode *snode = CTX_wm_space_node(C); + if (snode == nullptr) { + return {}; + }; + const geo_log::SocketLog *socket_log = geo_log::ModifierLog::find_socket_by_node_editor_context( *snode, node, socket); if (socket_log == nullptr) { @@ -970,6 +974,78 @@ static std::optional<std::string> create_socket_inspection_string(bContext *C, return ss.str(); } +static bool node_socket_has_tooltip(bNodeTree *ntree, bNodeSocket *socket) +{ + if (ntree->type == NTREE_GEOMETRY) { + return true; + } + + if (socket->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + return !socket_decl.description().is_empty(); + } + + return false; +} + +static char *node_socket_get_tooltip(bContext *C, + bNodeTree *ntree, + bNode *node, + bNodeSocket *socket) +{ + std::stringstream output; + if (socket->declaration != nullptr) { + const blender::nodes::SocketDeclaration &socket_decl = *socket->declaration; + blender::StringRef description = socket_decl.description(); + if (!description.is_empty()) { + output << TIP_(description.data()); + } + } + + if (ntree->type == NTREE_GEOMETRY) { + if (!output.str().empty()) { + output << ".\n\n"; + } + + std::optional<std::string> socket_inspection_str = create_socket_inspection_string( + C, *node, *socket); + if (socket_inspection_str.has_value()) { + output << *socket_inspection_str; + } + else { + output << TIP_("The socket value has not been computed yet"); + } + } + + if (output.str().empty()) { + output << nodeSocketLabel(socket); + } + + return BLI_strdup(output.str().c_str()); +} + +void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout) +{ + if (!node_socket_has_tooltip(ntree, sock)) { + return; + } + + SocketTooltipData *data = MEM_cnew<SocketTooltipData>(__func__); + data->ntree = ntree; + data->node = node; + data->socket = sock; + + uiLayoutSetTooltipFunc( + layout, + [](bContext *C, void *argN, const char *UNUSED(tip)) { + SocketTooltipData *data = static_cast<SocketTooltipData *>(argN); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); + }, + data, + MEM_dupallocN, + MEM_freeN); +} + static void node_socket_draw_nested(const bContext &C, bNodeTree &ntree, PointerRNA &node_ptr, @@ -1001,8 +1077,7 @@ static void node_socket_draw_nested(const bContext &C, size_id, outline_col_id); - if (ntree.type != NTREE_GEOMETRY) { - /* Only geometry nodes has socket value tooltips currently. */ + if (!node_socket_has_tooltip(&ntree, &sock)) { return; } @@ -1034,24 +1109,7 @@ static void node_socket_draw_nested(const bContext &C, but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - std::optional<std::string> socket_inspection_str = create_socket_inspection_string( - C, *data->node, *data->socket); - - std::stringstream output; - if (data->socket->declaration != nullptr) { - const blender::nodes::SocketDeclaration &socket_decl = *data->socket->declaration; - blender::StringRef description = socket_decl.description(); - if (!description.is_empty()) { - output << TIP_(description.data()) << ".\n\n"; - } - } - if (socket_inspection_str.has_value()) { - output << *socket_inspection_str; - } - else { - output << TIP_("The socket value has not been computed yet"); - } - return BLI_strdup(output.str().c_str()); + return node_socket_get_tooltip(C, data->ntree, data->node, data->socket); }, data, MEM_freeN); diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 44163cd5ae3..10d4ad36d95 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -134,6 +134,8 @@ void node_socket_color_get(const bContext &C, void node_draw_space(const bContext &C, ARegion ®ion); +void node_socket_add_tooltip(bNodeTree *ntree, bNode *node, bNodeSocket *sock, uiLayout *layout); + /** * Sort nodes by selection: unselected nodes first, then selected, * then the active node at the very end. Relative order is kept intact. diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index e92e0571157..58a313c328e 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -852,23 +852,19 @@ static void ui_node_draw_input( } } else { - row = uiLayoutRow(row, true); + uiLayout *sub = uiLayoutRow(row, true); - uiTemplateNodeLink(row, C, ntree, node, input); + uiTemplateNodeLink(sub, C, ntree, node, input); if (input->flag & SOCK_HIDE_VALUE) { add_dummy_decorator = true; } /* input not linked, show value */ else { - uiLayout *sub = row; - switch (input->type) { case SOCK_VECTOR: - if (input->type == SOCK_VECTOR) { - uiItemS(row); - sub = uiLayoutColumn(row, true); - } + uiItemS(sub); + sub = uiLayoutColumn(sub, true); ATTR_FALLTHROUGH; case SOCK_FLOAT: case SOCK_INT: @@ -884,7 +880,7 @@ static void ui_node_draw_input( if (node_tree->type == NTREE_GEOMETRY && snode != nullptr) { /* Only add the attribute search in the node editor, in other places there is not * enough context. */ - node_geometry_add_attribute_search_button(*C, *node, inputptr, *row); + node_geometry_add_attribute_search_button(*C, *node, inputptr, *sub); } else { uiItemR(sub, &inputptr, "default_value", 0, "", ICON_NONE); @@ -903,6 +899,8 @@ static void ui_node_draw_input( uiItemDecoratorR(split_wrapper.decorate_column, nullptr, nullptr, 0); } + node_socket_add_tooltip(ntree, node, input, row); + /* clear */ node->flag &= ~NODE_TEST; } |