diff --git a/demo/common/overview.c b/demo/common/overview.c index 87d5f686f..bd73116a3 100644 --- a/demo/common/overview.c +++ b/demo/common/overview.c @@ -839,12 +839,87 @@ 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 a 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 bottom centers plus a -y offset", NK_BOTTOM_CENTER, offset); + } + bounds = nk_widget_bounds(ctx); + nk_label(ctx, "Hover for a bottom left tooltip", NK_TEXT_LEFT); + if (nk_input_is_mouse_hovering_rect(in, bounds)) { + struct nk_vec2 offset = { 0, 0 }; + nk_tooltip_offset(ctx, "Bottom left positioning", NK_BOTTOM_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 * NK_COS(accum_time_seconds * speed); + offset.y = radius * NK_SIN(accum_time_seconds * speed); + nk_tooltip_offset(ctx, "WOW!", NK_MIDDLE_CENTER, 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 struct nk_vec2 offset = {0}; + static const char* tooltip_positions[] = + { + "TOP_LEFT", + "TOP_CENTER", + "TOP_RIGHT", + + "MIDDLE_LEFT", + "MIDDLE_CENTER", + "MIDDLE_RIGHT", + + "BOTTOM_LEFT", + "BOTTOM_CENTER", + "BOTTOM_RIGHT" + }; + static int cur_pos = NK_TOP_LEFT; + + 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, cur_pos, 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); + text_buf[text_len] = '\0'; /* TODO: why nk_edit_string is NOT setting this on its own? */ + nk_layout_row_dynamic(ctx, 30, 1); + cur_pos = nk_combo(ctx, tooltip_positions, NK_LEN(tooltip_positions), cur_pos, 25, nk_vec2(200, 200)); + + + 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/demo/common/style_configurator.c b/demo/common/style_configurator.c index ba87740e2..ad7de96f0 100644 --- a/demo/common/style_configurator.c +++ b/demo/common/style_configurator.c @@ -632,6 +632,22 @@ style_window_header(struct nk_context* ctx, struct nk_style_window_header* out_s static void style_window(struct nk_context* ctx, struct nk_style_window* out_style) { + static const char* tooltip_positions[] = + { + "TOP_LEFT", + "TOP_CENTER", + "TOP_RIGHT", + + "MIDDLE_LEFT", + "MIDDLE_CENTER", + "MIDDLE_RIGHT", + + "BOTTOM_LEFT", + "BOTTOM_CENTER", + "BOTTOM_RIGHT" + }; + /*static int cur_tooltip_pos = NK_TOP_LEFT;*/ + struct nk_style_window win = *out_style; nk_layout_row_dynamic(ctx, 30, 2); @@ -661,6 +677,10 @@ style_window(struct nk_context* ctx, struct nk_style_window* out_style) style_vec2(ctx, "Menu Padding:", &win.menu_padding); style_vec2(ctx, "Tooltip Padding:", &win.tooltip_padding); + nk_label(ctx, "Tooltip Origin", NK_TEXT_LEFT); + win.tooltip_origin = nk_combo(ctx, tooltip_positions, NK_LEN(tooltip_positions), win.tooltip_origin, 25, nk_vec2(200, 200)); + style_vec2(ctx, "Tooltip offset:", &win.tooltip_offset); + nk_property_float(ctx, "#Rounding:", -100.0f, &win.rounding, 100.0f, 1,0.5f); nk_property_float(ctx, "#Combo Border:", -100.0f, &win.combo_border, 100.0f, 1,0.5f); nk_property_float(ctx, "#Contextual Border:", -100.0f, &win.contextual_border, 100.0f, 1,0.5f); diff --git a/nuklear.h b/nuklear.h index aee190eb8..73afb7258 100644 --- a/nuklear.h +++ b/nuklear.h @@ -516,6 +516,20 @@ enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; +enum nk_tooltip_pos { + NK_TOP_LEFT, + NK_TOP_CENTER, + NK_TOP_RIGHT, + + NK_MIDDLE_LEFT, + NK_MIDDLE_CENTER, + NK_MIDDLE_RIGHT, + + NK_BOTTOM_LEFT, + NK_BOTTOM_CENTER, + NK_BOTTOM_RIGHT +}; + typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); typedef void (*nk_plugin_free)(nk_handle, void *old); typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); @@ -3827,11 +3841,15 @@ NK_API void nk_contextual_end(struct nk_context*); * * ============================================================================= */ NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos position, struct nk_vec2 offset); #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*, enum nk_tooltip_pos, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_tooltipfv_offset(struct nk_context*, enum nk_tooltip_pos, 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, enum nk_tooltip_pos, struct nk_vec2); NK_API void nk_tooltip_end(struct nk_context*); /* ============================================================================= * @@ -5581,6 +5599,9 @@ struct nk_style_window { struct nk_vec2 contextual_padding; struct nk_vec2 menu_padding; struct nk_vec2 tooltip_padding; + + enum nk_tooltip_pos tooltip_origin; + struct nk_vec2 tooltip_offset; }; struct nk_style { @@ -19228,6 +19249,15 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) win->contextual_padding = nk_vec2(4,4); win->menu_padding = nk_vec2(4,4); win->tooltip_padding = nk_vec2(4,4); + + /* default tooltip just down and to the right of the cursor + * so it doesn't cover the text + * + * TODO might be worth consolidating tooltip styling + * into its own style structure, though it is a + * type of window...*/ + win->tooltip_origin = NK_TOP_LEFT; + win->tooltip_offset = nk_vec2(12, 12); } NK_API void nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) @@ -30631,6 +30661,13 @@ nk_combobox_callback(struct nk_context *ctx, * ===============================================================*/ NK_API nk_bool nk_tooltip_begin(struct nk_context *ctx, float width) +{ + NK_ASSERT(ctx); + return nk_tooltip_begin_offset(ctx, width, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset); +} + +NK_API nk_bool +nk_tooltip_begin_offset(struct nk_context *ctx, float width, enum nk_tooltip_pos position, struct nk_vec2 offset) { int x,y,w,h; struct nk_window *win; @@ -30651,14 +30688,55 @@ 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 = NK_MAX(win->layout->row.min_height, ctx->style.font->height+2*ctx->style.window.padding.y); + + /* Default origin is top left, plus user offset */ + x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x + offset.x; + y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y + offset.y; + + /* Adjust origin based on enum */ + switch (position) { + case NK_TOP_LEFT: + /* no change */ + break; + case NK_TOP_CENTER: + x -= w/2; + break; + case NK_TOP_RIGHT: + x -= w; + break; + + case NK_MIDDLE_LEFT: + y -= h/2; + break; + case NK_MIDDLE_CENTER: + x -= w/2; + y -= h/2; + break; + case NK_MIDDLE_RIGHT: + x -= w; + y -= h/2; + break; + + case NK_BOTTOM_LEFT: + y -= h; + break; + case NK_BOTTOM_CENTER: + x -= w/2; + y -= h; + break; + case NK_BOTTOM_RIGHT: + x -= w; + y -= h; + break; + default: + NK_ASSERT(0 && "Invalid tooltip position"); + } 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); @@ -30678,8 +30756,9 @@ nk_tooltip_end(struct nk_context *ctx) nk_popup_close(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, enum nk_tooltip_pos position, struct nk_vec2 offset) { const struct nk_style *style; struct nk_vec2 padding; @@ -30707,14 +30786,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, position, 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_ASSERT(ctx); + nk_tooltip_offset(ctx, text, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset); +} #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void +nk_tooltipf_offset(struct nk_context *ctx, enum nk_tooltip_pos position, struct nk_vec2 offset, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv_offset(ctx, position, offset, fmt, args); + va_end(args); +} +NK_API void nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) { va_list args; @@ -30723,6 +30817,13 @@ nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) va_end(args); } NK_API void +nk_tooltipfv_offset(struct nk_context *ctx, enum nk_tooltip_pos position, 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, position, offset); +} +NK_API void nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256]; diff --git a/src/nuklear.h b/src/nuklear.h index 6fa62204c..ca497872e 100644 --- a/src/nuklear.h +++ b/src/nuklear.h @@ -293,6 +293,20 @@ enum nk_popup_type {NK_POPUP_STATIC, NK_POPUP_DYNAMIC}; enum nk_layout_format {NK_DYNAMIC, NK_STATIC}; enum nk_tree_type {NK_TREE_NODE, NK_TREE_TAB}; +enum nk_tooltip_pos { + NK_TOP_LEFT, + NK_TOP_CENTER, + NK_TOP_RIGHT, + + NK_MIDDLE_LEFT, + NK_MIDDLE_CENTER, + NK_MIDDLE_RIGHT, + + NK_BOTTOM_LEFT, + NK_BOTTOM_CENTER, + NK_BOTTOM_RIGHT +}; + typedef void*(*nk_plugin_alloc)(nk_handle, void *old, nk_size); typedef void (*nk_plugin_free)(nk_handle, void *old); typedef nk_bool(*nk_plugin_filter)(const struct nk_text_edit*, nk_rune unicode); @@ -3604,11 +3618,15 @@ NK_API void nk_contextual_end(struct nk_context*); * * ============================================================================= */ NK_API void nk_tooltip(struct nk_context*, const char*); +NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos position, struct nk_vec2 offset); #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*, enum nk_tooltip_pos, struct nk_vec2, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(4); +NK_API void nk_tooltipfv_offset(struct nk_context*, enum nk_tooltip_pos, 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, enum nk_tooltip_pos, struct nk_vec2); NK_API void nk_tooltip_end(struct nk_context*); /* ============================================================================= * @@ -5358,6 +5376,9 @@ struct nk_style_window { struct nk_vec2 contextual_padding; struct nk_vec2 menu_padding; struct nk_vec2 tooltip_padding; + + enum nk_tooltip_pos tooltip_origin; + struct nk_vec2 tooltip_offset; }; struct nk_style { diff --git a/src/nuklear_style.c b/src/nuklear_style.c index 377836a4a..b241e7757 100644 --- a/src/nuklear_style.c +++ b/src/nuklear_style.c @@ -720,6 +720,15 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table) win->contextual_padding = nk_vec2(4,4); win->menu_padding = nk_vec2(4,4); win->tooltip_padding = nk_vec2(4,4); + + /* default tooltip just down and to the right of the cursor + * so it doesn't cover the text + * + * TODO might be worth consolidating tooltip styling + * into its own style structure, though it is a + * type of window...*/ + win->tooltip_origin = NK_TOP_LEFT; + win->tooltip_offset = nk_vec2(12, 12); } NK_API void nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font) diff --git a/src/nuklear_tooltip.c b/src/nuklear_tooltip.c index f4d78c692..3729a0f52 100644 --- a/src/nuklear_tooltip.c +++ b/src/nuklear_tooltip.c @@ -8,6 +8,13 @@ * ===============================================================*/ NK_API nk_bool nk_tooltip_begin(struct nk_context *ctx, float width) +{ + NK_ASSERT(ctx); + return nk_tooltip_begin_offset(ctx, width, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset); +} + +NK_API nk_bool +nk_tooltip_begin_offset(struct nk_context *ctx, float width, enum nk_tooltip_pos position, struct nk_vec2 offset) { int x,y,w,h; struct nk_window *win; @@ -28,14 +35,55 @@ 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 = NK_MAX(win->layout->row.min_height, ctx->style.font->height+2*ctx->style.window.padding.y); + + /* Default origin is top left, plus user offset */ + x = nk_ifloorf(in->mouse.pos.x + 1) - (int)win->layout->clip.x + offset.x; + y = nk_ifloorf(in->mouse.pos.y + 1) - (int)win->layout->clip.y + offset.y; + + /* Adjust origin based on enum */ + switch (position) { + case NK_TOP_LEFT: + /* no change */ + break; + case NK_TOP_CENTER: + x -= w/2; + break; + case NK_TOP_RIGHT: + x -= w; + break; + + case NK_MIDDLE_LEFT: + y -= h/2; + break; + case NK_MIDDLE_CENTER: + x -= w/2; + y -= h/2; + break; + case NK_MIDDLE_RIGHT: + x -= w; + y -= h/2; + break; + + case NK_BOTTOM_LEFT: + y -= h; + break; + case NK_BOTTOM_CENTER: + x -= w/2; + y -= h; + break; + case NK_BOTTOM_RIGHT: + x -= w; + y -= h; + break; + default: + NK_ASSERT(0 && "Invalid tooltip position"); + } 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); @@ -55,8 +103,9 @@ nk_tooltip_end(struct nk_context *ctx) nk_popup_close(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, enum nk_tooltip_pos position, struct nk_vec2 offset) { const struct nk_style *style; struct nk_vec2 padding; @@ -84,14 +133,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, position, 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_ASSERT(ctx); + nk_tooltip_offset(ctx, text, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset); +} #ifdef NK_INCLUDE_STANDARD_VARARGS NK_API void +nk_tooltipf_offset(struct nk_context *ctx, enum nk_tooltip_pos position, struct nk_vec2 offset, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + nk_tooltipfv_offset(ctx, position, offset, fmt, args); + va_end(args); +} +NK_API void nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) { va_list args; @@ -100,6 +164,13 @@ nk_tooltipf(struct nk_context *ctx, const char *fmt, ...) va_end(args); } NK_API void +nk_tooltipfv_offset(struct nk_context *ctx, enum nk_tooltip_pos position, 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, position, offset); +} +NK_API void nk_tooltipfv(struct nk_context *ctx, const char *fmt, va_list args) { char buf[256];