From 903995f85e8a062c0203f21bd6e6547ac582b681 Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Thu, 10 Oct 2024 11:13:11 -0700 Subject: [PATCH] file path map building & ui --- .../dwrite/font_provider_dwrite.c | 5 +- src/raddbg/raddbg_core.c | 71 +++++++++++++- src/raddbg/raddbg_views.c | 93 +++++++++++-------- src/raddbg/raddbg_views.h | 3 +- src/ui/ui_core.c | 2 +- 5 files changed, 129 insertions(+), 45 deletions(-) diff --git a/src/font_provider/dwrite/font_provider_dwrite.c b/src/font_provider/dwrite/font_provider_dwrite.c index 675f01ac..cb6f477b 100644 --- a/src/font_provider/dwrite/font_provider_dwrite.c +++ b/src/font_provider/dwrite/font_provider_dwrite.c @@ -432,12 +432,11 @@ fp_raster(Arena *arena, FP_Handle font_handle, F32 size, FP_RasterFlags flags, S F32 right_side_bearing = 0; if(font.face != 0) { - atlas_dim.y = (S16)ceil_f32((96.f/72.f) * size * (font_metrics.ascent + font_metrics.descent) / design_units_per_em); + atlas_dim.y = (S16)ceil_f32((96.f/72.f) * size * (font_metrics.ascent + font_metrics.descent) / design_units_per_em) + 1; for(U64 idx = 0; idx < glyphs_count; idx += 1) { DWRITE_GLYPH_METRICS *glyph_metrics = glyphs_metrics + idx; F32 glyph_advance_width = (96.f/72.f) * size * glyph_metrics->advanceWidth / design_units_per_em; - F32 glyph_advance_height = (96.f/72.f) * size * glyph_metrics->advanceHeight / design_units_per_em; advance += glyph_advance_width; atlas_dim.x = Max(atlas_dim.x, (S16)(advance+1)); if(idx == 0) @@ -480,7 +479,7 @@ fp_raster(Arena *arena, FP_Handle font_handle, F32 size, FP_RasterFlags flags, S Vec2F32 draw_p = {0, (F32)atlas_dim.y}; if(font.face != 0) { - F32 descent = (96.f/72.f)*size * font_metrics.descent / design_units_per_em; + F32 descent = floor_f32((96.f/72.f)*size * font_metrics.descent / design_units_per_em); draw_p.y -= descent; } DWRITE_GLYPH_RUN glyph_run = {0}; diff --git a/src/raddbg/raddbg_core.c b/src/raddbg/raddbg_core.c index 77b0372d..d9eec961 100644 --- a/src/raddbg/raddbg_core.c +++ b/src/raddbg/raddbg_core.c @@ -1750,6 +1750,8 @@ rd_title_fstrs_from_entity(Arena *arena, RD_Entity *entity, Vec4F32 secondary_co RD_Entity *args = rd_entity_child_from_kind(entity, RD_EntityKind_Arguments); RD_Entity *loc = rd_entity_child_from_kind(entity, RD_EntityKind_Location); RD_Entity *cnd = rd_entity_child_from_kind(entity, RD_EntityKind_Condition); + RD_Entity *src = rd_entity_child_from_kind(entity, RD_EntityKind_Source); + RD_Entity *dst = rd_entity_child_from_kind(entity, RD_EntityKind_Dest); RD_IconKind icon_kind = rd_entity_kind_icon_kind_table[entity->kind]; Vec4F32 color = rd_rgba_from_theme_color(RD_ThemeColor_Text); if(icon_kind != RD_IconKind_Null) @@ -1844,6 +1846,14 @@ rd_title_fstrs_from_entity(Arena *arena, RD_Entity *entity, Vec4F32 secondary_co dr_fancy_string_list_concat_in_place(&result, &cnd_fstrs); } } + if(!rd_entity_is_nil(src) && !rd_entity_is_nil(dst)) + { + dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, color, src->string); + dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" ")); + dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Icons), size, secondary_color, rd_icon_kind_text_table[RD_IconKind_RightArrow]); + dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" ")); + dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Main), size, color, dst->string); + } if((entity->kind == RD_EntityKind_Target || entity->kind == RD_EntityKind_Breakpoint) && entity->disabled) { dr_fancy_string_list_push_new(arena, &result, rd_font_from_slot(RD_FontSlot_Code), size, v4f32(0, 0, 0, 0), str8_lit(" ")); @@ -2072,6 +2082,8 @@ rd_ctrl_meta_eval_from_entity(Arena *arena, RD_Entity *entity) RD_Entity *entr= rd_entity_child_from_kind(entity, RD_EntityKind_EntryPoint); RD_Entity *loc = rd_entity_child_from_kind(entity, RD_EntityKind_Location); RD_Entity *cnd = rd_entity_child_from_kind(entity, RD_EntityKind_Condition); + RD_Entity *src = rd_entity_child_from_kind(entity, RD_EntityKind_Source); + RD_Entity *dst = rd_entity_child_from_kind(entity, RD_EntityKind_Dest); String8 label_string = push_str8_copy(arena, entity->string); String8 src_loc_string = {0}; String8 vaddr_loc_string = {0}; @@ -2100,6 +2112,8 @@ rd_ctrl_meta_eval_from_entity(Arena *arena, RD_Entity *entity) meval->source_location = src_loc_string; meval->address_location = vaddr_loc_string; meval->function_location = function_loc_string; + meval->source_path = src->string; + meval->destination_path = dst->string; meval->condition = cnd_string; return meval; } @@ -2312,6 +2326,8 @@ rd_eval_space_write(void *u, E_Space space, void *in, Rng1U64 range) StringMemberCase(args) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_Arguments), str8_cstring_capped(in, (U8 *)in + 4096));} StringMemberCase(working_directory) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_WorkingDirectory), str8_cstring_capped(in, (U8 *)in + 4096));} StringMemberCase(entry_point) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_EntryPoint), str8_cstring_capped(in, (U8 *)in + 4096));} + StringMemberCase(source_path) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_Source), str8_cstring_capped(in, (U8 *)in + 4096));} + StringMemberCase(destination_path) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_Dest), str8_cstring_capped(in, (U8 *)in + 4096));} StringMemberCase(condition) {result = 1; rd_entity_equip_name(rd_entity_child_from_kind_or_alloc(entity, RD_EntityKind_Condition), str8_cstring_capped(in, (U8 *)in + 4096));} StringMemberCase(source_location) { @@ -11675,7 +11691,7 @@ rd_frame(void) { RD_CmdKindInfo *info = rd_cmd_kind_info_from_string(cmd->regs->string); - // rjf: command simply executes - just no-op in this layer + // rjf: command does not have a query - simply execute with the current registers if(!(info->query.flags & RD_QueryFlag_Required)) { RD_RegsScope(.string = str8_zero()) rd_push_cmd(cmd->regs->string, rd_regs()); @@ -13195,6 +13211,59 @@ rd_frame(void) //- rjf: unpack String8 src_path = rd_regs()->string; String8 dst_path = rd_regs()->file_path; + String8 src_path__normalized = path_normalized_from_string(scratch.arena, src_path); + String8 dst_path__normalized = path_normalized_from_string(scratch.arena, dst_path); + String8List src_path_parts = str8_split_path(scratch.arena, src_path__normalized); + String8List dst_path_parts = str8_split_path(scratch.arena, dst_path__normalized); + + //- rjf: reverse path parts + String8List src_path_parts__reversed = {0}; + String8List dst_path_parts__reversed = {0}; + for(String8Node *n = src_path_parts.first; n != 0; n = n->next) + { + str8_list_push_front(scratch.arena, &src_path_parts__reversed, n->string); + } + for(String8Node *n = dst_path_parts.first; n != 0; n = n->next) + { + str8_list_push_front(scratch.arena, &dst_path_parts__reversed, n->string); + } + + //- rjf: trace from each path upwards, in lock-step, to find the first difference + // between the paths + String8Node *first_diff_src = src_path_parts__reversed.first; + String8Node *first_diff_dst = dst_path_parts__reversed.first; + for(;first_diff_src != 0 && first_diff_dst != 0;) + { + if(!str8_match(first_diff_src->string, first_diff_dst->string, StringMatchFlag_CaseInsensitive)) + { + break; + } + first_diff_src = first_diff_src->next; + first_diff_dst = first_diff_dst->next; + } + + //- rjf: form final map paths + String8List map_src_parts = {0}; + String8List map_dst_parts = {0}; + for(String8Node *n = first_diff_src; n != 0; n = n->next) + { + str8_list_push_front(scratch.arena, &map_src_parts, n->string); + } + for(String8Node *n = first_diff_dst; n != 0; n = n->next) + { + str8_list_push_front(scratch.arena, &map_dst_parts, n->string); + } + StringJoin map_join = {.sep = str8_lit("/")}; + String8 map_src = str8_list_join(scratch.arena, &map_src_parts, &map_join); + String8 map_dst = str8_list_join(scratch.arena, &map_dst_parts, &map_join); + + //- rjf: store as file path map entity + RD_Entity *map = rd_entity_alloc(rd_entity_root(), RD_EntityKind_FilePathMap); + RD_Entity *src = rd_entity_alloc(map, RD_EntityKind_Source); + RD_Entity *dst = rd_entity_alloc(map, RD_EntityKind_Dest); + rd_entity_equip_name(src, map_src); + rd_entity_equip_name(dst, map_dst); + #if 0 // TODO(rjf): @msgs //- rjf: grab src file & chosen replacement diff --git a/src/raddbg/raddbg_views.c b/src/raddbg/raddbg_views.c index dca92ac4..a192fd13 100644 --- a/src/raddbg/raddbg_views.c +++ b/src/raddbg/raddbg_views.c @@ -951,6 +951,28 @@ rd_watch_view_row_kind_from_flags_row_info(RD_WatchViewFlags flags, EV_Row *row, //- rjf: row/column -> strings +internal E_Expr * +rd_expr_from_watch_view_row_column(Arena *arena, EV_View *ev_view, EV_Row *row, RD_WatchViewColumn *col) +{ + E_Expr *expr = row->expr; + switch(col->kind) + { + default:{}break; + case RD_WatchViewColumnKind_Member: + { + Temp scratch = scratch_begin(&arena, 1); + String8 access_string = str8(col->string_buffer, col->string_size); + String8List accesses = str8_split(scratch.arena, access_string, (U8 *)".", 1, 0); + for(String8Node *n = accesses.first; n != 0; n = n->next) + { + expr = e_expr_ref_member_access(arena, expr, n->string); + } + scratch_end(scratch); + }break; + } + return expr; +} + internal String8 rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_WatchViewColumn *col, EV_StringFlags string_flags, U32 default_radix, FNT_Tag font, F32 font_size, F32 max_size_px) { @@ -961,6 +983,7 @@ rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_Wa view_rules = ev_view_rule_list_copy(arena, row->view_rules); ev_view_rule_list_push_string(arena, view_rules, str8(col->view_rule_buffer, col->view_rule_size)); } + E_Expr *row_col_expr = rd_expr_from_watch_view_row_column(arena, ev, row, col); switch(col->kind) { default:{}break; @@ -969,13 +992,14 @@ rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_Wa result = ev_expr_string_from_row(arena, row, string_flags); }break; case RD_WatchViewColumnKind_Value: + case RD_WatchViewColumnKind_Member: { - E_Eval eval = e_eval_from_expr(arena, row->expr); + E_Eval eval = e_eval_from_expr(arena, row_col_expr); result = rd_value_string_from_eval(arena, string_flags, default_radix, font, font_size, max_size_px, eval, row->member, view_rules); }break; case RD_WatchViewColumnKind_Type: { - E_IRTreeAndType irtree = e_irtree_and_type_from_expr(arena, row->expr); + E_IRTreeAndType irtree = e_irtree_and_type_from_expr(arena, row_col_expr); E_TypeKey type_key = irtree.type_key; result = !e_type_key_match(type_key, e_type_key_zero()) ? e_type_string_from_key(arena, type_key) : str8_zero(); result = str8_skip_chop_whitespace(result); @@ -984,24 +1008,9 @@ rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_Wa { result = ev_view_rule_from_key(ev, row->key); }break; - case RD_WatchViewColumnKind_Member: - { - Temp scratch = scratch_begin(&arena, 1); - String8 access_string = str8(col->string_buffer, col->string_size); - String8List accesses = str8_split(scratch.arena, access_string, (U8 *)".", 1, 0); - E_Expr *expr = row->expr; - for(String8Node *n = accesses.first; n != 0; n = n->next) - { - expr = e_expr_ref_member_access(arena, expr, n->string); - } - expr = ev_resolved_from_expr(scratch.arena, expr, view_rules); - E_Eval eval = e_eval_from_expr(arena, expr); - result = rd_value_string_from_eval(arena, string_flags, default_radix, font, font_size, max_size_px, eval, row->member, view_rules); - scratch_end(scratch); - }break; case RD_WatchViewColumnKind_Module: { - E_Eval eval = e_eval_from_expr(arena, row->expr); + E_Eval eval = e_eval_from_expr(arena, row_col_expr); E_Eval value_eval = e_value_eval_from_eval(eval); CTRL_Entity *entity = rd_ctrl_entity_from_eval_space(eval.space); CTRL_Entity *process = ctrl_process_from_entity(entity); @@ -1012,9 +1021,9 @@ rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_Wa { Temp scratch = scratch_begin(&arena, 1); DI_Scope *di_scope = di_scope_open(); - E_Eval eval = e_eval_from_expr(arena, row->expr); - E_Expr *vaddr_expr = e_expr_ref_member_access(scratch.arena, row->expr, str8_lit("vaddr")); - E_Expr *depth_expr = e_expr_ref_member_access(scratch.arena, row->expr, str8_lit("inline_depth")); + E_Eval eval = e_eval_from_expr(arena, row_col_expr); + E_Expr *vaddr_expr = e_expr_ref_member_access(scratch.arena, row_col_expr, str8_lit("vaddr")); + E_Expr *depth_expr = e_expr_ref_member_access(scratch.arena, row_col_expr, str8_lit("inline_depth")); E_Eval vaddr_eval = e_eval_from_expr(scratch.arena, vaddr_expr); E_Eval depth_eval = e_eval_from_expr(scratch.arena, depth_expr); E_Eval vaddr_value_eval = e_value_eval_from_eval(vaddr_eval); @@ -1212,6 +1221,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo {RD_EntityKind_Target, CTRL_EntityKind_Null, RD_CmdKind_RemoveEntity }, {RD_EntityKind_Breakpoint, CTRL_EntityKind_Null, RD_CmdKind_EnableEntity }, {RD_EntityKind_Breakpoint, CTRL_EntityKind_Null, RD_CmdKind_RemoveEntity }, + {RD_EntityKind_FilePathMap,CTRL_EntityKind_Null, RD_CmdKind_RemoveEntity }, {RD_EntityKind_Nil, CTRL_EntityKind_Thread, RD_CmdKind_FreezeThread }, {RD_EntityKind_Nil, CTRL_EntityKind_Thread, RD_CmdKind_SelectThread }, }; @@ -1680,11 +1690,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo B32 success = 0; if(rows.first != 0) { - E_Expr *expr = rows.first->expr; - if(col->kind == RD_WatchViewColumnKind_Member) - { - expr = e_expr_ref_member_access(scratch.arena, expr, str8(col->string_buffer, col->string_size)); - } + E_Expr *expr = rd_expr_from_watch_view_row_column(scratch.arena, eval_view, row, col); E_Eval dst_eval = e_eval_from_expr(scratch.arena, expr); success = rd_commit_eval_value_string(dst_eval, new_string); } @@ -1830,11 +1836,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo } else if(tbl.y != 0 && (col->kind == RD_WatchViewColumnKind_Value || col->kind == RD_WatchViewColumnKind_Member) && row_kind == RD_WatchViewRowKind_Normal) { - E_Expr *expr = row->expr; - if(col->kind == RD_WatchViewColumnKind_Member) - { - expr = e_expr_ref_member_access(scratch.arena, expr, str8(col->string_buffer, col->string_size)); - } + E_Expr *expr = rd_expr_from_watch_view_row_column(scratch.arena, eval_view, row, col); E_Eval dst_eval = e_eval_from_expr(scratch.arena, expr); rd_commit_eval_value_string(dst_eval, str8_zero()); } @@ -2179,7 +2181,6 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo } E_Type *row_type = e_type_from_key(scratch.arena, row_eval.type_key); B32 row_is_expandable = ev_row_is_expandable(row); - B32 row_is_editable = ev_row_is_editable(row); B32 next_row_expanded = row_expanded; RD_ViewRuleInfo *ui_view_rule_info = rd_view_rule_info_from_string(row->block->expand_view_rule_info->string); MD_Node *ui_view_rule_params_root = row->block->expand_view_rule_params; @@ -2312,6 +2313,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo ui_spacer(ui_em(1.5f, 1)); RD_Font(RD_FontSlot_Code) ui_labelf("only:(member_1 ... member_n)"); UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) ui_label_multiline(max_width, str8_lit("Specifies that only the specified members should appear in struct, union, or class evaluations.")); + ui_spacer(ui_em(1.5f, 1)); RD_Font(RD_FontSlot_Code) ui_labelf("list:(next_link_member_name)"); UI_FlagsAdd(UI_BoxFlag_DrawTextWeak) ui_label_multiline(max_width, str8_lit("Specifies that some struct, union, or class forms the top of a linked list, with next_link_member_name being the member which points at the next element in the list.")); ui_spacer(ui_em(1.5f, 1)); @@ -2498,6 +2500,15 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo rd_cmd(RD_CmdKind_RunCommand, .string = rd_cmd_kind_info_table[RD_CmdKind_AddWatchPin].string); } } + if(rd_entity_is_nil(entity) && collection_entity_kind == RD_EntityKind_FilePathMap) + UI_Palette(palette) + { + ui_set_next_focus_hot(row_selected ? UI_FocusKind_On : UI_FocusKind_Off); + if(ui_clicked(rd_icon_buttonf(RD_IconKind_Add, 0, "Add New"))) + { + rd_entity_alloc(rd_entity_root(), RD_EntityKind_FilePathMap); + } + } //- rjf: build entity box if(!rd_entity_is_nil(entity) || ctrl_entity != &ctrl_entity_nil) @@ -2723,6 +2734,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo //- rjf: unpack column-kind-specific info E_Eval cell_eval = row_eval; + E_Type *cell_type = row_type; B32 cell_can_edit = 0; FuzzyMatchRangeList cell_matches = {0}; String8 cell_inheritance_string = {0}; @@ -2772,7 +2784,9 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo }goto value_cell; case RD_WatchViewColumnKind_Member: { - cell_eval = e_member_eval_from_eval_member_name(cell_eval, str8(col->string_buffer, col->string_size)); + E_Expr *expr = rd_expr_from_watch_view_row_column(scratch.arena, eval_view, row, col); + cell_eval = e_eval_from_expr(scratch.arena, expr); + cell_type = e_type_from_key(scratch.arena, cell_eval.type_key); }goto value_cell; value_cell:; { @@ -2803,7 +2817,7 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo RD_AutoCompListerFlag_Globals| RD_AutoCompListerFlag_ThreadLocals| RD_AutoCompListerFlag_Types); - if(row_type->flags & E_TypeFlag_IsPathText) + if(cell_type->flags & E_TypeFlag_IsPathText) { cell_autocomp_flags = RD_AutoCompListerFlag_Files; } @@ -2897,13 +2911,14 @@ rd_watch_view_build(RD_WatchViewState *ewv, RD_WatchViewFlags flags, String8 roo } }break; case RD_WatchViewColumnKind_Value: + case RD_WatchViewColumnKind_Member: { - if(row_type->flags & E_TypeFlag_IsCodeText) + if(cell_type->flags & E_TypeFlag_IsCodeText) { cell_is_code = 1; } - else if(row_type->flags & E_TypeFlag_IsPathText || - row_type->flags & E_TypeFlag_IsPlainText) + else if(cell_type->flags & E_TypeFlag_IsPathText || + cell_type->flags & E_TypeFlag_IsPlainText) { cell_is_code = 0; } @@ -5042,10 +5057,10 @@ RD_VIEW_RULE_UI_FUNCTION_DEF(file_path_map) { rd_watch_view_init(wv); rd_watch_view_column_alloc(wv, RD_WatchViewColumnKind_Expr, 0.25f); - rd_watch_view_column_alloc(wv, RD_WatchViewColumnKind_Value, 0.75f, .dequote_string = 1); + rd_watch_view_column_alloc(wv, RD_WatchViewColumnKind_Value, 0.75f, .dequote_string = 1, .is_non_code = 0); } rd_watch_view_build(wv, RD_WatchViewFlag_NoHeader|RD_WatchViewFlag_PrettyNameMembers|RD_WatchViewFlag_PrettyEntityRows|RD_WatchViewFlag_DisableCacheLines, - str8_lit("file_path_maps"), str8_lit("only: source_path destination_path str"), 0, 10, rect); + str8_lit("file_path_maps"), str8_lit("only: source_path destination_path str"), 1, 10, rect); ProfEnd(); } diff --git a/src/raddbg/raddbg_views.h b/src/raddbg/raddbg_views.h index db1aeceb..53c1b71d 100644 --- a/src/raddbg/raddbg_views.h +++ b/src/raddbg/raddbg_views.h @@ -192,7 +192,8 @@ internal RD_WatchViewRowInfo rd_watch_view_row_info_from_row(EV_Row *row); //- rjf: watch view flags & row & row info -> row kind internal RD_WatchViewRowKind rd_watch_view_row_kind_from_flags_row_info(RD_WatchViewFlags flags, EV_Row *row, RD_WatchViewRowInfo *info); -//- rjf: row/column -> strings +//- rjf: row/column -> exprs / strings +internal E_Expr *rd_expr_from_watch_view_row_column(Arena *arena, EV_View *ev_view, EV_Row *row, RD_WatchViewColumn *col); internal String8 rd_string_from_eval_viz_row_column(Arena *arena, EV_View *ev, EV_Row *row, RD_WatchViewColumn *col, EV_StringFlags string_flags, U32 default_radix, FNT_Tag font, F32 font_size, F32 max_size_px); //- rjf: table coordinates -> text edit state diff --git a/src/ui/ui_core.c b/src/ui/ui_core.c index 368d12c0..1bc64cd2 100644 --- a/src/ui/ui_core.c +++ b/src/ui/ui_core.c @@ -2508,7 +2508,7 @@ ui_box_text_position(UI_Box *box) FNT_Tag font = box->font; F32 font_size = box->font_size; FNT_Metrics font_metrics = fnt_metrics_from_tag_size(font, font_size); - result.y = floor_f32((box->rect.p0.y + box->rect.p1.y)/2.f) + font_metrics.capital_height/2.f; + result.y = floor_f32((box->rect.p0.y + box->rect.p1.y)/2.f) + font_metrics.capital_height/2.f - 1.f; if(!fnt_tag_match(font, ui_icon_font())) { result.y += font_metrics.descent/2;