Skip to content

Commit 859d17b

Browse files
authored
Add Delayed Tooltips (#943)
* Add nk_input_is_mouse_hovering_delay_rect() to implement delayed popups * Add tooltip convenience functions, and a default delay used only with appropriate convenience function * Add 2 input hovering motionless input functions, use delayed version for delayed tooltips * Add tooltip disappears when item is clicked functionality * Add Doxygen comments above definitions
1 parent 50853aa commit 859d17b

6 files changed

Lines changed: 386 additions & 2 deletions

File tree

demo/common/overview.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,10 @@ overview(struct nk_context *ctx)
770770
const struct nk_input *in = &ctx->input;
771771
struct nk_rect bounds;
772772

773+
/* seconds */
774+
static float delay_timer = 0.0;
775+
static nk_bool clicked = nk_false;
776+
773777
/* menu contextual */
774778
nk_layout_row_static(ctx, 30, 160, 1);
775779
bounds = nk_widget_bounds(ctx);
@@ -846,6 +850,24 @@ overview(struct nk_context *ctx)
846850
if (nk_input_is_mouse_hovering_rect(in, bounds)) {
847851
nk_tooltip(ctx, "This is a default tooltip");
848852
}
853+
854+
bounds = nk_widget_bounds(ctx);
855+
nk_label(ctx, "Hover motionless for a default delayed tooltip", NK_TEXT_LEFT);
856+
nk_do_tooltip_delay(ctx, "This is a delayed tooltip", bounds, &delay_timer);
857+
858+
bounds = nk_widget_bounds(ctx);
859+
nk_label(ctx, "Hover motionless longer a custom delayed tooltip", NK_TEXT_LEFT);
860+
if (nk_input_is_mouse_hovering_still_delay_rect(ctx, bounds, &delay_timer, 1.5)) {
861+
nk_tooltip(ctx, "This is a custom delayed tooltip");
862+
}
863+
864+
bounds = nk_widget_bounds(ctx);
865+
if (nk_button_label(ctx, "Delayed tooltip with click sensitivity")) {
866+
clicked = nk_true;
867+
}
868+
nk_do_tooltip_delay_clicked(ctx, "disappears when clicked, timer starts when you move again", bounds, &delay_timer, &clicked);
869+
870+
849871
bounds = nk_widget_bounds(ctx);
850872
nk_label(ctx, "Hover for Gnome-like tooltip", NK_TEXT_LEFT);
851873
if (nk_input_is_mouse_hovering_rect(in, bounds)) {

nuklear.h

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3810,6 +3810,9 @@ NK_API void nk_contextual_end(struct nk_context*);
38103810
* ============================================================================= */
38113811
NK_API void nk_tooltip(struct nk_context*, const char*);
38123812
NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos position, struct nk_vec2 offset);
3813+
NK_API void nk_do_tooltip(struct nk_context*, const char*, struct nk_rect);
3814+
NK_API void nk_do_tooltip_delay(struct nk_context*, const char*, struct nk_rect, float*);
3815+
NK_API void nk_do_tooltip_delay_clicked(struct nk_context*, const char*, struct nk_rect, float* timer, nk_bool*);
38133816
#ifdef NK_INCLUDE_STANDARD_VARARGS
38143817
NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2);
38153818
NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2);
@@ -4935,6 +4938,10 @@ NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, en
49354938
NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect);
49364939
NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect);
49374940
NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect);
4941+
NK_API nk_bool nk_input_is_mouse_hovering_still_rect(const struct nk_input*, struct nk_rect);
4942+
NK_API nk_bool nk_input_is_mouse_hovering_delay_rect(const struct nk_context*, struct nk_rect, float*, float);
4943+
NK_API nk_bool nk_input_is_mouse_hovering_still_delay_rect(const struct nk_context*, struct nk_rect, float*, float);
4944+
NK_API nk_bool nk_input_is_mouse_hovering_still_delay_clicked_rect(const struct nk_context*, struct nk_rect, float*, float, nk_bool*);
49384945
NK_API nk_bool nk_input_is_mouse_moved(const struct nk_input*);
49394946
NK_API nk_bool nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect);
49404947
NK_API nk_bool nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons);
@@ -5570,6 +5577,7 @@ struct nk_style_window {
55705577

55715578
enum nk_tooltip_pos tooltip_origin;
55725579
struct nk_vec2 tooltip_offset;
5580+
float tooltip_delay;
55735581
};
55745582

55755583
struct nk_style {
@@ -18425,6 +18433,139 @@ nk_input_is_mouse_hovering_rect(const struct nk_input *i, struct nk_rect rect)
1842518433
if (!i) return nk_false;
1842618434
return NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h);
1842718435
}
18436+
18437+
/**
18438+
* # nk_input_is_mouse_hovering_still_rect
18439+
* Returns true if the mouse is hovering over rect and hasn't moved since the last
18440+
* frame, false otherwise.
18441+
*/
18442+
NK_API nk_bool
18443+
nk_input_is_mouse_hovering_still_rect(const struct nk_input *i, struct nk_rect rect)
18444+
{
18445+
if (!i) return nk_false;
18446+
return (NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h) &&
18447+
!nk_input_is_mouse_moved(i));
18448+
}
18449+
18450+
/**
18451+
* # nk_input_is_mouse_hovering_delay_rect
18452+
* Returns true if the mouse has been hovering over rect for `delay` seconds or more.
18453+
*
18454+
* Parameter | Description
18455+
* ------------|---------------------------------------------------------------
18456+
* \param[in] ctx | Must point to an either stack or heap allocated `nk_context` struct
18457+
* \param[in] rect | The rect area you're checking against
18458+
* \param[in|out] timer | Must point to a float used to track the total seconds hovered across frames
18459+
* \param[in] delay | The wait time in seconds
18460+
*
18461+
* \returns `true` if the the mouse has hovered long enough, `false otherwise`
18462+
*/
18463+
NK_API nk_bool
18464+
nk_input_is_mouse_hovering_delay_rect(const struct nk_context *ctx, struct nk_rect rect, float* timer, float delay)
18465+
{
18466+
NK_ASSERT(ctx);
18467+
if (!ctx) {
18468+
return nk_false;
18469+
} else {
18470+
const struct nk_input* i = &ctx->input;
18471+
if (NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h)) {
18472+
*timer += ctx->delta_time_seconds;
18473+
return *timer >= delay;
18474+
} else if (NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h)) {
18475+
*timer = 0;
18476+
}
18477+
return nk_false;
18478+
}
18479+
18480+
}
18481+
18482+
/**
18483+
* # nk_input_is_mouse_hovering_still_delay_rect
18484+
* Returns true if the mouse has been hovering motionless over rect for `delay` seconds or more. The timer
18485+
* does not reset once the delay is reached as long as you are still hovering over the rect.
18486+
*
18487+
* Parameter | Description
18488+
* ------------|---------------------------------------------------------------
18489+
* \param[in] ctx | Must point to an either stack or heap allocated `nk_context` struct
18490+
* \param[in] rect | The rect area you're checking against
18491+
* \param[in|out] timer | Must point to a float used to track the total seconds hovered across frames
18492+
* \param[in] delay | The wait time in seconds
18493+
*
18494+
* \returns `true` if the the mouse has hovered without moving long enough, `false otherwise`
18495+
*/
18496+
NK_API nk_bool
18497+
nk_input_is_mouse_hovering_still_delay_rect(const struct nk_context *ctx, struct nk_rect rect, float* timer, float delay)
18498+
{
18499+
NK_ASSERT(ctx);
18500+
if (!ctx) {
18501+
return nk_false;
18502+
} else {
18503+
const struct nk_input* i = &ctx->input;
18504+
if (NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h)) {
18505+
/* once it triggers, moving within the bounds should not make it disappear */
18506+
if (*timer >= delay) {
18507+
return nk_true;
18508+
}
18509+
if (!nk_input_is_mouse_moved(i)) {
18510+
*timer += ctx->delta_time_seconds;
18511+
return *timer >= delay;
18512+
}
18513+
*timer = 0;
18514+
} else if (NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h)) {
18515+
*timer = 0;
18516+
}
18517+
return nk_false;
18518+
}
18519+
}
18520+
18521+
/**
18522+
* # nk_input_is_mouse_hovering_still_delay_clicked_rect
18523+
* Works the same as `nk_input_is_mouse_hovering_still_delay_rect` unless clicked is set. If clicked is true,
18524+
* then it returns false regardless of the timer value as long as the mouse is motionless. It goes back to working
18525+
* as normal once the mouse has moved (clicked is set to false, timer to 0).
18526+
*
18527+
* Parameter | Description
18528+
* ------------|---------------------------------------------------------------
18529+
* \param[in] ctx | Must point to an either stack or heap allocated `nk_context` struct
18530+
* \param[in] rect | The rect area you're checking against
18531+
* \param[in|out] timer | Must point to a float used to track the total seconds hovered across frames
18532+
* \param[in] delay | The wait time in seconds
18533+
* \param[in|out] clicked | Must point to an nk_bool used to indicate whether the item in question has been clicked (reset to false internally on mouse motion)
18534+
*
18535+
* \returns `true` if the the mouse has hovered without moving long enough, `false otherwise`
18536+
*/
18537+
NK_API nk_bool
18538+
nk_input_is_mouse_hovering_still_delay_clicked_rect(const struct nk_context *ctx, struct nk_rect rect, float* timer, float delay, nk_bool* clicked)
18539+
{
18540+
NK_ASSERT(ctx);
18541+
if (!ctx) {
18542+
return nk_false;
18543+
} else {
18544+
const struct nk_input* i = &ctx->input;
18545+
if (*clicked) {
18546+
/* could also be based on maintaining hover rather than motionless once clicked */
18547+
if (!nk_input_is_mouse_moved(i)) {
18548+
return nk_false;
18549+
}
18550+
*clicked = nk_false;
18551+
*timer = 0;
18552+
}
18553+
if (NK_INBOX(i->mouse.pos.x, i->mouse.pos.y, rect.x, rect.y, rect.w, rect.h)) {
18554+
/* once it triggers, moving within the bounds should not make it disappear */
18555+
if (*timer >= delay) {
18556+
return nk_true;
18557+
}
18558+
if (!nk_input_is_mouse_moved(i)) {
18559+
*timer += ctx->delta_time_seconds;
18560+
return *timer >= delay;
18561+
}
18562+
*timer = 0;
18563+
} else if (NK_INBOX(i->mouse.prev.x, i->mouse.prev.y, rect.x, rect.y, rect.w, rect.h)) {
18564+
*timer = 0;
18565+
}
18566+
return nk_false;
18567+
}
18568+
}
1842818569
NK_API nk_bool
1842918570
nk_input_is_mouse_prev_hovering_rect(const struct nk_input *i, struct nk_rect rect)
1843018571
{
@@ -19221,13 +19362,14 @@ nk_style_from_table(struct nk_context *ctx, const struct nk_color *table)
1922119362
win->tooltip_padding = nk_vec2(4,4);
1922219363

1922319364
/* default tooltip just down and to the right of the cursor
19224-
* so it doesn't cover the text
19365+
* so it doesn't cover the text and a default delay of 0.5 seconds
1922519366
*
1922619367
* TODO might be worth consolidating tooltip styling
1922719368
* into its own style structure, though it is a
1922819369
* type of window...*/
1922919370
win->tooltip_origin = NK_TOP_LEFT;
1923019371
win->tooltip_offset = nk_vec2(12, 12);
19372+
win->tooltip_delay = 0.5f;
1923119373
}
1923219374
NK_API void
1923319375
nk_style_set_font(struct nk_context *ctx, const struct nk_user_font *font)
@@ -30740,6 +30882,45 @@ nk_tooltip_end(struct nk_context *ctx)
3074030882
nk_popup_end(ctx);
3074130883
}
3074230884

30885+
/**
30886+
* Display a default tooltip if the mouse is hovering over the rect `bounds`
30887+
*/
30888+
NK_API void
30889+
nk_do_tooltip(struct nk_context* ctx, const char* text, struct nk_rect bounds)
30890+
{
30891+
NK_ASSERT(ctx);
30892+
if (nk_input_is_mouse_hovering_rect(&ctx->input, bounds)) {
30893+
nk_tooltip_offset(ctx, text, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset);
30894+
}
30895+
}
30896+
30897+
/**
30898+
* Display a default tooltip if the mouse hovers motionless for the default delay (ctx->style.window.tooltip_delay)
30899+
* `timer` is used to track the time across frames.
30900+
*/
30901+
NK_API void
30902+
nk_do_tooltip_delay(struct nk_context* ctx, const char* text, struct nk_rect bounds, float* timer)
30903+
{
30904+
NK_ASSERT(ctx);
30905+
if (nk_input_is_mouse_hovering_still_delay_rect(ctx, bounds, timer, ctx->style.window.tooltip_delay)) {
30906+
nk_tooltip_offset(ctx, text, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset);
30907+
}
30908+
}
30909+
30910+
/**
30911+
* Display a default tooltip if the mouse hovers motionless for the default delay (ctx->style.window.tooltip_delay) unless
30912+
* `clicked` is true. `clicked` will be reset to false (and `timer` to 0) when the mouse moves.
30913+
* `timer` is used to track the time across frames.
30914+
*/
30915+
NK_API void
30916+
nk_do_tooltip_delay_clicked(struct nk_context* ctx, const char* text, struct nk_rect bounds, float* timer, nk_bool* clicked)
30917+
{
30918+
NK_ASSERT(ctx);
30919+
if (nk_input_is_mouse_hovering_still_delay_clicked_rect(ctx, bounds, timer, ctx->style.window.tooltip_delay, clicked)) {
30920+
nk_tooltip_offset(ctx, text, ctx->style.window.tooltip_origin, ctx->style.window.tooltip_offset);
30921+
}
30922+
}
30923+
3074330924
NK_API void
3074430925
nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos position, struct nk_vec2 offset)
3074530926
{

src/nuklear.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3620,6 +3620,9 @@ NK_API void nk_contextual_end(struct nk_context*);
36203620
* ============================================================================= */
36213621
NK_API void nk_tooltip(struct nk_context*, const char*);
36223622
NK_API void nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos position, struct nk_vec2 offset);
3623+
NK_API void nk_do_tooltip(struct nk_context*, const char*, struct nk_rect);
3624+
NK_API void nk_do_tooltip_delay(struct nk_context*, const char*, struct nk_rect, float*);
3625+
NK_API void nk_do_tooltip_delay_clicked(struct nk_context*, const char*, struct nk_rect, float* timer, nk_bool*);
36233626
#ifdef NK_INCLUDE_STANDARD_VARARGS
36243627
NK_API void nk_tooltipf(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, ...) NK_PRINTF_VARARG_FUNC(2);
36253628
NK_API void nk_tooltipfv(struct nk_context*, NK_PRINTF_FORMAT_STRING const char*, va_list) NK_PRINTF_VALIST_FUNC(2);
@@ -4745,6 +4748,10 @@ NK_API nk_bool nk_input_is_mouse_click_down_in_rect(const struct nk_input *i, en
47454748
NK_API nk_bool nk_input_any_mouse_click_in_rect(const struct nk_input*, struct nk_rect);
47464749
NK_API nk_bool nk_input_is_mouse_prev_hovering_rect(const struct nk_input*, struct nk_rect);
47474750
NK_API nk_bool nk_input_is_mouse_hovering_rect(const struct nk_input*, struct nk_rect);
4751+
NK_API nk_bool nk_input_is_mouse_hovering_still_rect(const struct nk_input*, struct nk_rect);
4752+
NK_API nk_bool nk_input_is_mouse_hovering_delay_rect(const struct nk_context*, struct nk_rect, float*, float);
4753+
NK_API nk_bool nk_input_is_mouse_hovering_still_delay_rect(const struct nk_context*, struct nk_rect, float*, float);
4754+
NK_API nk_bool nk_input_is_mouse_hovering_still_delay_clicked_rect(const struct nk_context*, struct nk_rect, float*, float, nk_bool*);
47484755
NK_API nk_bool nk_input_is_mouse_moved(const struct nk_input*);
47494756
NK_API nk_bool nk_input_mouse_clicked(const struct nk_input*, enum nk_buttons, struct nk_rect);
47504757
NK_API nk_bool nk_input_is_mouse_down(const struct nk_input*, enum nk_buttons);
@@ -5380,6 +5387,7 @@ struct nk_style_window {
53805387

53815388
enum nk_tooltip_pos tooltip_origin;
53825389
struct nk_vec2 tooltip_offset;
5390+
float tooltip_delay;
53835391
};
53845392

53855393
struct nk_style {

0 commit comments

Comments
 (0)