From 0d62612da12fae269a9bd45796c6c6d33213cf13 Mon Sep 17 00:00:00 2001 From: sleeptightAnsiC <91839286+sleeptightAnsiC@users.noreply.github.com> Date: Tue, 17 Feb 2026 11:20:14 +0100 Subject: [PATCH] feat(tooltip): add new variation of tooltip with offset Adds new variation of nk_tooltip_* that let's you specify the relative position to the cursor and the offset. This was done in order to prevent cursor from covering the content of tooltip. The default tooltip should now provide more sane offset (still not perfect!) Also, added a tooltip editor to demo overview in order to test it. Parts of this code were originally written by Robert Winkler (thanks!) Origin: https://github.com/Immediate-Mode-UI/Nuklear/pull/881 Co-authored-by: Robert Winkler --- demo/common/overview.c | 83 +++++++++++++++++++++++++++++-- nuklear.h | 108 ++++++++++++++++++++++++++++++++++++++--- src/CHANGELOG | 2 + src/nuklear.h | 15 ++++++ src/nuklear_tooltip.c | 91 +++++++++++++++++++++++++++++++--- 5 files changed, 278 insertions(+), 21 deletions(-) diff --git a/demo/common/overview.c b/demo/common/overview.c index 87d5f686f..3dae90b14 100644 --- a/demo/common/overview.c +++ b/demo/common/overview.c @@ -839,12 +839,85 @@ overview(struct nk_context *ctx) } else popup_active = nk_false; } - /* tooltip */ - nk_layout_row_static(ctx, 30, 150, 1); + /* tooltips */ + nk_layout_row_static(ctx, 30, 400, 1); bounds = nk_widget_bounds(ctx); - nk_label(ctx, "Hover me for tooltip", NK_TEXT_LEFT); - if (nk_input_is_mouse_hovering_rect(in, bounds)) - nk_tooltip(ctx, "This is a tooltip"); + nk_label(ctx, "Hover for default tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + nk_tooltip(ctx, "This is very boring default tooltip..."); + } + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover for Gnome-like tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + struct nk_vec2 offset = { 0, 15 }; + nk_tooltip_offset(ctx, "Gnome centers above cursor with greater y offset", NK_TOOLTIP_ABOVE, offset); + } + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover for above-on-left tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + struct nk_vec2 offset = { 0, 0 }; + nk_tooltip_offset(ctx, "above-on-left from cursor with 0:0 offset", NK_TOOLTIP_ABOVE|NK_TOOLTIP_ON_LEFT, offset); + } + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover for MAGIC!", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + static double accum_time_seconds = 0.0; + const double speed = 3.0, radius = 50.0; + struct nk_vec2 offset; + offset.x = radius * cos(accum_time_seconds * speed); + offset.y = radius * sin(accum_time_seconds * speed); + nk_tooltip_offset(ctx, "WOW!", NK_TOOLTIP_ABS_OFFSET, offset); + accum_time_seconds += (double)(ctx->delta_time_seconds); + } + + /* editor for custom tooltip */ + { + static char text_buf[64] = {0}; + static int text_len = 0; + static int text_initialized = 0; + static nk_flags tooltip = 0; + static struct nk_vec2 offset = {0}; + if (!text_initialized) { + const char text_default[] = "you can customize this!"; + NK_ASSERT(sizeof(text_default) < sizeof(text_buf)); + memcpy(text_buf, text_default, sizeof(text_default)); + text_len = sizeof(text_default) - 1; + text_initialized = 1; + } + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover for custom tooltip (you can customize it below)", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + nk_tooltip_offset(ctx, text_buf, tooltip, offset); + } + nk_layout_row_dynamic(ctx, 1, 1); + nk_rule_horizontal(ctx, nk_white, nk_true); + nk_layout_row_dynamic(ctx, 30, 2); + nk_label(ctx, "custom tooltip text:", NK_TEXT_LEFT); + nk_edit_string(ctx, NK_EDIT_FIELD, text_buf, &text_len, sizeof(text_buf), nk_filter_default); + NK_ASSERT(text_len < (int)sizeof(text_buf)); + text_buf[text_len] = '\0'; /* TODO: why nk_edit_string is NOT setting this on its own? */ + nk_layout_row_dynamic(ctx, 30, 1); + #define TOOLTIP_FOREACH_FLAG(BODY) \ + BODY(ABOVE) \ + BODY(BELOW) \ + BODY(ON_LEFT) \ + BODY(ON_RIGHT) \ + BODY(ABS_OFFSET) + #define TOOLTIP_CHECKBOX_FLAG(FLAG) \ + { \ + nk_bool checked = !!(tooltip & NK_TOOLTIP_##FLAG); \ + nk_checkbox_label_align(ctx, "custom tooltip flag: " #FLAG, &checked, NK_WIDGET_RIGHT, NK_TEXT_LEFT);\ + tooltip = checked ? (tooltip | NK_TOOLTIP_##FLAG) : (tooltip & ~NK_TOOLTIP_##FLAG); \ + } + TOOLTIP_FOREACH_FLAG(TOOLTIP_CHECKBOX_FLAG) + #undef TOOLTIP_FOREACH_FLAG + #undef TOOLTIP_CHECKBOX_FLAG + nk_layout_row_dynamic(ctx, 30, 2); + nk_label(ctx, "custom tooltip offset", NK_TEXT_LEFT); + nk_property_float(ctx, "x", -100.0f, &offset.x, 100.0f, 5.0f, 0.5f); + nk_label(ctx, "custom tooltip offset", NK_TEXT_LEFT); + nk_property_float(ctx, "y", -100.0f, &offset.y, 100.0f, 5.0f, 0.5f); + } nk_tree_pop(ctx); } diff --git a/nuklear.h b/nuklear.h index ed61bbd85..1ac0d86d2 100644 --- a/nuklear.h +++ b/nuklear.h @@ -3826,12 +3826,27 @@ NK_API void nk_contextual_end(struct nk_context*); * TOOLTIP * * ============================================================================= */ +enum nk_tooltip_flags { + /**!< tells where tooltip should appear relatively to the cursor, can be combined (e.g. BELOW|ON_RIGHT) */ + NK_TOOLTIP_ABOVE = NK_FLAG(0), + NK_TOOLTIP_BELOW = NK_FLAG(1), + NK_TOOLTIP_ON_LEFT = NK_FLAG(2), + NK_TOOLTIP_ON_RIGHT = NK_FLAG(3), + /**!< if set, the offset will be absolute, instead of relative */ + NK_TOOLTIP_ABS_OFFSET = NK_FLAG(4) + /* FIXME: https://github.com/Immediate-Mode-UI/Nuklear/issues/899 */ + /*NK_TOOLTIP_CLAMP_IN_SCREEN = NK_FLAG(5),*/ +}; NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, nk_flags tooltip, struct nk_vec2); #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2); NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); +NK_API void nk_tooltipf_offset(struct nk_context*, nk_flags tooltip, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_tooltipfv_offset(struct nk_context*, nk_flags tooltip, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4); #endif NK_API nk_bool nk_tooltip_begin(struct nk_context*, float width); +NK_API nk_bool nk_tooltip_begin_offset(struct nk_context*, float width, nk_flags tooltip, struct nk_vec2); NK_API void nk_tooltip_end(struct nk_context*); /* ============================================================================= * @@ -30603,10 +30618,40 @@ nk_combobox_callback(struct nk_context *ctx, * TOOLTIP * * ===============================================================*/ +NK_LIB struct nk_vec2 +nk_tooltip_get_default_offset(const struct nk_context *ctx) +{ + struct nk_vec2 offset = {0}; + NK_ASSERT(ctx); + if (!ctx) return offset; + if (ctx->style.cursor_active) { + /* nuklear is drawing its own cursor so we can reuse its size (best case!) */ + offset.x = ctx->style.window.padding.x + ctx->style.cursor_active->size.x; + offset.x = ctx->style.window.padding.y + ctx->style.cursor_active->size.y; + } else if (ctx->style.font) { + /* assume that cursor size is similar to font height (flawed but reasonable)*/ + offset.y = ctx->style.window.padding.x + ctx->style.font->height; + offset.x = ctx->style.window.padding.y + ctx->style.font->height; + } + return offset; +} +NK_LIB nk_flags +nk_tooltip_get_default_flags(const struct nk_context *ctx) +{ + NK_UNUSED(ctx); + return NK_TOOLTIP_BELOW|NK_TOOLTIP_ON_RIGHT; +} NK_API nk_bool nk_tooltip_begin(struct nk_context *ctx, float width) { - int x,y,w,h; + return nk_tooltip_begin_offset(ctx, width, + nk_tooltip_get_default_flags(ctx), + nk_tooltip_get_default_offset(ctx)); +} +NK_API nk_bool +nk_tooltip_begin_offset(struct nk_context *ctx, float width, nk_flags tooltip, struct nk_vec2 offset) +{ + int x,y,w,h,mul_x,mul_y; struct nk_window *win; const struct nk_input *in; struct nk_rect bounds; @@ -30625,14 +30670,38 @@ nk_tooltip_begin(struct nk_context *ctx, float width) return 0; w = nk_iceilf(width); - h = nk_iceilf(nk_null_rect.h); - x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; - y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + h = ctx->current->layout->row.min_height; + + /* find axis multipliers based on bitmask state */ + mul_x = 0; + mul_x -= !!(tooltip & NK_TOOLTIP_ON_LEFT ); + mul_x += !!(tooltip & NK_TOOLTIP_ON_RIGHT); + NK_ASSERT(mul_x == -1 || mul_x == 0 || mul_x == 1); + mul_y = 0; + mul_y -= !!(tooltip & NK_TOOLTIP_ABOVE); + mul_y += !!(tooltip & NK_TOOLTIP_BELOW); + NK_ASSERT(mul_y == -1 || mul_y == 0 || mul_y == 1); + + /* turn relative offset into absolute, unless it's already absolute + * notice that offset axis is ignored in cases where mul==0 + * (if you don't like this behavior, make sure to use ABS_OFFSET flag)*/ + if (!(tooltip & NK_TOOLTIP_ABS_OFFSET)) { + offset.x *= mul_x; + offset.y *= mul_y; + } + + /* find origin */ + x = -w/2 + (mul_x * w/2); + x += nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; + x += offset.x; + y = -h/2 + (mul_y * h/2); + y += nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + y += offset.y; bounds.x = (float)x; bounds.y = (float)y; bounds.w = (float)w; - bounds.h = (float)h; + bounds.h = (float)nk_iceilf(nk_null_rect.h); ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); @@ -30641,7 +30710,6 @@ nk_tooltip_begin(struct nk_context *ctx, float width) ctx->current->layout->type = NK_PANEL_TOOLTIP; return ret; } - NK_API void nk_tooltip_end(struct nk_context *ctx) { @@ -30653,7 +30721,7 @@ nk_tooltip_end(struct nk_context *ctx) nk_popup_end(ctx); } NK_API void -nk_tooltip(struct nk_context *ctx, const char *text) +nk_tooltip_offset(struct nk_context *ctx, const char *text, nk_flags tooltip, struct nk_vec2 offset) { const struct nk_style *style; struct nk_vec2 padding; @@ -30681,14 +30749,29 @@ nk_tooltip(struct nk_context *ctx, const char *text) text_height = (style->font->height + 2 * padding.y); /* execute tooltip and fill with text */ - if (nk_tooltip_begin(ctx, (float)text_width)) { + if (nk_tooltip_begin_offset(ctx, (float)text_width, tooltip, offset)) { nk_layout_row_dynamic(ctx, (float)text_height, 1); nk_text(ctx, text, text_len, NK_TEXT_LEFT); nk_tooltip_end(ctx); } } +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + nk_tooltip_offset(ctx, text, + nk_tooltip_get_default_flags(ctx), + nk_tooltip_get_default_offset(ctx)); +} #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void +nk_tooltipf_offset(struct nk_context *ctx, nk_flags tooltip, struct nk_vec2 offset, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv_offset(ctx, tooltip, offset, fmt, args); + va_end(args); +} +NK_API void nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) { va_list args; @@ -30697,6 +30780,13 @@ nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) va_end(args); } NK_API void +nk_tooltipfv_offset(struct nk_context *ctx, nk_flags tooltip, struct nk_vec2 offset, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_tooltip_offset(ctx, buf, tooltip, offset); +} +NK_API void nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256]; @@ -30762,6 +30852,8 @@ nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2026/02/17 (4.13.3) - Fixed default tooltip from being covered by the cursor, +/// added new tooltip variation that allows to set the offset manually /// - 2026/01/31 (4.13.2) - Fix: replace incorrect static asserts for size(nk_bool) /// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro /// - Fix: failure to build from source, due to diff --git a/src/CHANGELOG b/src/CHANGELOG index ec11e0d7a..716e3a5fa 100644 --- a/src/CHANGELOG +++ b/src/CHANGELOG @@ -7,6 +7,8 @@ /// - [y]: Minor version with non-breaking API and library changes /// - [z]: Patch version with no direct changes to the API /// +/// - 2026/02/17 (4.13.3) - Fixed default tooltip from being covered by the cursor, +/// added new tooltip variation that allows to set the offset manually /// - 2026/01/31 (4.13.2) - Fix: replace incorrect static asserts for size(nk_bool) /// - 2026/01/26 (4.13.1) - Fix: nk_do_property now uses NK_STRTOD via macro /// - Fix: failure to build from source, due to diff --git a/src/nuklear.h b/src/nuklear.h index d61f8bca2..f16c679f5 100644 --- a/src/nuklear.h +++ b/src/nuklear.h @@ -3603,12 +3603,27 @@ NK_API void nk_contextual_end(struct nk_context*); * TOOLTIP * * ============================================================================= */ +enum nk_tooltip_flags { + /**!< tells where tooltip should appear relatively to the cursor, can be combined (e.g. BELOW|ON_RIGHT) */ + NK_TOOLTIP_ABOVE = NK_FLAG(0), + NK_TOOLTIP_BELOW = NK_FLAG(1), + NK_TOOLTIP_ON_LEFT = NK_FLAG(2), + NK_TOOLTIP_ON_RIGHT = NK_FLAG(3), + /**!< if set, the offset will be absolute, instead of relative */ + NK_TOOLTIP_ABS_OFFSET = NK_FLAG(4) + /* FIXME: https://github.com/Immediate-Mode-UI/Nuklear/issues/899 */ + /*NK_TOOLTIP_CLAMP_IN_SCREEN = NK_FLAG(5),*/ +}; NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, nk_flags tooltip, struct nk_vec2); #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2); NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2); +NK_API void nk_tooltipf_offset(struct nk_context*, nk_flags tooltip, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_tooltipfv_offset(struct nk_context*, nk_flags tooltip, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(4); #endif NK_API nk_bool nk_tooltip_begin(struct nk_context*, float width); +NK_API nk_bool nk_tooltip_begin_offset(struct nk_context*, float width, nk_flags tooltip, struct nk_vec2); NK_API void nk_tooltip_end(struct nk_context*); /* ============================================================================= * diff --git a/src/nuklear_tooltip.c b/src/nuklear_tooltip.c index f4d78c692..4aab883ae 100644 --- a/src/nuklear_tooltip.c +++ b/src/nuklear_tooltip.c @@ -6,10 +6,40 @@ * TOOLTIP * * ===============================================================*/ +NK_LIB struct nk_vec2 +nk_tooltip_get_default_offset(const struct nk_context *ctx) +{ + struct nk_vec2 offset = {0}; + NK_ASSERT(ctx); + if (!ctx) return offset; + if (ctx->style.cursor_active) { + /* nuklear is drawing its own cursor so we can reuse its size (best case!) */ + offset.x = ctx->style.window.padding.x + ctx->style.cursor_active->size.x; + offset.x = ctx->style.window.padding.y + ctx->style.cursor_active->size.y; + } else if (ctx->style.font) { + /* assume that cursor size is similar to font height (flawed but reasonable)*/ + offset.y = ctx->style.window.padding.x + ctx->style.font->height; + offset.x = ctx->style.window.padding.y + ctx->style.font->height; + } + return offset; +} +NK_LIB nk_flags +nk_tooltip_get_default_flags(const struct nk_context *ctx) +{ + NK_UNUSED(ctx); + return NK_TOOLTIP_BELOW|NK_TOOLTIP_ON_RIGHT; +} NK_API nk_bool nk_tooltip_begin(struct nk_context *ctx, float width) { - int x,y,w,h; + return nk_tooltip_begin_offset(ctx, width, + nk_tooltip_get_default_flags(ctx), + nk_tooltip_get_default_offset(ctx)); +} +NK_API nk_bool +nk_tooltip_begin_offset(struct nk_context *ctx, float width, nk_flags tooltip, struct nk_vec2 offset) +{ + int x,y,w,h,mul_x,mul_y; struct nk_window *win; const struct nk_input *in; struct nk_rect bounds; @@ -28,14 +58,38 @@ nk_tooltip_begin(struct nk_context *ctx, float width) return 0; w = nk_iceilf(width); - h = nk_iceilf(nk_null_rect.h); - x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; - y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + h = ctx->current->layout->row.min_height; + + /* find axis multipliers based on bitmask state */ + mul_x = 0; + mul_x -= !!(tooltip & NK_TOOLTIP_ON_LEFT ); + mul_x += !!(tooltip & NK_TOOLTIP_ON_RIGHT); + NK_ASSERT(mul_x == -1 || mul_x == 0 || mul_x == 1); + mul_y = 0; + mul_y -= !!(tooltip & NK_TOOLTIP_ABOVE); + mul_y += !!(tooltip & NK_TOOLTIP_BELOW); + NK_ASSERT(mul_y == -1 || mul_y == 0 || mul_y == 1); + + /* turn relative offset into absolute, unless it's already absolute + * notice that offset axis is ignored in cases where mul==0 + * (if you don't like this behavior, make sure to use ABS_OFFSET flag)*/ + if (!(tooltip & NK_TOOLTIP_ABS_OFFSET)) { + offset.x *= mul_x; + offset.y *= mul_y; + } + + /* find origin */ + x = -w/2 + (mul_x * w/2); + x += nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x; + x += offset.x; + y = -h/2 + (mul_y * h/2); + y += nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y; + y += offset.y; bounds.x = (float)x; bounds.y = (float)y; bounds.w = (float)w; - bounds.h = (float)h; + bounds.h = (float)nk_iceilf(nk_null_rect.h); ret = nk_popup_begin(ctx, NK_POPUP_DYNAMIC, "__##Tooltip##__", NK_WINDOW_NO_SCROLLBAR|NK_WINDOW_BORDER, bounds); @@ -44,7 +98,6 @@ nk_tooltip_begin(struct nk_context *ctx, float width) ctx->current->layout->type = NK_PANEL_TOOLTIP; return ret; } - NK_API void nk_tooltip_end(struct nk_context *ctx) { @@ -56,7 +109,7 @@ nk_tooltip_end(struct nk_context *ctx) nk_popup_end(ctx); } NK_API void -nk_tooltip(struct nk_context *ctx, const char *text) +nk_tooltip_offset(struct nk_context *ctx, const char *text, nk_flags tooltip, struct nk_vec2 offset) { const struct nk_style *style; struct nk_vec2 padding; @@ -84,14 +137,29 @@ nk_tooltip(struct nk_context *ctx, const char *text) text_height = (style->font->height + 2 * padding.y); /* execute tooltip and fill with text */ - if (nk_tooltip_begin(ctx, (float)text_width)) { + if (nk_tooltip_begin_offset(ctx, (float)text_width, tooltip, offset)) { nk_layout_row_dynamic(ctx, (float)text_height, 1); nk_text(ctx, text, text_len, NK_TEXT_LEFT); nk_tooltip_end(ctx); } } +NK_API void +nk_tooltip(struct nk_context *ctx, const char *text) +{ + nk_tooltip_offset(ctx, text, + nk_tooltip_get_default_flags(ctx), + nk_tooltip_get_default_offset(ctx)); +} #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void +nk_tooltipf_offset(struct nk_context *ctx, nk_flags tooltip, struct nk_vec2 offset, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv_offset(ctx, tooltip, offset, fmt, args); + va_end(args); +} +NK_API void nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) { va_list args; @@ -100,6 +168,13 @@ nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) va_end(args); } NK_API void +nk_tooltipfv_offset(struct nk_context *ctx, nk_flags tooltip, struct nk_vec2 offset, const char *fmt, va_list args) +{ + char buf[256]; + nk_strfmt(buf, NK_LEN(buf), fmt, args); + nk_tooltip_offset(ctx, buf, tooltip, offset); +} +NK_API void nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256];