diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index 2b63b159..ef0642cc 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -2886,6 +2886,19 @@ df_window_update_and_render(Arena *arena, OS_EventList *events, DF_Window *ws, D Rng2F32 bottom_bar_rect = r2f32p(window_rect.x0, window_rect_dim.y - ui_top_pref_height().value, window_rect.x0+window_rect_dim.x, window_rect.y0+window_rect_dim.y); Rng2F32 content_rect = r2f32p(window_rect.x0, top_bar_rect.y1, window_rect.x0+window_rect_dim.x, bottom_bar_rect.y0); + //////////////////////////// + //- rjf: truncated string hover + // + if(ui_string_hover_active()) UI_Tooltip + { + Temp scratch = scratch_begin(&arena, 1); + String8 string = ui_string_hover_string(scratch.arena); + D_FancyRunList runs = ui_string_hover_runs(scratch.arena); + UI_Box *box = ui_build_box_from_key(UI_BoxFlag_DrawText, ui_key_zero()); + ui_box_equip_display_string_fancy_runs(box, string, &runs); + scratch_end(scratch); + } + //////////////////////////// //- rjf: drag/drop visualization tooltips // diff --git a/src/draw/draw.c b/src/draw/draw.c index 4e28d30c..7b610fc7 100644 --- a/src/draw/draw.c +++ b/src/draw/draw.c @@ -90,6 +90,22 @@ d_fancy_run_list_from_fancy_string_list(Arena *arena, D_FancyStringList *strs) return run_list; } +internal D_FancyRunList +d_fancy_run_list_copy(Arena *arena, D_FancyRunList *src) +{ + D_FancyRunList dst = {0}; + for(D_FancyRunNode *src_n = src->first; src_n != 0; src_n = src_n->next) + { + D_FancyRunNode *dst_n = push_array(arena, D_FancyRunNode, 1); + SLLQueuePush(dst.first, dst.last, dst_n); + MemoryCopyStruct(&dst_n->v, &src_n->v); + dst_n->v.run.pieces = f_piece_array_copy(arena, &src_n->v.run.pieces); + dst.node_count += 1; + } + dst.dim = src->dim; + return dst; +} + //////////////////////////////// //~ rjf: Top-Level API // diff --git a/src/draw/draw.h b/src/draw/draw.h index 06178bab..72719163 100644 --- a/src/draw/draw.h +++ b/src/draw/draw.h @@ -111,6 +111,7 @@ internal U64 d_hash_from_string(String8 string); internal void d_fancy_string_list_push(Arena *arena, D_FancyStringList *list, D_FancyString *str); internal String8 d_string_from_fancy_string_list(Arena *arena, D_FancyStringList *list); internal D_FancyRunList d_fancy_run_list_from_fancy_string_list(Arena *arena, D_FancyStringList *strs); +internal D_FancyRunList d_fancy_run_list_copy(Arena *arena, D_FancyRunList *src); //////////////////////////////// //~ rjf: Top-Level API diff --git a/src/font_cache/font_cache.c b/src/font_cache/font_cache.c index 80cfbecd..b3b8eaa2 100644 --- a/src/font_cache/font_cache.c +++ b/src/font_cache/font_cache.c @@ -502,6 +502,16 @@ f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list) return array; } +internal F_PieceArray +f_piece_array_copy(Arena *arena, F_PieceArray *src) +{ + F_PieceArray dst = {0}; + dst.count = src->count; + dst.v = push_array_no_zero(arena, F_Piece, dst.count); + MemoryCopy(dst.v, src->v, sizeof(F_Piece)*dst.count); + return dst; +} + //////////////////////////////// //~ rjf: Rasterization Cache diff --git a/src/font_cache/font_cache.h b/src/font_cache/font_cache.h index bfc9fa4b..f0de383f 100644 --- a/src/font_cache/font_cache.h +++ b/src/font_cache/font_cache.h @@ -241,6 +241,7 @@ internal void f_atlas_region_release(F_Atlas *atlas, Rng2S16 region); internal F_Piece *f_piece_chunk_list_push_new(Arena *arena, F_PieceChunkList *list, U64 cap); internal void f_piece_chunk_list_push(Arena *arena, F_PieceChunkList *list, U64 cap, F_Piece *piece); internal F_PieceArray f_piece_array_from_chunk_list(Arena *arena, F_PieceChunkList *list); +internal F_PieceArray f_piece_array_copy(Arena *arena, F_PieceArray *src); //////////////////////////////// //~ rjf: Rasterization Cache diff --git a/src/raddbg/raddbg.h b/src/raddbg/raddbg.h index 10c213a9..c3eba688 100644 --- a/src/raddbg/raddbg.h +++ b/src/raddbg/raddbg.h @@ -7,13 +7,9 @@ // [ ] source view -> floating margin/line-nums // [ ] theme colors -> more explicit about e.g. opaque backgrounds vs. floating // & scrollbars etc. -// [ ] need bilinear interpolation on color picker - fine for most UI to not do -// do it but it needs to be correct for the color picker, so maybe that -// should be a heavier path // [ ] drag/drop tab cleanup // [ ] target/breakpoint/watch-pin reordering // [ ] watch window reordering -// [x] query views, cleanup & floating - maybe merge "applies to view" vs. not // [ ] standard way to filter // [ ] visualize remapped files (via path map) // [ ] hovering truncated string for a short time -> tooltip with full wrapped diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index a4a0d2bc..df90bd27 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -428,6 +428,7 @@ ui_state_alloc(void) ui->build_arenas[0] = arena_alloc(); ui->build_arenas[1] = arena_alloc(); ui->drag_state_arena = arena_alloc(); + ui->string_hover_arena = arena_alloc(); ui->box_table_size = 4096; ui->box_table = push_array(arena, UI_BoxHashSlot, ui->box_table_size); UI_InitStackNils(ui); @@ -437,6 +438,7 @@ ui_state_alloc(void) internal void ui_state_release(UI_State *state) { + arena_release(state->string_hover_arena); arena_release(state->drag_state_arena); for(int i = 0; i < ArrayCount(state->build_arenas); i += 1) { @@ -551,6 +553,35 @@ ui_get_drag_data(U64 min_required_size) return ui_state->drag_state_data; } +//- rjf: hovered string info + +internal B32 +ui_string_hover_active(void) +{ + return (ui_state->build_index > 0 && ui_state->string_hover_build_index >= ui_state->build_index-1 && + os_now_microseconds() >= ui_state->string_hover_begin_us + 500000); +} + +internal U64 +ui_string_hover_begin_time_us(void) +{ + return ui_state->string_hover_begin_us; +} + +internal String8 +ui_string_hover_string(Arena *arena) +{ + String8 result = push_str8_copy(arena, ui_state->string_hover_string); + return result; +} + +internal D_FancyRunList +ui_string_hover_runs(Arena *arena) +{ + D_FancyRunList result = d_fancy_run_list_copy(arena, &ui_state->string_hover_fancy_runs); + return result; +} + //- rjf: interaction keys internal UI_Key @@ -1270,6 +1301,55 @@ ui_end_build(void) scratch_end(scratch); } + //- rjf: hovering possibly-truncated drawn text -> store text + { + B32 found = 0; + for(UI_Box *box = ui_state->root, *next = 0; !ui_box_is_nil(box); box = next) + { + UI_BoxRec rec = ui_box_rec_df_pre(box, ui_state->root); + next = rec.next; + S32 pop_idx = 0; + for(UI_Box *b = box; !ui_box_is_nil(b) && pop_idx <= rec.pop_count; b = b->parent, pop_idx += 1) + { + if(b->flags & UI_BoxFlag_DrawText && !(b->flags & UI_BoxFlag_DisableTextTrunc)) + { + String8 box_display_string = ui_box_display_string(b); + Vec2F32 text_pos = ui_box_text_position(b); + Vec2F32 drawn_text_dim = b->display_string_runs.dim; + if(drawn_text_dim.x > dim_2f32(b->rect).x && + contains_2f32(r2f32p(text_pos.x, + b->rect.y0, + Min(text_pos.x+drawn_text_dim.x, b->rect.x1), + b->rect.y1), + ui_state->mouse)) + { + if(!str8_match(box_display_string, ui_state->string_hover_string, 0)) + { + arena_clear(ui_state->string_hover_arena); + ui_state->string_hover_string = push_str8_copy(ui_state->string_hover_arena, box_display_string); + ui_state->string_hover_fancy_runs = d_fancy_run_list_copy(ui_state->string_hover_arena, &b->display_string_runs); + ui_state->string_hover_begin_us = os_now_microseconds(); + } + ui_state->string_hover_build_index = ui_state->build_index; + found = 1; + goto break_all_hover_string; + } + } + } + } + break_all_hover_string:; + if(!found) + { + arena_clear(ui_state->string_hover_arena); + ui_state->string_hover_build_index = 0; + MemoryZeroStruct(&ui_state->string_hover_string); + } + if(found && !ui_string_hover_active()) + { + ui_state->is_animating = 1; + } + } + ui_state->build_index += 1; arena_clear(ui_build_arena()); ProfEnd(); @@ -2103,6 +2183,14 @@ ui_box_equip_display_fancy_strings(UI_Box *box, D_FancyStringList *strings) box->display_string_runs = d_fancy_run_list_from_fancy_string_list(ui_build_arena(), strings); } +internal inline void +ui_box_equip_display_string_fancy_runs(UI_Box *box, String8 string, D_FancyRunList *runs) +{ + box->flags |= UI_BoxFlag_HasDisplayString; + box->string = push_str8_copy(ui_build_arena(), string); + box->display_string_runs = d_fancy_run_list_copy(ui_build_arena(), runs); +} + internal void ui_box_equip_draw_bucket(UI_Box *box, D_Bucket *bucket) { diff --git a/src/ui/ui_core.h b/src/ui/ui_core.h index 9012e2e2..915339ce 100644 --- a/src/ui/ui_core.h +++ b/src/ui/ui_core.h @@ -416,6 +416,11 @@ struct UI_State Vec2F32 drag_start_mouse; Arena *drag_state_arena; String8 drag_state_data; + Arena *string_hover_arena; + String8 string_hover_string; + D_FancyRunList string_hover_fancy_runs; + U64 string_hover_begin_us; + U64 string_hover_build_index; //- rjf: tooltip state F32 tooltip_open_t; @@ -533,6 +538,10 @@ internal String8 ui_get_drag_data(U64 min_required_size); #define ui_store_drag_struct(ptr) ui_store_drag_data(str8_struct(ptr)) #define ui_get_drag_struct(type) ((type *)ui_get_drag_data(sizeof(type)).str) +//- rjf: hovered string info +internal B32 ui_string_hover_active(void); +internal D_FancyRunList ui_string_hover_runs(Arena *arena); + //- rjf: interaction keys internal UI_Key ui_hot_key(void); internal UI_Key ui_active_key(Side side); @@ -591,6 +600,7 @@ internal UI_Box * ui_build_box_from_stringf(UI_BoxFlags flags, char *fm //- rjf: box node equipment internal inline void ui_box_equip_display_string(UI_Box *box, String8 string); internal inline void ui_box_equip_display_fancy_strings(UI_Box *box, D_FancyStringList *strings); +internal inline void ui_box_equip_display_string_fancy_runs(UI_Box *box, String8 string, D_FancyRunList *runs); internal inline void ui_box_equip_draw_bucket(UI_Box *box, D_Bucket *bucket); internal inline void ui_box_equip_custom_draw(UI_Box *box, UI_BoxCustomDrawFunctionType *custom_draw, void *user_data);