UI: Customizable quick label tooltip for buttons #110016

Closed
Julian Eisel wants to merge 1 commits from JulianEisel/blender:temp-but-quick-label into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
5 changed files with 56 additions and 7 deletions

View File

@ -8,6 +8,11 @@
#pragma once
#ifdef __cplusplus
# include <functional>
# include <string>
#endif
#include "BLI_compiler_attrs.h"
#include "BLI_string_utf8_symbols.h"
#include "BLI_sys_types.h" /* size_t */
@ -289,8 +294,11 @@ enum {
UI_BUT_TEXT_RIGHT = 1 << 3,
/** Prevent the button to show any tooltip. */
UI_BUT_NO_TOOLTIP = 1 << 4,
/** Always show a tooltip label (the short tooltip that appears faster than the full one and only
* shows the label) even the label may already be visible. */
UI_BUT_FORCE_TOOLTIP_LABEL = 1 << 5,
/** Do not add the usual horizontal padding for text drawing. */
UI_BUT_NO_TEXT_PADDING = 1 << 5,
UI_BUT_NO_TEXT_PADDING = 1 << 6,
/* Button align flag, for drawing groups together.
* Used in 'uiBlock.flag', take care! */
@ -1386,6 +1394,8 @@ typedef enum uiStringInfoType {
BUT_GET_RNASTRUCT_IDENTIFIER,
BUT_GET_RNAENUM_IDENTIFIER,
BUT_GET_LABEL,
/** Sometimes the button doesn't have a label itself, but provides one for the tooltip. */
BUT_GET_TIP_LABEL,
BUT_GET_RNA_LABEL,
BUT_GET_RNAENUM_LABEL,
BUT_GET_RNA_LABEL_CONTEXT, /* Context specified in CTX_XXX_ macros are just unreachable! */
@ -1746,6 +1756,10 @@ void UI_but_func_drawextra_set(
void UI_but_func_menu_step_set(uiBut *but, uiMenuStepFunc func);
#ifdef __cplusplus
void UI_but_func_tooltip_label_set(uiBut *but, std::function<std::string(const uiBut *but)> func);
#endif
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg);
/**
* Recreate tool-tip (use to update dynamic tips)

View File

@ -856,7 +856,7 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
/* flags from the buttons we want to refresh, may want to add more here... */
const int flag_copy = UI_BUT_REDALERT | UI_HAS_ICON | UI_SELECT_DRAW;
const int drawflag_copy = 0; /* None currently. */
const int drawflag_copy = UI_BUT_FORCE_TOOLTIP_LABEL;
/* still stuff needs to be copied */
oldbut->rect = but->rect;
@ -878,6 +878,7 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but)
std::swap(oldbut->tip_func, but->tip_func);
std::swap(oldbut->tip_arg, but->tip_arg);
std::swap(oldbut->tip_arg_free, but->tip_arg_free);
std::swap(oldbut->tip_label_func, but->tip_label_func);
oldbut->flag = (oldbut->flag & ~flag_copy) | (but->flag & flag_copy);
oldbut->drawflag = (oldbut->drawflag & ~drawflag_copy) | (but->drawflag & drawflag_copy);
@ -6076,6 +6077,11 @@ void UI_but_func_menu_step_set(uiBut *but, uiMenuStepFunc func)
but->menu_step_func = func;
}
void UI_but_func_tooltip_label_set(uiBut *but, std::function<std::string(const uiBut *but)> func)
{
but->tip_label_func = func;
}
void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *arg, uiFreeArgFunc free_arg)
{
but->tip_func = func;
@ -6577,6 +6583,17 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...)
uiStringInfoType type = si->type;
char *tmp = nullptr;
if (type == BUT_GET_TIP_LABEL) {
if (but->tip_label_func) {
const std::string tooltip_label = but->tip_label_func(but);
tmp = BLI_strdupn(tooltip_label.c_str(), tooltip_label.size());
}
/* Fallback to the regular label. */
else {
type = BUT_GET_LABEL;
}
}
if (type == BUT_GET_LABEL) {
if (but->str && but->str[0]) {
const char *str_sep;

View File

@ -219,6 +219,7 @@ struct uiBut {
uiButToolTipFunc tip_func = nullptr;
void *tip_arg = nullptr;
uiFreeArgFunc tip_arg_free = nullptr;
std::function<std::string(const uiBut *)> tip_label_func;
/** info on why button is disabled, displayed in tooltip */
const char *disabled_info = nullptr;

View File

@ -144,11 +144,19 @@ bool UI_but_is_tool(const uiBut *but)
bool UI_but_has_tooltip_label(const uiBut *but)
{
if (but->drawflag & UI_BUT_FORCE_TOOLTIP_LABEL) {
Review

It may be better to use the new method completely, so that UI_BUT_FORCE_TOOLTIP_LABEL becomes UI_BUT_HAS_TOOLTIP_LABEL. That decentralizes the logic in a way that's more consistent and more extensible.

It also avoids adding complexity as this PR currently does by adding a new way to do the same thing.

It may be better to use the new method completely, so that `UI_BUT_FORCE_TOOLTIP_LABEL` becomes `UI_BUT_HAS_TOOLTIP_LABEL`. That decentralizes the logic in a way that's more consistent and more extensible. It also avoids adding complexity as this PR currently does by adding a new way to do the same thing.
Review

Submitted a separate PR for this: #110200. Both solutions are fine with me, but the new one is a bit more risky since it changes existing code.

Submitted a separate PR for this: #110200. Both solutions are fine with me, but the new one is a bit more risky since it changes existing code.

The other PR still includes UI_BUT_FORCE_TOOLTIP_LABEL,. Wasn't it supposed to be completely replaced by UI_BUT_HAS_TOOLTIP_LABEL?

The other PR still includes `UI_BUT_FORCE_TOOLTIP_LABEL`,. Wasn't it supposed to be completely replaced by `UI_BUT_HAS_TOOLTIP_LABEL`?
Review

That was to skip the !STRPREFIX(but->drawstr, but_tip_label.strinfo) condition in the tooltip code, but I think we can remove this entirely. It was a weak check and didn't account for label clipping.

That was to skip the `!STRPREFIX(but->drawstr, but_tip_label.strinfo)` condition in the tooltip code, but I think we can remove this entirely. It was a weak check and didn't account for label clipping.
return true;
}
/* No tooltip label if the button itself shows a label already. */
if (but->drawstr[0] != '\0') {
return false;
}
if (but->tip_label_func) {
return true;
}
if (UI_but_is_tool(but)) {
return !ui_block_is_popover(but->block);
}

View File

@ -751,6 +751,7 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
const bool is_label)
{
uiStringInfo but_label = {BUT_GET_LABEL, nullptr};
uiStringInfo but_tip_label = {BUT_GET_TIP_LABEL, nullptr};
uiStringInfo but_tip = {BUT_GET_TIP, nullptr};
uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, nullptr};
uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, nullptr};
@ -770,20 +771,22 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
if (extra_icon) {
if (is_label) {
UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &enum_label, nullptr);
UI_but_extra_icon_string_info_get(C, extra_icon, &but_tip_label, &enum_label, nullptr);
}
else {
UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, nullptr);
UI_but_extra_icon_string_info_get(
C, extra_icon, &but_label, &but_tip_label, &but_tip, &op_keymap, nullptr);
}
}
else {
if (is_label) {
UI_but_string_info_get(C, but, &but_label, &enum_label, nullptr);
UI_but_string_info_get(C, but, &but_tip_label, &enum_label, nullptr);
}
else {
UI_but_string_info_get(C,
but,
&but_label,
&but_tip_label,
&but_tip,
&enum_label,
&enum_tip,
@ -799,11 +802,14 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
* Check prefix instead of comparing because the button may include the shortcut.
* Buttons with dynamic tool-tips also don't get their default label here since they
* can already provide more accurate and specific tool-tip content. */
if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) {
const bool force_tip_label = but->drawflag & UI_BUT_FORCE_TOOLTIP_LABEL;
if (but_tip_label.strinfo &&
(force_tip_label || (!STRPREFIX(but->drawstr, but_tip_label.strinfo) && !but->tip_func)))
{
uiTooltipField *field = text_field_add(
data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal);
field->text = BLI_strdup(but_label.strinfo);
field->text = BLI_strdup(but_tip_label.strinfo);
}
/* Tip */
@ -986,6 +992,9 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
if (but_label.strinfo) {
MEM_freeN(but_label.strinfo);
}
if (but_tip_label.strinfo) {
MEM_freeN(but_tip_label.strinfo);
}
if (but_tip.strinfo) {
MEM_freeN(but_tip.strinfo);
}