diff --git a/demo/sdl3_renderer/main.c b/demo/sdl3_renderer/main.c index 2c7725b8f..dfdfdbfd9 100644 --- a/demo/sdl3_renderer/main.c +++ b/demo/sdl3_renderer/main.c @@ -323,6 +323,113 @@ SDL_AppEvent(void *appstate, SDL_Event* event) return SDL_APP_CONTINUE; } +/* Visual checks for the tree-element hit box and nk_combo height cap. + * Styles are exaggerated so a regression is obvious. */ +static void +layout_sizing_bugs(struct nk_context *ctx) +{ + static const char *items[] = { + "Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune" + }; + static int combo_small = 0; + static int combo_large = 0; + static nk_bool tree_selected = nk_true; + + const int count = (int)NK_LEN(items); + const int item_h = 25; + const float demo_sel_pad = 14.0f; + const float demo_combo_pad = 48.0f; + const float demo_parent_pad = 48.0f; + + struct nk_vec2 sel_pad_save; + struct nk_vec2 combo_pad_save; + struct nk_vec2 win_pad_save; + float spacing_y, combo_border; + float accurate, current_h; + + if (!nk_begin(ctx, "Sizing bugs", nk_rect(430, 40, 400, 620), + NK_WINDOW_BORDER|NK_WINDOW_MOVABLE|NK_WINDOW_SCALABLE| + NK_WINDOW_MINIMIZABLE|NK_WINDOW_TITLE)) + { + nk_end(ctx); + return; + } + + spacing_y = ctx->style.window.spacing.y; + combo_border = ctx->style.window.combo_border; + + /* ---- tree element: selectable is text + 2 * padding ---- */ + nk_layout_row_dynamic(ctx, 18, 1); + nk_label(ctx, "Tree element: selectable hugs the text", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 54, 1); + nk_label_wrap(ctx, "nk_tree_element_* sizes the title to text width + 2 * selectable.padding.x so nk_do_selectable can inset both sides. Highlight should have equal pad left and right of 'Hi'."); + + sel_pad_save = ctx->style.selectable.padding; + ctx->style.selectable.padding.x = demo_sel_pad; + + if (nk_tree_element_push(ctx, NK_TREE_NODE, "Hi", NK_MAXIMIZED, &tree_selected)) { + nk_label(ctx, "Child row (collapse uses the triangle only).", NK_TEXT_LEFT); + nk_tree_element_pop(ctx); + } + ctx->style.selectable.padding = sel_pad_save; + + nk_layout_row_dynamic(ctx, 18, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "selected=%s pad.x=%.0f (box = text + 2*%.0f)", + tree_selected ? "true" : "false", demo_sel_pad, demo_sel_pad); + nk_layout_row_dynamic(ctx, 32, 1); + nk_label_wrap(ctx, "Click in the right pad (still toggles), then further right on the same row (does not)."); + + nk_layout_row_dynamic(ctx, 8, 1); + nk_spacing(ctx, 1); + + /* ---- combo with large combo_padding: cap must include it ---- */ + nk_layout_row_dynamic(ctx, 18, 1); + nk_label(ctx, "Combo + large combo_padding.y", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 54, 1); + nk_label_wrap(ctx, "Height cap includes combo_padding and border. The top band is padding; all items should fit with no scrollbar."); + + accurate = (float)count * (float)item_h + (float)count * spacing_y + + demo_combo_pad + 2.0f * combo_border; + current_h = (float)count * (float)item_h + (float)count * spacing_y + + 2.0f * spacing_y + 2.0f * ctx->style.window.padding.y; + + nk_layout_row_dynamic(ctx, 18, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "nk_combo cap=%.0f old formula=%.0f", accurate, current_h); + + combo_pad_save = ctx->style.window.combo_padding; + ctx->style.window.combo_padding.y = demo_combo_pad; + nk_layout_row_dynamic(ctx, (float)item_h, 1); + combo_small = nk_combo(ctx, items, count, combo_small, item_h, + nk_vec2(nk_widget_width(ctx), 400)); + ctx->style.window.combo_padding = combo_pad_save; + + nk_layout_row_dynamic(ctx, 8, 1); + nk_spacing(ctx, 1); + + /* ---- combo with large parent padding: cap must ignore it ---- */ + nk_layout_row_dynamic(ctx, 18, 1); + nk_label(ctx, "Combo + large window.padding.y", NK_TEXT_LEFT); + nk_layout_row_dynamic(ctx, 54, 1); + nk_label_wrap(ctx, "Height cap uses combo padding, not parent window.padding. Allocated height should match the list; a click just below the last item should close the combo."); + + accurate = (float)count * (float)item_h + (float)count * spacing_y + + ctx->style.window.combo_padding.y + 2.0f * combo_border; + current_h = (float)count * (float)item_h + (float)count * spacing_y + + 2.0f * spacing_y + 2.0f * demo_parent_pad; + + nk_layout_row_dynamic(ctx, 18, 1); + nk_labelf(ctx, NK_TEXT_LEFT, "nk_combo cap=%.0f old formula=%.0f", accurate, current_h); + + win_pad_save = ctx->style.window.padding; + ctx->style.window.padding.y = demo_parent_pad; + nk_layout_row_dynamic(ctx, (float)item_h, 1); + combo_large = nk_combo(ctx, items, count, combo_large, item_h, + nk_vec2(nk_widget_width(ctx), 400)); + ctx->style.window.padding = win_pad_save; + + nk_end(ctx); +} + SDL_AppResult SDL_AppIterate(void *appstate) { @@ -371,6 +478,8 @@ SDL_AppIterate(void *appstate) } nk_end(ctx); + layout_sizing_bugs(ctx); + /* -------------- EXAMPLES ---------------- */ #ifdef INCLUDE_CALCULATOR calculator(ctx); diff --git a/nuklear.h b/nuklear.h index 3f77eb7d1..c35972a91 100644 --- a/nuklear.h +++ b/nuklear.h @@ -23240,10 +23240,10 @@ nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type /* draw label */ {nk_flags dummy = 0; struct nk_rect label; - /* calculate size of the text and tooltip */ + /* selectable insets by padding on both sides */ text_len = nk_strlen(title); text_width = style->font->width(style->font->userdata, style->font->height, title, text_len); - text_width += (4 * padding.x); + text_width += 2 * padding.x; header.w = NK_MAX(header.w, sym.w + item_spacing.x); label.x = sym.x + sym.w + item_spacing.x; @@ -30013,6 +30013,22 @@ nk_color_picker(struct nk_context *ctx, struct nk_colorf color, * COMBO * * ===============================================================*/ +/* Outer popup height that fits `count` rows: each row is item_height plus + * trailing spacing, plus combo top padding and the border nk_panel_begin + * subtracts. Combos always get NK_WINDOW_BORDER from nk_nonblock_begin. */ +NK_INTERN float +nk_combo_calc_max_height(const struct nk_context *ctx, int count, int item_height) +{ + struct nk_vec2 spacing; + struct nk_vec2 padding; + + spacing = ctx->style.window.spacing; + padding = nk_panel_get_padding(&ctx->style, NK_PANEL_COMBO); + return (float)count * (float)item_height + + (float)count * spacing.y + + padding.y + + 2.0f * ctx->style.window.combo_border; +} NK_INTERN nk_bool nk_combo_begin(struct nk_context *ctx, struct nk_window *win, struct nk_vec2 size, nk_bool is_clicked, struct nk_rect header) @@ -30713,9 +30729,7 @@ nk_combo(struct nk_context *ctx, const char *const *items, int count, int selected, int item_height, struct nk_vec2 size) { int i = 0; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; NK_ASSERT(ctx); NK_ASSERT(items); @@ -30723,11 +30737,8 @@ nk_combo(struct nk_context *ctx, const char *const *items, int count, if (!ctx || !items ||!count) return selected; - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); if (nk_combo_begin_label(ctx, items[selected], size)) { nk_layout_row_dynamic(ctx, (float)item_height, 1); for (i = 0; i < count; ++i) { @@ -30743,9 +30754,7 @@ nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separa int separator, int selected, int count, int item_height, struct nk_vec2 size) { int i; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; const char *current_item; const char *iter; int length = 0; @@ -30755,12 +30764,8 @@ nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separa if (!ctx || !items_separated_by_separator) return selected; - /* calculate popup window */ - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); /* find selected item */ current_item = items_separated_by_separator; @@ -30798,9 +30803,7 @@ nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const c void *userdata, int selected, int count, int item_height, struct nk_vec2 size) { int i; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; const char *item; NK_ASSERT(ctx); @@ -30808,12 +30811,8 @@ nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const c if (!ctx || !item_getter) return selected; - /* calculate popup window */ - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); item_getter(userdata, selected, &item); if (nk_combo_begin_label(ctx, item, size)) { @@ -31031,7 +31030,9 @@ nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos text_len = nk_strlen(text); text_width = style->font->width(style->font->userdata, style->font->height, text, text_len); - text_width += (4 * padding.x); + /* outer bounds: nk_panel_begin subtracts popup padding and border */ + text_width += 2 * style->window.popup_padding.x; + text_width += 2 * style->window.popup_border; text_height = (style->font->height + 2 * padding.y); /* execute tooltip and fill with text */ diff --git a/src/nuklear_combo.c b/src/nuklear_combo.c index b0bada143..07e43629b 100644 --- a/src/nuklear_combo.c +++ b/src/nuklear_combo.c @@ -6,6 +6,22 @@ * COMBO * * ===============================================================*/ +/* Outer popup height that fits `count` rows: each row is item_height plus + * trailing spacing, plus combo top padding and the border nk_panel_begin + * subtracts. Combos always get NK_WINDOW_BORDER from nk_nonblock_begin. */ +NK_INTERN float +nk_combo_calc_max_height(const struct nk_context *ctx, int count, int item_height) +{ + struct nk_vec2 spacing; + struct nk_vec2 padding; + + spacing = ctx->style.window.spacing; + padding = nk_panel_get_padding(&ctx->style, NK_PANEL_COMBO); + return (float)count * (float)item_height + + (float)count * spacing.y + + padding.y + + 2.0f * ctx->style.window.combo_border; +} NK_INTERN nk_bool nk_combo_begin(struct nk_context *ctx, struct nk_window *win, struct nk_vec2 size, nk_bool is_clicked, struct nk_rect header) @@ -706,9 +722,7 @@ nk_combo(struct nk_context *ctx, const char *const *items, int count, int selected, int item_height, struct nk_vec2 size) { int i = 0; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; NK_ASSERT(ctx); NK_ASSERT(items); @@ -716,11 +730,8 @@ nk_combo(struct nk_context *ctx, const char *const *items, int count, if (!ctx || !items ||!count) return selected; - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); if (nk_combo_begin_label(ctx, items[selected], size)) { nk_layout_row_dynamic(ctx, (float)item_height, 1); for (i = 0; i < count; ++i) { @@ -736,9 +747,7 @@ nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separa int separator, int selected, int count, int item_height, struct nk_vec2 size) { int i; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; const char *current_item; const char *iter; int length = 0; @@ -748,12 +757,8 @@ nk_combo_separator(struct nk_context *ctx, const char *items_separated_by_separa if (!ctx || !items_separated_by_separator) return selected; - /* calculate popup window */ - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); /* find selected item */ current_item = items_separated_by_separator; @@ -791,9 +796,7 @@ nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const c void *userdata, int selected, int count, int item_height, struct nk_vec2 size) { int i; - int max_height; - struct nk_vec2 item_spacing; - struct nk_vec2 window_padding; + float max_height; const char *item; NK_ASSERT(ctx); @@ -801,12 +804,8 @@ nk_combo_callback(struct nk_context *ctx, void(*item_getter)(void*, int, const c if (!ctx || !item_getter) return selected; - /* calculate popup window */ - item_spacing = ctx->style.window.spacing; - window_padding = nk_panel_get_padding(&ctx->style, ctx->current->layout->type); - max_height = count * item_height + count * (int)item_spacing.y; - max_height += (int)item_spacing.y * 2 + (int)window_padding.y * 2; - size.y = NK_MIN(size.y, (float)max_height); + max_height = nk_combo_calc_max_height(ctx, count, item_height); + size.y = NK_MIN(size.y, max_height); item_getter(userdata, selected, &item); if (nk_combo_begin_label(ctx, item, size)) { diff --git a/src/nuklear_tooltip.c b/src/nuklear_tooltip.c index 08a00a440..6cc959ae7 100644 --- a/src/nuklear_tooltip.c +++ b/src/nuklear_tooltip.c @@ -168,7 +168,9 @@ nk_tooltip_offset(struct nk_context *ctx, const char *text, enum nk_tooltip_pos text_len = nk_strlen(text); text_width = style->font->width(style->font->userdata, style->font->height, text, text_len); - text_width += (4 * padding.x); + /* outer bounds: nk_panel_begin subtracts popup padding and border */ + text_width += 2 * style->window.popup_padding.x; + text_width += 2 * style->window.popup_border; text_height = (style->font->height + 2 * padding.y); /* execute tooltip and fill with text */ diff --git a/src/nuklear_tree.c b/src/nuklear_tree.c index 0ccf68ecb..2a6d1f514 100644 --- a/src/nuklear_tree.c +++ b/src/nuklear_tree.c @@ -281,10 +281,10 @@ nk_tree_element_image_push_hashed_base(struct nk_context *ctx, enum nk_tree_type /* draw label */ {nk_flags dummy = 0; struct nk_rect label; - /* calculate size of the text and tooltip */ + /* selectable insets by padding on both sides */ text_len = nk_strlen(title); text_width = style->font->width(style->font->userdata, style->font->height, title, text_len); - text_width += (4 * padding.x); + text_width += 2 * padding.x; header.w = NK_MAX(header.w, sym.w + item_spacing.x); label.x = sym.x + sym.w + item_spacing.x;