38static double _clamp(
double v,
double lo,
double hi) {
39 if (v < lo)
return lo;
40 if (v > hi)
return hi;
48 if (step <= 0.0) step = 1.0;
49 double steps = round((val - min_val) / step);
50 double snapped = min_val + steps * step;
51 if (snapped < min_val) snapped = min_val;
52 if (snapped > max_val) snapped = max_val;
60 if (font && text) al_get_text_dimensions(font, text, &d.
x, &d.
y, &d.
w, &d.
h);
95 for (
char* src = s; *src; src++) {
96 if (*src !=
'\r') *dst++ = *src;
105static float _text_w(ALLEGRO_FONT* font,
const char* text) {
106 if (font && text)
return (
float)al_get_text_width(font, text);
116static int _point_in_rect(
float px,
float py,
float rx,
float ry,
float rw,
float rh) {
117 return (px >= rx && px <= rx + rw && py >= ry && py <= ry + rh);
131static float _scrollbar_calc_scroll(
float mouse,
float track_start,
float track_length,
float viewport,
float content,
float thumb_min) {
132 float ratio = viewport / content;
133 if (ratio > 1.0f) ratio = 1.0f;
134 float thumb = ratio * track_length;
135 if (thumb < thumb_min) thumb = thumb_min;
136 float max_scroll = content - viewport;
137 float track_range = track_length - thumb;
138 if (track_range <= 0 || max_scroll <= 0)
return 0;
139 float pos_ratio = (mouse - track_start - thumb / 2.0f) / track_range;
140 if (pos_ratio < 0) pos_ratio = 0;
141 if (pos_ratio > 1) pos_ratio = 1;
142 return pos_ratio * max_scroll;
154static int _scrollbar_calc_scroll_int(
float mouse,
float track_start,
float track_length,
int visible_items,
int total_items,
float thumb_min) {
155 int max_off = total_items - visible_items;
156 if (max_off <= 0)
return 0;
158 (
float)visible_items, (
float)total_items, thumb_min);
159 int offset = (int)(scroll + 0.5f);
160 if (offset < 0) offset = 0;
161 if (offset > max_off) offset = max_off;
233 if (win && win->
id == window_id)
return node;
243 snprintf(
key,
sizeof(
key),
"%d", w->
id);
250 size_t new_cap = (*cap == 0) ? 8 : (*cap) * 2;
251 if (new_cap < *cap)
return 0;
255 if (*items && *nb > 0) {
296 t.
bg_normal = al_map_rgba(50, 50, 60, 230);
297 t.
bg_hover = al_map_rgba(70, 70, 85, 240);
298 t.
bg_active = al_map_rgba(90, 90, 110, 250);
303 t.
text_hover = al_map_rgba(255, 255, 255, 255);
330 ALLEGRO_COLOR bg_hover,
331 ALLEGRO_COLOR bg_active,
332 ALLEGRO_COLOR border,
333 ALLEGRO_COLOR border_hover,
334 ALLEGRO_COLOR border_active,
336 ALLEGRO_COLOR text_hover,
337 ALLEGRO_COLOR text_active,
338 float border_thickness,
396 s.
grip_color = al_map_rgba(160, 160, 180, 200);
473 n_log(
LOG_ERR,
"n_gui_new_ctx: failed to allocate window list");
479 n_log(
LOG_ERR,
"n_gui_new_ctx: failed to allocate widget hash table");
525 if ((*ctx)->windows) {
528 if ((*ctx)->widgets_by_id) {
544 if (dw <= 0 || dh <= 0)
return;
565 if (win->
w > 0 && win->
h > 0) {
591 win->
title[0] =
'\0';
601 n_log(
LOG_ERR,
"n_gui_add_window: failed to allocate widget list");
648 if (win) win->
state &= ~N_GUI_WIN_OPEN;
710 for (
int i = 1; i < n; i++) {
717 if (gj > gi || (gj == gi && gi == 1 && arr[j]->z_value > tmp->
z_value)) {
734 for (
int i = 0; i < n; i++) {
785 n_log(
LOG_ERR,
"n_gui_window_set_zorder: unknown z_mode %d for window %d (expected N_GUI_ZORDER_NORMAL, N_GUI_ZORDER_ALWAYS_ON_TOP, N_GUI_ZORDER_ALWAYS_BEHIND, or N_GUI_ZORDER_FIXED), falling back to N_GUI_ZORDER_NORMAL", z_mode, window_id);
845 if (win) win->
flags = flags;
854 if (win)
return win->
flags;
864 if (!win || !win->
widgets)
return;
866 float max_right = 0.0f;
867 float max_bottom = 0.0f;
873 float r = wgt->
x + wgt->
w;
874 float b = wgt->
y + wgt->
h;
875 if (r > max_right) max_right = r;
876 if (b > max_bottom) max_bottom = b;
879 float new_w = max_right + pad;
880 float new_h = max_bottom + pad +
_win_tbh(win);
881 if (new_w < win->min_w) new_w = win->
min_w;
882 if (new_h < win->min_h) new_h = win->
min_h;
895 n_log(
LOG_ERR,
"n_gui_window_set_autofit: window %d not found", window_id);
918 float max_right = 0.0f;
919 float max_bottom = 0.0f;
922 if (!wgt || !wgt->
visible)
continue;
923 float r = wgt->
x + wgt->
w;
924 float b = wgt->
y + wgt->
h;
925 if (r > max_right) max_right = r;
926 if (b > max_bottom) max_bottom = b;
936 float needed_w = max_right + border * 2.0f;
937 if (needed_w < win->min_w) needed_w = win->
min_w;
941 win->
x -= (needed_w - win->
w);
948 float needed_h = max_bottom + border * 2.0f + tbh;
949 if (needed_h < win->min_h) needed_h = win->
min_h;
953 win->
y -= (needed_h - win->
h);
965 if (!win || !win->
widgets)
return;
966 float max_right = 0.0f;
967 float max_bottom = 0.0f;
970 if (!wgt || !wgt->
visible)
continue;
971 float r = wgt->
x + wgt->
w;
972 float b = wgt->
y + wgt->
h;
978 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
981 float text_r = wgt->
x + tw + 8.0f;
982 if (text_r > r) r = text_r;
986 if (r > max_right) max_right = r;
987 if (b > max_bottom) max_bottom = b;
1001int n_gui_add_button(
N_GUI_CTX* ctx,
int window_id,
const char* label,
float x,
float y,
float w,
float h,
int shape,
void (*on_click)(
int,
void*),
void* user_data) {
1042int n_gui_add_button_bitmap(
N_GUI_CTX* ctx,
int window_id,
const char* label,
float x,
float y,
float w,
float h, ALLEGRO_BITMAP* normal, ALLEGRO_BITMAP* hover, ALLEGRO_BITMAP* active,
void (*on_click)(
int,
void*),
void* user_data) {
1044 if (
id < 0)
return -1;
1046 if (wgt && wgt->
data) {
1070int n_gui_add_toggle_button(
N_GUI_CTX* ctx,
int window_id,
const char* label,
float x,
float y,
float w,
float h,
int shape,
int initial_state,
void (*on_click)(
int,
void*),
void* user_data) {
1071 int id =
n_gui_add_button(ctx, window_id, label, x, y, w, h, shape, on_click, user_data);
1072 if (
id < 0)
return -1;
1074 if (wgt && wgt->
data) {
1077 bd->
toggled = initial_state ? 1 : 0;
1147 for (
int i = 0; i < count; i++) {
1160int n_gui_add_slider(
N_GUI_CTX* ctx,
int window_id,
float x,
float y,
float w,
float h,
double min_val,
double max_val,
double initial,
int mode,
void (*on_change)(
int,
double,
void*),
void* user_data) {
1200int n_gui_add_vslider(
N_GUI_CTX* ctx,
int window_id,
float x,
float y,
float w,
float h,
double min_val,
double max_val,
double initial,
int mode,
void (*on_change)(
int,
double,
void*),
void* user_data) {
1201 int id =
n_gui_add_slider(ctx, window_id, x, y, w, h, min_val, max_val, initial,
mode, on_change, user_data);
1202 if (
id < 0)
return -1;
1204 if (wgt && wgt->
data) {
1214int n_gui_add_textarea(
N_GUI_CTX* ctx,
int window_id,
float x,
float y,
float w,
float h,
int multiline,
size_t char_limit,
void (*on_change)(
int,
const char*,
void*),
void* user_data) {
1225 memset(td, 0,
sizeof(*td));
1259int n_gui_add_checkbox(
N_GUI_CTX* ctx,
int window_id,
const char* label,
float x,
float y,
float w,
float h,
int initial_checked,
void (*on_toggle)(
int,
int,
void*),
void* user_data) {
1274 cd->
checked = initial_checked ? 1 : 0;
1293int n_gui_add_scrollbar(
N_GUI_CTX* ctx,
int window_id,
float x,
float y,
float w,
float h,
int orientation,
int shape,
double content_size,
double viewport_size,
void (*
on_scroll)(
int,
double,
void*),
void* user_data) {
1305 sb->
content_size = content_size > 0 ? content_size : 1;
1336int n_gui_add_listbox(
N_GUI_CTX* ctx,
int window_id,
float x,
float y,
float w,
float h,
int selection_mode,
void (*on_select)(
int,
int,
int,
void*),
void* user_data) {
1479 id->bitmap = bitmap;
1480 id->scale_mode = scale_mode;
1538int n_gui_add_label_link(
N_GUI_CTX* ctx,
int window_id,
const char* text,
const char* link,
float x,
float y,
float w,
float h,
int align,
void (*
on_link_click)(
int,
const char*,
void*),
void* user_data) {
1540 if (wid < 0)
return -1;
1542 if (wgt && wgt->
data) {
1564 snprintf(
key,
sizeof(
key),
"%d", widget_id);
1577 if (w) w->
theme = theme;
1619 if (prev) prev->
state &= ~N_GUI_STATE_FOCUSED;
1622 if (widget_id < 0) {
1629 n_log(
LOG_ERR,
"n_gui_set_focus: widget %d not found", widget_id);
1646 if (!win || !win->
widgets)
continue;
1649 if (w && w->
id == widget_id) {
1827 if (max_scroll < 0) max_scroll = 0;
1843 sb->
content_size = content_size > 0 ? content_size : 1;
1846 if (max_scroll < 0) max_scroll = 0;
1866 item->
text[0] =
'\0';
1887 if (index < 0 || (
size_t)index >= ld->
nb_items)
return -1;
1888 if ((
size_t)index < ld->nb_items - 1) {
1889 memmove(&ld->
items[index], &ld->
items[index + 1],
1941 if (index >= 0 && (
size_t)index < ld->nb_items) {
1958 for (
size_t i = 0; i < ld->
nb_items; i++) {
1976 if (index >= 0 && (
size_t)index < ld->nb_items) {
1994 if (index < 0 || (
size_t)index >= ld->
nb_items)
return;
2013 if (max_offset < 0) max_offset = 0;
2014 if (offset < 0) offset = 0;
2015 if (offset > max_offset) offset = max_offset;
2034 item->
text[0] =
'\0';
2083 if (index >= -1 && (index == -1 || (
size_t)index < rd->nb_items)) {
2115 item->
text[0] =
'\0';
2149 if (index >= -1 && (index == -1 || (
size_t)index < cd->nb_items)) {
2218 size_t new_cap = (*cap == 0) ? 8 : (*cap) * 2;
2219 if (new_cap < *cap)
return 0;
2223 if (*entries && *nb > 0) {
2237int n_gui_add_dropmenu(
N_GUI_CTX* ctx,
int window_id,
const char* label,
float x,
float y,
float w,
float h,
void (*on_open)(
int,
void*),
void* on_open_user_data) {
2248 dd->
label[0] =
'\0';
2284 memset(e, 0,
sizeof(*e));
2308 n_log(
LOG_ERR,
"Invalid NULL widget for ctx %p widget_id %d", ctx, widget_id);
2314 n_log(
LOG_ERR,
"Widget has NULL data for ctx %p widget_id %d", ctx, widget_id);
2332 for (
size_t i = 0; i < dd->
nb_entries; i++) {
2347 if (dm_max < 0) dm_max = 0;
2370 if (index < 0 || (
size_t)index >= dd->
nb_entries)
return;
2400 n_log(
LOG_WARNING,
"n_gui_window_set_bitmaps: window %d not found", window_id);
2412void n_gui_slider_set_bitmaps(
N_GUI_CTX* ctx,
int widget_id, ALLEGRO_BITMAP* track, ALLEGRO_BITMAP* fill, ALLEGRO_BITMAP* handle, ALLEGRO_BITMAP* handle_hover, ALLEGRO_BITMAP* handle_active) {
2415 n_log(
LOG_WARNING,
"n_gui_slider_set_bitmaps: widget %d is not a slider", widget_id);
2433 n_log(
LOG_WARNING,
"n_gui_scrollbar_set_bitmaps: widget %d is not a scrollbar", widget_id);
2450 n_log(
LOG_WARNING,
"n_gui_checkbox_set_bitmaps: widget %d is not a checkbox", widget_id);
2466 n_log(
LOG_WARNING,
"n_gui_textarea_set_bitmap: widget %d is not a textarea", widget_id);
2480 n_log(
LOG_WARNING,
"n_gui_textarea_set_mask_char: widget %d is not a textarea", widget_id);
2493 n_log(
LOG_WARNING,
"n_gui_listbox_set_bitmaps: widget %d is not a listbox", widget_id);
2509 n_log(
LOG_WARNING,
"n_gui_radiolist_set_bitmaps: widget %d is not a radiolist", widget_id);
2525 n_log(
LOG_WARNING,
"n_gui_combobox_set_bitmaps: widget %d is not a combobox", widget_id);
2541 n_log(
LOG_WARNING,
"n_gui_dropmenu_set_bitmaps: widget %d is not a dropmenu", widget_id);
2556 n_log(
LOG_WARNING,
"n_gui_label_set_bitmap: widget %d is not a label", widget_id);
2569 float bw = (float)al_get_bitmap_width(bmp);
2570 float bh = (float)al_get_bitmap_height(bmp);
2572 al_draw_scaled_bitmap(bmp, 0, 0, bw, bh, dx, dy, dw, dh, 0);
2574 al_draw_bitmap(bmp, dx + (dw - bw) / 2.0f, dy + (dh - bh) / 2.0f, 0);
2579 float s = (sx < sy) ? sx : sy;
2582 al_draw_scaled_bitmap(bmp, 0, 0, bw, bh, dx + (dw - rw) / 2.0f, dy + (dh - rh) / 2.0f, rw, rh, 0);
2589static ALLEGRO_BITMAP*
_select_state_bitmap(
int state, ALLEGRO_BITMAP* normal_bmp, ALLEGRO_BITMAP* hover_bmp, ALLEGRO_BITMAP* active_bmp) {
2592 if (normal_bmp)
return normal_bmp;
2605 const ALLEGRO_TRANSFORM* tf = al_get_current_transform();
2609 cx[1] = (float)(wx + ww);
2612 cy[2] = (float)(wy + wh);
2613 cx[3] = (float)(wx + ww);
2614 cy[3] = (float)(wy + wh);
2615 for (
int i = 0; i < 4; i++)
2616 al_transform_coordinates(tf, &cx[i], &cy[i]);
2618 float min_x = cx[0], max_x = cx[0];
2619 float min_y = cy[0], max_y = cy[0];
2620 for (
int i = 1; i < 4; i++) {
2621 if (cx[i] < min_x) min_x = cx[i];
2622 if (cx[i] > max_x) max_x = cx[i];
2623 if (cy[i] < min_y) min_y = cy[i];
2624 if (cy[i] > max_y) max_y = cy[i];
2627 int new_x = (int)min_x;
2628 int new_y = (int)min_y;
2629 int new_w = (int)(max_x - min_x);
2630 int new_h = (int)(max_y - min_y);
2633 int pcx, pcy, pcw, pch;
2634 al_get_clipping_rectangle(&pcx, &pcy, &pcw, &pch);
2636 int right = new_x + new_w;
2637 int bottom = new_y + new_h;
2638 int prev_right = pcx + pcw;
2639 int prev_bottom = pcy + pch;
2641 if (new_x < pcx) new_x = pcx;
2642 if (new_y < pcy) new_y = pcy;
2643 if (right > prev_right) right = prev_right;
2644 if (bottom > prev_bottom) bottom = prev_bottom;
2646 new_w = right - new_x;
2647 new_h = bottom - new_y;
2648 if (new_w < 0) new_w = 0;
2649 if (new_h < 0) new_h = 0;
2651 al_set_clipping_rectangle(new_x, new_y, new_w, new_h);
2656static void _draw_text_truncated(ALLEGRO_FONT* font, ALLEGRO_COLOR color,
float x,
float y,
float max_w,
const char* text) {
2657 if (!font || !text || !text[0])
return;
2658 float tw =
_text_w(font, text);
2660 al_draw_text(font, color, x, y, 0, text);
2664 float ellipsis_w =
_text_w(font,
"...");
2665 float avail = max_w - ellipsis_w;
2666 if (avail < 0) avail = 0;
2667 size_t len = strlen(text);
2670 for (
size_t i = 0; i < len && i < (size_t)(
N_GUI_TEXT_MAX - 5); i++) {
2673 float cw =
_text_w(font, buf);
2674 if (cw > avail)
break;
2680 buf[fit + 3] =
'\0';
2681 al_draw_text(font, color, x, y, 0, buf);
2688static void _draw_text_justified(ALLEGRO_FONT* font, ALLEGRO_COLOR color,
float x,
float y,
float max_w,
float max_h,
const char* text) {
2689 if (!font || !text || !text[0])
return;
2696 float word_widths[256];
2698 char* tok = strtok(buf,
" \t");
2699 while (tok && nwords < 256) {
2700 words[nwords] = tok;
2701 word_widths[nwords] =
_text_w(font, tok);
2703 tok = strtok(NULL,
" \t");
2705 if (nwords == 0)
return;
2711 float fh = (float)al_get_font_line_height(font);
2712 float space_w =
_text_w(font,
" ");
2716 while (line_start < nwords) {
2718 if (cy + fh > y + max_h + 0.5f)
break;
2721 float line_words_w = word_widths[line_start];
2722 int line_end = line_start + 1;
2723 for (
int i = line_start + 1; i < nwords; i++) {
2724 float test_w = line_words_w + space_w + word_widths[i];
2725 if (test_w > max_w)
break;
2726 line_words_w = test_w;
2730 int words_on_line = line_end - line_start;
2731 int is_last_line = (line_end >= nwords);
2733 if (words_on_line == 1) {
2735 if (word_widths[line_start] > max_w) {
2738 al_draw_text(font, color, x, cy, 0, words[line_start]);
2740 }
else if (is_last_line) {
2743 for (
int i = line_start; i < line_end; i++) {
2744 al_draw_text(font, color, cx, cy, 0, words[i]);
2745 cx += word_widths[i] + space_w;
2749 float total_word_w = 0;
2750 for (
int i = line_start; i < line_end; i++) total_word_w += word_widths[i];
2751 float total_space = max_w - total_word_w;
2752 if (total_space < 0) total_space = 0;
2753 float gap = total_space / (float)(words_on_line - 1);
2755 for (
int i = line_start; i < line_end; i++) {
2756 al_draw_text(font, color, cx, cy, 0, words[i]);
2757 cx += word_widths[i] + gap;
2761 line_start = line_end;
2768 if (c < 0x80)
return 1;
2769 if ((c & 0xE0) == 0xC0)
return 2;
2770 if ((c & 0xF0) == 0xE0)
return 3;
2771 if ((c & 0xF8) == 0xF0)
return 4;
2778 if (!font || td->
text_len == 0)
return 0;
2779 float fh = (float)al_get_font_line_height(font);
2782 for (
size_t i = 0; i < td->
text_len;) {
2783 if (td->
text[i] ==
'\n') {
2792 memcpy(ch, &td->
text[i], (
size_t)clen);
2795 if (cx + cw > widget_w - pad * 2) {
2818 float view_h = w->
h - pad * 2;
2820 float max_sy = ch - view_h;
2821 if (max_sy < 0) max_sy = 0;
2858 float fh = (float)al_get_font_line_height(font);
2859 float view_h = w->
h - pad * 2;
2865 float text_area_w = w->
w - sb_size;
2871 size_t target = (byte_offset <= td->
text_len) ? byte_offset : td->
text_len;
2872 for (
size_t i = 0; i < target && i < td->
text_len;) {
2873 if (td->
text[i] ==
'\n') {
2882 memcpy(ch, &td->
text[i], (
size_t)clen);
2885 if (cx + cw > text_area_w - pad * 2) {
2895 float max_sy = content_h - view_h;
2896 if (max_sy < 0) max_sy = 0;
2897 float ideal = cy - view_h / 2 + fh / 2;
2898 if (ideal < 0) ideal = 0;
2899 if (ideal > max_sy) ideal = max_sy;
2920static int _justified_char_at_pos(
const char* text, ALLEGRO_FONT* font,
float max_w,
float text_x,
float text_y,
float scroll_y,
float click_mx,
float click_my) {
2921 if (!font || !text || !text[0])
return -1;
2924 size_t text_len = strlen(text);
2929 int word_starts[256];
2931 float word_widths[256];
2935 while (i < text_len && nwords < 256) {
2937 while (i < text_len && (text[i] ==
' ' || text[i] ==
'\t')) i++;
2938 if (i >= text_len)
break;
2940 while (i < text_len && text[i] !=
' ' && text[i] !=
'\t') i++;
2941 int wlen = (int)(i - ws);
2942 word_starts[nwords] = (int)ws;
2943 word_lens[nwords] = wlen;
2945 memcpy(tmp, &text[ws], (
size_t)wlen);
2947 word_widths[nwords] =
_text_w(font, tmp);
2950 if (nwords == 0)
return 0;
2952 float fh = (float)al_get_font_line_height(font);
2953 float space_w =
_text_w(font,
" ");
2954 float cy = text_y - scroll_y;
2955 float click_y_content = click_my;
2959 while (line_start < nwords) {
2960 float line_words_w = word_widths[line_start];
2961 int line_end = line_start + 1;
2962 for (
int j = line_start + 1; j < nwords; j++) {
2963 float test_w = line_words_w + space_w + word_widths[j];
2964 if (test_w > max_w)
break;
2965 line_words_w = test_w;
2969 int words_on_line = line_end - line_start;
2970 int is_last_line = (line_end >= nwords);
2972 if (click_y_content >= cy && click_y_content < cy + fh) {
2974 float click_x_rel = click_mx - text_x;
2975 if (click_x_rel < 0)
return word_starts[line_start];
2978 float gap = space_w;
2979 if (!is_last_line && words_on_line > 1) {
2980 float total_word_w = 0;
2981 for (
int j = line_start; j < line_end; j++) total_word_w += word_widths[j];
2982 float total_space = max_w - total_word_w;
2983 if (total_space < 0) total_space = 0;
2984 gap = total_space / (float)(words_on_line - 1);
2989 for (
int j = line_start; j < line_end; j++) {
2991 if (click_x_rel >= cx && click_x_rel < cx + word_widths[j]) {
2995 float best_dist = click_x_rel - cx;
2996 if (best_dist < 0) best_dist = -best_dist;
2997 for (
int k = 0; k < word_lens[j];) {
2998 int clen =
_utf8_char_len((
unsigned char)text[word_starts[j] + k]);
2999 if (k + clen > word_lens[j]) clen = word_lens[j] - k;
3001 memcpy(ch, &text[word_starts[j] + k], (
size_t)clen);
3005 float dist = (click_x_rel - cx) - wx;
3006 if (dist < 0) dist = -dist;
3007 if (dist < best_dist) {
3009 best_off = k + clen;
3013 return word_starts[j] + best_off;
3016 if (j < line_end - 1 && click_x_rel >= cx + word_widths[j] && click_x_rel < cx + word_widths[j] + gap) {
3018 float mid = cx + word_widths[j] + gap / 2.0f;
3019 if (click_x_rel < mid)
3020 return word_starts[j] + word_lens[j];
3022 return word_starts[j + 1];
3024 cx += word_widths[j] + gap;
3027 return word_starts[line_end - 1] + word_lens[line_end - 1];
3031 line_start = line_end;
3034 return (
int)text_len;
3039static void _draw_justified_selection(ALLEGRO_FONT* font,
float x,
float y,
float max_w,
float max_h,
const char* text,
int sel_start,
int sel_end, ALLEGRO_COLOR sel_color) {
3040 if (!font || !text || !text[0] || sel_start == sel_end)
return;
3041 int slo = sel_start < sel_end ? sel_start : sel_end;
3042 int shi = sel_start < sel_end ? sel_end : sel_start;
3043 size_t text_len = strlen(text);
3044 if ((
size_t)slo > text_len) slo = (int)text_len;
3045 if ((
size_t)shi > text_len) shi = (
int)text_len;
3048 int word_starts[256], word_lens[256];
3049 float word_widths[256];
3052 while (i < text_len && nwords < 256) {
3053 while (i < text_len && (text[i] ==
' ' || text[i] ==
'\t')) i++;
3054 if (i >= text_len)
break;
3056 while (i < text_len && text[i] !=
' ' && text[i] !=
'\t') i++;
3057 int wlen = (int)(i - ws);
3058 word_starts[nwords] = (int)ws;
3059 word_lens[nwords] = wlen;
3061 memcpy(tmp, &text[ws], (
size_t)wlen);
3063 word_widths[nwords] =
_text_w(font, tmp);
3066 if (nwords == 0)
return;
3068 float fh = (float)al_get_font_line_height(font);
3069 float space_w =
_text_w(font,
" ");
3073 while (line_start < nwords) {
3074 if (cy + fh > y + max_h + 0.5f)
break;
3075 float line_words_w = word_widths[line_start];
3076 int line_end = line_start + 1;
3077 for (
int j = line_start + 1; j < nwords; j++) {
3078 float test_w = line_words_w + space_w + word_widths[j];
3079 if (test_w > max_w)
break;
3080 line_words_w = test_w;
3083 int words_on_line = line_end - line_start;
3084 int is_last_line = (line_end >= nwords);
3087 float gap = space_w;
3088 if (!is_last_line && words_on_line > 1) {
3089 float total_word_w = 0;
3090 for (
int j = line_start; j < line_end; j++) total_word_w += word_widths[j];
3091 float total_space = max_w - total_word_w;
3092 if (total_space < 0) total_space = 0;
3093 gap = total_space / (float)(words_on_line - 1);
3098 for (
int j = line_start; j < line_end; j++) {
3099 int ws2 = word_starts[j];
3100 int we = ws2 + word_lens[j];
3102 if (slo < we && shi > ws2) {
3104 float sx1 = 0, sx2 = word_widths[j];
3107 int off = slo - ws2;
3108 memcpy(tmp2, &text[ws2], (
size_t)off);
3114 int off = shi - ws2;
3115 memcpy(tmp2, &text[ws2], (
size_t)off);
3119 al_draw_filled_rectangle(cx + sx1, cy, cx + sx2, cy + fh, sel_color);
3122 if (j < line_end - 1) {
3124 int gap_end = word_starts[j + 1];
3125 if (slo < gap_end && shi > gap_start) {
3126 al_draw_filled_rectangle(cx + word_widths[j], cy,
3127 cx + word_widths[j] + gap, cy + fh, sel_color);
3130 cx += word_widths[j] + gap;
3134 line_start = line_end;
3141 if (!font || !text || !text[0])
return 0;
3142 float fh = (float)al_get_font_line_height(font);
3143 float space_w =
_text_w(font,
" ");
3148 float word_widths[256];
3150 const char* tok = strtok(buf,
" \t");
3151 while (tok && nwords < 256) {
3152 word_widths[nwords] =
_text_w(font, tok);
3154 tok = strtok(NULL,
" \t");
3156 if (nwords == 0)
return 0;
3157 if (nwords == 1)
return fh;
3161 while (line_start < nwords) {
3162 float line_words_w = word_widths[line_start];
3163 int line_end = line_start + 1;
3164 for (
int i = line_start + 1; i < nwords; i++) {
3165 float test_w = line_words_w + space_w + word_widths[i];
3166 if (test_w > max_w)
break;
3167 line_words_w = test_w;
3171 line_start = line_end;
3173 return (
float)nlines * fh;
3182 float sb_x = area_x + area_w - sb_size;
3185 al_draw_filled_rectangle(sb_x, area_y, sb_x + sb_size, area_y + view_h,
3189 float ratio = view_h / content_h;
3190 if (ratio > 1.0f) ratio = 1.0f;
3191 float thumb_h = ratio * view_h;
3193 float max_scroll = content_h - view_h;
3194 float pos_ratio = (max_scroll > 0) ? scroll_y / max_scroll : 0;
3195 float thumb_y = area_y + pos_ratio * (view_h - thumb_h);
3208 const ALLEGRO_TRANSFORM* tf = al_get_current_transform();
3210 float sx = hypotf(tf->m[0][0], tf->m[0][1]);
3211 float sy = hypotf(tf->m[1][0], tf->m[1][1]);
3213 float scale = (sx > sy) ? sx : sy;
3214 if (scale < 0.01f) scale = 0.01f;
3216 float min_t = 1.0f / scale;
3217 return (requested > min_t) ? requested : min_t;
3247 float ht = thickness * 0.5f;
3249 al_draw_filled_rounded_rectangle(x + ht, y + ht, x + w - ht, y + h - ht, t->
corner_rx, t->
corner_ry, bg);
3250 al_draw_rounded_rectangle(x + ht, y + ht, x + w - ht, y + h - ht, t->
corner_rx, t->
corner_ry, bd, thickness);
3252 al_draw_filled_rectangle(x, y, x + w, y + h, bg);
3253 al_draw_rectangle(x + ht, y + ht, x + w - ht, y + h - ht, bd, thickness);
3260 float ax = ox + wgt->
x;
3261 float ay = oy + wgt->
y;
3262 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3265 int draw_state = wgt->
state;
3271 ALLEGRO_BITMAP* bmp = bd->
bitmap;
3276 al_draw_scaled_bitmap(bmp, 0, 0,
3277 (
float)al_get_bitmap_width(bmp), (
float)al_get_bitmap_height(bmp),
3278 ax, ay, wgt->
w, wgt->
h, 0);
3284 if (font && bd->
label[0]) {
3286 int bbx = 0, bby = 0, bbw = 0, bbh = 0;
3287 al_get_text_dimensions(font, bd->
label, &bbx, &bby, &bbw, &bbh);
3288 float tw = (float)bbw;
3289 float th = (float)bbh;
3291 float max_text_w = wgt->
w - pad * 2.0f;
3292 if (tw > max_text_w) {
3295 al_draw_text(font, tc, ax + (wgt->
w - tw) / 2.0f - (
float)bbx, ay + (wgt->
h - th) / 2.0f - (
float)bby, 0, bd->
label);
3303 float ax = ox + wgt->
x;
3304 float ay = oy + wgt->
y;
3305 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3308 double ratio = (range > 0) ? (sd->
value - sd->
min_val) / range : 0;
3313 float track_x = ax + (wgt->
w - track_w) / 2.0f;
3317 track_x, ay, track_w, wgt->
h, 0);
3324 float fill_h = (float)(ratio * (
double)wgt->
h);
3328 track_x, ay + wgt->
h - fill_h, track_w, fill_h, 0);
3334 float hy = ay + wgt->
h - fill_h;
3339 float hd = handle_r * 2.0f;
3340 al_draw_scaled_bitmap(hbmp, 0, 0,
3341 (
float)al_get_bitmap_width(hbmp), (
float)al_get_bitmap_height(hbmp),
3342 ax + wgt->
w / 2.0f - handle_r, hy - handle_r, hd, hd, 0);
3346 al_draw_filled_circle(ax + wgt->
w / 2.0f, hy, handle_r, hc);
3354 snprintf(val_str,
sizeof(val_str),
"%.0f%%", sd->
value);
3356 snprintf(val_str,
sizeof(val_str),
"%.1f", sd->
value);
3357 int vbbx = 0, vbby = 0, vbbw = 0, vbbh = 0;
3358 al_get_text_dimensions(font, val_str, &vbbx, &vbby, &vbbw, &vbbh);
3368 float track_y = ay + (wgt->
h - track_h) / 2.0f;
3372 ax, track_y, wgt->
w, track_h, 0);
3379 float fill_w = (float)(ratio * (
double)wgt->
w);
3383 ax, track_y, fill_w, track_h, 0);
3389 float hx = ax + fill_w;
3394 float hd = handle_r * 2.0f;
3395 al_draw_scaled_bitmap(hbmp, 0, 0,
3396 (
float)al_get_bitmap_width(hbmp), (
float)al_get_bitmap_height(hbmp),
3397 hx - handle_r, ay + wgt->
h / 2.0f - handle_r, hd, hd, 0);
3401 al_draw_filled_circle(hx, ay + wgt->
h / 2.0f, handle_r, hc);
3409 snprintf(val_str,
sizeof(val_str),
"%.0f%%", sd->
value);
3411 snprintf(val_str,
sizeof(val_str),
"%.1f", sd->
value);
3413 int sbbx = 0, sbby = 0, sbbw = 0, sbbh = 0;
3414 al_get_text_dimensions(font, val_str, &sbbx, &sbby, &sbbw, &sbbh);
3415 float th = (float)sbbh;
3424 if (!font || td->
text_len == 0)
return 0;
3425 float fh = (float)al_get_font_line_height(font);
3428 float click_x = mx - (ax + pad) + td->
scroll_x;
3429 if (click_x < 0) click_x = 0;
3442 float best_dist = click_x;
3444 for (
size_t ci = 0; ci < td->
text_len;) {
3447 size_t pos = ci + (size_t)clen;
3450 float tw =
_text_w(font, ctmp);
3451 float dist = click_x - tw;
3452 if (dist < 0) dist = -dist;
3453 if (dist < best_dist) {
3465 float view_h = widget_h - pad * 2;
3467 float sb_size = (content_h > view_h) ? scrollbar_size : 0.0f;
3468 float text_area_w = widget_w - sb_size;
3469 float avail_w = text_area_w - pad * 2;
3470 float click_x_rel = mx - (ax + pad);
3471 float click_y_content = my - (ay + pad) + (
float)td->
scroll_y;
3472 if (click_x_rel < 0) click_x_rel = 0;
3473 if (click_y_content < 0) click_y_content = 0;
3474 int target_line = (int)(click_y_content / fh);
3478 size_t best_pos = 0;
3479 float best_dist = click_x_rel;
3480 int found_line = (target_line == 0) ? 1 : 0;
3482 for (
size_t ci = 0; ci < td->
text_len;) {
3483 if (cur_line > target_line)
break;
3484 if (td->
text[ci] ==
'\n') {
3485 if (cur_line == target_line)
break;
3488 if (cur_line == target_line) {
3491 best_dist = click_x_rel;
3499 memcpy(ch2, &td->
text[ci], (
size_t)clen);
3501 float cw =
_text_w(font, ch2);
3502 if (cur_cx + cw > avail_w) {
3503 if (cur_line == target_line)
break;
3506 if (cur_line == target_line) {
3509 best_dist = click_x_rel;
3512 if (cur_line == target_line) {
3513 float dist = click_x_rel - (cur_cx + cw);
3514 if (dist < 0) dist = -dist;
3515 if (dist < best_dist) {
3517 best_pos = ci + (size_t)clen;
3523 if (!found_line) best_pos = td->
text_len;
3546 float ax = ox + wgt->
x;
3547 float ay = oy + wgt->
y;
3548 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3554 al_draw_scaled_bitmap(td->
bg_bitmap, 0, 0,
3555 (
float)al_get_bitmap_width(td->
bg_bitmap), (
float)al_get_bitmap_height(td->
bg_bitmap),
3556 ax, ay, wgt->
w, wgt->
h, 0);
3559 al_draw_filled_rectangle(ax, ay, ax + wgt->
w, ay + wgt->
h, bg);
3565 float fh = (float)al_get_font_line_height(font);
3569 int cursor_visible = 0;
3571 double now = al_get_time();
3574 if (period <= 0.0f) period = 1.0f;
3575 double phase = elapsed - (double)(
int)(elapsed / (double)period) * (double)period;
3576 cursor_visible = (phase < (double)period * 0.5) ? 1 : 0;
3581 float view_h = wgt->
h - pad * 2;
3585 int need_scrollbar = (content_h > view_h) ? 1 : 0;
3587 float text_area_w = wgt->
w - sb_size;
3588 if (need_scrollbar) {
3598 if (td->
text[i] ==
'\n') {
3607 memcpy(ch, &td->
text[i], (
size_t)clen);
3609 float cw2 =
_text_w(font, ch);
3610 if (cur_cx + cw2 > text_area_w - pad * 2) {
3618 if (cur_cy < (
float)td->
scroll_y) {
3621 if (cur_cy + fh > (
float)td->
scroll_y + view_h) {
3622 td->
scroll_y = (int)(cur_cy + fh - view_h);
3627 if (need_scrollbar) {
3628 float max_sy = content_h - view_h;
3629 if (max_sy < 0) max_sy = 0;
3637 int pcx, pcy, pcw, pch;
3638 al_get_clipping_rectangle(&pcx, &pcy, &pcw, &pch);
3641 size_t sel_lo = 0, sel_hi = 0;
3647 float cx = ax + pad;
3648 float cy = ay + pad - (float)td->
scroll_y;
3650 float cursor_cx = cx;
3651 float cursor_cy = cy;
3652 for (
size_t i = 0; i < td->
text_len;) {
3657 if (td->
text[i] ==
'\n') {
3659 if (has_sel && i >= sel_lo && i < sel_hi && cy + fh > ay && cy < ay + wgt->h) {
3660 float space_w =
_text_w(font,
" ");
3661 al_draw_filled_rectangle(cx, cy, cx + space_w, cy + fh, sel_bg);
3671 memcpy(ch, &td->
text[i], (
size_t)clen);
3674 if (cx + cw > ax + text_area_w - pad) {
3682 if (cy + fh > ay && cy < ay + wgt->h) {
3684 if (has_sel && i >= sel_lo && i < sel_hi) {
3685 al_draw_filled_rectangle(cx, cy, cx + cw, cy + fh, sel_bg);
3691 for (
int b = 1; b < clen; b++) {
3705 if (cursor_visible) {
3706 al_draw_filled_rectangle(cursor_cx, cursor_cy, cursor_cx + cursor_w, cursor_cy + fh, wgt->
theme.
text_active);
3708 al_set_clipping_rectangle(pcx, pcy, pcw, pch);
3711 if (need_scrollbar) {
3716 float ty = ay + (wgt->
h - fh) / 2.0f;
3717 float inner_w = wgt->
w - pad * 2;
3725 display_text[n] =
'\0';
3735 memcpy(tmp, display_text, cpos);
3737 float cursor_px =
_text_w(font, tmp);
3740 if (cursor_px - td->
scroll_x < 0) {
3743 if (cursor_px - td->
scroll_x > inner_w - cursor_w) {
3744 td->
scroll_x = cursor_px - inner_w + cursor_w;
3749 int pcx, pcy, pcw, pch;
3750 al_get_clipping_rectangle(&pcx, &pcy, &pcw, &pch);
3757 memcpy(stmp, display_text, sl);
3759 float sel_x1 =
_text_w(font, stmp);
3760 memcpy(stmp, display_text, sh);
3762 float sel_x2 =
_text_w(font, stmp);
3763 float sx1 = ax + pad + sel_x1 - td->
scroll_x;
3764 float sx2 = ax + pad + sel_x2 - td->
scroll_x;
3769 if (cursor_visible) {
3770 float cx = ax + pad + cursor_px - td->
scroll_x;
3771 al_draw_filled_rectangle(cx, ty, cx + cursor_w, ty + fh, wgt->
theme.
text_active);
3773 al_set_clipping_rectangle(pcx, pcy, pcw, pch);
3780 float ax = ox + wgt->
x;
3781 float ay = oy + wgt->
y;
3782 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3785 float box_y = ay + (wgt->
h - box_size) / 2.0f;
3788 ALLEGRO_BITMAP* box_bmp = NULL;
3798 al_draw_scaled_bitmap(box_bmp, 0, 0,
3799 (
float)al_get_bitmap_width(box_bmp), (
float)al_get_bitmap_height(box_bmp),
3800 ax, box_y, box_size, box_size, 0);
3808 al_draw_line(ax + m, box_y + box_size / 2.0f,
3810 al_draw_line(ax + box_size / 2.0f, box_y + box_size - m,
3815 if (font && cd->
label[0]) {
3816 int cbbx = 0, cbby = 0, cbbw = 0, cbbh = 0;
3817 al_get_text_dimensions(font, cd->
label, &cbbx, &cbby, &cbbw, &cbbh);
3818 float fh = (float)cbbh;
3828 float ax = ox + wgt->
x;
3829 float ay = oy + wgt->
y;
3836 ax, ay, wgt->
w, wgt->
h, 0);
3843 if (ratio > 1.0) ratio = 1.0;
3845 if (max_scroll < 0) max_scroll = 0;
3846 double pos_ratio = (max_scroll > 0) ? sb->
scroll_pos / max_scroll : 0;
3848 float thumb_x, thumb_y, thumb_w, thumb_h;
3850 thumb_h = (float)(ratio * (
double)wgt->
h);
3854 float track_range = wgt->
h - thumb_h;
3855 thumb_y = ay + (float)(pos_ratio * (
double)track_range);
3857 thumb_w = (float)(ratio * (
double)wgt->
w);
3861 float track_range = wgt->
w - thumb_w;
3862 thumb_x = ax + (float)(pos_ratio * (
double)track_range);
3867 al_draw_scaled_bitmap(tbmp, 0, 0,
3868 (
float)al_get_bitmap_width(tbmp), (
float)al_get_bitmap_height(tbmp),
3869 thumb_x, thumb_y, thumb_w, thumb_h, 0);
3878 float ax = ox + wgt->
x;
3879 float ay = oy + wgt->
y;
3880 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3884 al_draw_scaled_bitmap(ld->
bg_bitmap, 0, 0,
3885 (
float)al_get_bitmap_width(ld->
bg_bitmap), (
float)al_get_bitmap_height(ld->
bg_bitmap),
3886 ax, ay, wgt->
w, wgt->
h, 0);
3888 al_draw_filled_rectangle(ax, ay, ax + wgt->
w, ay + wgt->
h, wgt->
theme.
bg_normal);
3893 float fh = (float)al_get_font_line_height(font);
3895 int visible_count = (int)(wgt->
h / ih);
3899 int max_off = (int)ld->
nb_items - visible_count;
3900 if (max_off < 0) max_off = 0;
3904 int need_scrollbar = ((int)ld->
nb_items > visible_count) ? 1 : 0;
3906 float item_area_w = wgt->
w - sb_w;
3910 float iy = ay + (float)i * ih;
3917 }
else if (item->selected) {
3924 float item_max_w = item_area_w - pad * 2.0f;
3926 ax + pad, iy + (ih - fh) / 2.0f, item_max_w, item->text);
3930 if (need_scrollbar) {
3931 float sb_x = ax + wgt->
w - sb_w;
3933 al_draw_filled_rectangle(sb_x, ay, ax + wgt->
w, ay + wgt->
h, wgt->
theme.
bg_normal);
3936 float ratio = (float)visible_count / (
float)ld->
nb_items;
3937 float thumb_h = ratio * wgt->
h;
3939 float track_range = wgt->
h - thumb_h;
3940 float pos_ratio = (max_off > 0) ? (
float)ld->
scroll_offset / (float)max_off : 0;
3941 float thumb_y = ay + pos_ratio * track_range;
3943 al_draw_filled_rounded_rectangle(sb_x + thumb_pad, thumb_y, ax + wgt->
w - thumb_pad, thumb_y + thumb_h,
3951 float ax = ox + wgt->
x;
3952 float ay = oy + wgt->
y;
3953 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
3957 al_draw_scaled_bitmap(rd->
bg_bitmap, 0, 0,
3958 (
float)al_get_bitmap_width(rd->
bg_bitmap), (
float)al_get_bitmap_height(rd->
bg_bitmap),
3959 ax, ay, wgt->
w, wgt->
h, 0);
3961 al_draw_filled_rectangle(ax, ay, ax + wgt->
w, ay + wgt->
h, wgt->
theme.
bg_normal);
3966 float fh = (float)al_get_font_line_height(font);
3968 int visible_count = (int)(wgt->
h / ih);
3974 int max_off = (int)rd->
nb_items - visible_count;
3975 if (max_off < 0) max_off = 0;
3979 int need_scrollbar = ((int)rd->
nb_items > visible_count) ? 1 : 0;
3983 float iy = ay + (float)i * ih;
3984 float cy = iy + ih / 2.0f;
3985 float cx = ax + pad + radio_r;
3991 ax, iy, wgt->
w, ih, 0);
3995 ax, iy, wgt->
w, ih, 0);
4008 cx + radio_r + pad, iy + (ih - fh) / 2.0f, 0, rd->
items[idx].
text);
4012 if (need_scrollbar) {
4014 float sb_x = ax + wgt->
w - sb_w;
4015 al_draw_filled_rectangle(sb_x, ay, ax + wgt->
w, ay + wgt->
h, wgt->
theme.
bg_normal);
4017 float ratio = (float)visible_count / (
float)rd->
nb_items;
4018 float thumb_h = ratio * wgt->
h;
4020 float track_range = wgt->
h - thumb_h;
4021 float pos_ratio = (max_off > 0) ? (
float)rd->
scroll_offset / (float)max_off : 0;
4022 float thumb_y = ay + pos_ratio * track_range;
4024 al_draw_filled_rounded_rectangle(sb_x + thumb_pad, thumb_y, ax + wgt->
w - thumb_pad, thumb_y + thumb_h,
4032 float ax = ox + wgt->
x;
4033 float ay = oy + wgt->
y;
4034 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
4037 al_draw_scaled_bitmap(cd->
bg_bitmap, 0, 0,
4038 (
float)al_get_bitmap_width(cd->
bg_bitmap), (
float)al_get_bitmap_height(cd->
bg_bitmap),
4039 ax, ay, wgt->
w, wgt->
h, 0);
4049 const char* display_text =
"";
4053 int cbbbx = 0, cbbby = 0, cbbbw = 0, cbbbh = 0;
4054 al_get_text_dimensions(font, display_text[0] ? display_text :
"Ay", &cbbbx, &cbbby, &cbbbw, &cbbbh);
4055 float fh = (float)cbbbh;
4058 ax + pad, ay + (wgt->
h - fh) / 2.0f - (
float)cbbby, max_text_w, display_text);
4062 float arrow_y = ay + wgt->
h / 2.0f;
4072 if (!wgt || !wgt->
data)
return;
4077 float ox = 0, oy = 0;
4083 float ax = ox + wgt->
x;
4084 float ay = oy + wgt->
y + wgt->
h;
4085 float fh = (float)al_get_font_line_height(font);
4089 float dropdown_h = (float)vis * ih;
4093 float dropdown_w = wgt->
w;
4095 float max_item_w = 0;
4096 for (
size_t i = 0; i < cd->
nb_items; i++) {
4098 if (tw > max_item_w) max_item_w = tw;
4100 float needed = max_item_w + pad * 2;
4101 if (needed > dropdown_w) dropdown_w = needed;
4105 if (dropdown_w > cap) dropdown_w = cap;
4108 dropdown_w = (float)ctx->
display_w - ax;
4109 if (dropdown_w < wgt->w) dropdown_w = wgt->
w;
4116 if (sb_size < 10.0f) sb_size = 10.0f;
4117 float item_area_w = need_scrollbar ? dropdown_w - sb_size : dropdown_w;
4121 al_draw_scaled_bitmap(cd->
bg_bitmap, 0, 0,
4122 (
float)al_get_bitmap_width(cd->
bg_bitmap), (
float)al_get_bitmap_height(cd->
bg_bitmap),
4123 ax, ay, dropdown_w, dropdown_h, 0);
4125 al_draw_filled_rectangle(ax, ay, ax + dropdown_w, ay + dropdown_h, wgt->
theme.
bg_normal);
4130 int prev_cx = 0, prev_cy = 0, prev_cw = 0, prev_ch = 0;
4131 al_get_clipping_rectangle(&prev_cx, &prev_cy, &prev_cw, &prev_ch);
4132 al_set_clipping_rectangle((
int)ax, (
int)ay, (
int)dropdown_w, (
int)dropdown_h);
4135 float max_text_w = item_area_w - pad * 2;
4138 float iy = ay + (float)i * ih;
4140 ax, iy, item_area_w, ih);
4150 }
else if (hovered) {
4164 al_set_clipping_rectangle(prev_cx, prev_cy, prev_cw, prev_ch);
4167 if (need_scrollbar) {
4168 float sb_x = ax + dropdown_w - sb_size;
4170 al_draw_filled_rectangle(sb_x, ay, sb_x + sb_size, ay + dropdown_h,
4174 if (ratio > 1.0f) ratio = 1.0f;
4175 float thumb_h = ratio * dropdown_h;
4177 if (thumb_min < 12.0f) thumb_min = 12.0f;
4178 if (thumb_h < thumb_min) thumb_h = thumb_min;
4180 float pos_ratio = (max_offset > 0) ? (
float)cd->
scroll_offset / (float)max_offset : 0.0f;
4181 float thumb_y = ay + pos_ratio * (dropdown_h - thumb_h);
4184 al_draw_filled_rounded_rectangle(sb_x + thumb_pad, thumb_y,
4185 sb_x + sb_size - thumb_pad,
4195 float ax = ox + wgt->
x;
4196 float ay = oy + wgt->
y;
4197 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
4199 int draw_state = wgt->
state;
4204 if (font && dd->
label[0]) {
4206 int dbbx = 0, dbby = 0, dbbw = 0, dbbh = 0;
4207 al_get_text_dimensions(font, dd->
label, &dbbx, &dbby, &dbbw, &dbbh);
4208 float fh = (float)dbbh;
4216 float arrow_y = ay + wgt->
h / 2.0f;
4226 if (!wgt || !wgt->
data)
return;
4231 float ox = 0, oy = 0;
4237 float ax = ox + wgt->
x;
4238 float ay = oy + wgt->
y + wgt->
h;
4239 float fh = (float)al_get_font_line_height(font);
4243 float panel_h = (float)vis * ih;
4249 if (sb_size < 10.0f) sb_size = 10.0f;
4250 float item_area_w = need_scrollbar ? wgt->
w - sb_size : wgt->
w;
4256 ax, ay, wgt->
w, panel_h, 0);
4258 al_draw_filled_rectangle(ax, ay, ax + wgt->
w, ay + panel_h, wgt->
theme.
bg_normal);
4263 float max_text_w = item_area_w - pad * 2.0f;
4266 float iy = ay + (float)i * ih;
4268 ax, iy, item_area_w, ih);
4285 if (need_scrollbar) {
4286 float sb_x = ax + wgt->
w - sb_size;
4288 al_draw_filled_rectangle(sb_x, ay, sb_x + sb_size, ay + panel_h,
4292 if (ratio > 1.0f) ratio = 1.0f;
4293 float thumb_h = ratio * panel_h;
4295 if (thumb_min < 12.0f) thumb_min = 12.0f;
4296 if (thumb_h < thumb_min) thumb_h = thumb_min;
4298 float pos_ratio = (max_offset > 0) ? (
float)dd->
scroll_offset / (float)max_offset : 0.0f;
4299 float thumb_y = ay + pos_ratio * (panel_h - thumb_h);
4302 al_draw_filled_rounded_rectangle(sb_x + thumb_pad, thumb_y,
4303 sb_x + sb_size - thumb_pad,
4313 float ax = ox + wgt->
x;
4314 float ay = oy + wgt->
y;
4320 float bw = (float)al_get_bitmap_width(id->
bitmap);
4321 float bh = (float)al_get_bitmap_height(id->
bitmap);
4324 al_draw_scaled_bitmap(id->
bitmap, 0, 0, bw, bh, ax, ay, wgt->
w, wgt->
h, 0);
4326 float dx = ax + (wgt->
w - bw) / 2.0f;
4327 float dy = ay + (wgt->
h - bh) / 2.0f;
4328 al_draw_bitmap(id->
bitmap, dx, dy, 0);
4331 float scale_x = wgt->
w / bw;
4332 float scale_y = wgt->
h / bh;
4333 float scale = (scale_x < scale_y) ? scale_x : scale_y;
4334 float dw = bw * scale;
4335 float dh = bh * scale;
4336 float dx = ax + (wgt->
w - dw) / 2.0f;
4337 float dy = ay + (wgt->
h - dh) / 2.0f;
4338 al_draw_scaled_bitmap(id->
bitmap, 0, 0, bw, bh, dx, dy, dw, dh, 0);
4348 if (!font || !lb->
text[0])
return -1;
4349 if (click_x <= 0)
return 0;
4350 size_t len = strlen(lb->
text);
4353 float best_dist = click_x;
4354 for (
size_t i = 0; i < len;) {
4356 if (i + (
size_t)clen > len) clen = (int)(len - i);
4357 size_t pos = i + (size_t)clen;
4358 memcpy(tmp, lb->
text, pos);
4360 float tw =
_text_w(font, tmp);
4361 float dist = click_x - tw;
4362 if (dist < 0) dist = -dist;
4363 if (dist < best_dist) {
4367 if (tw > click_x)
break;
4377 float effective_w = wgt_w;
4379 float max_from_win = win_w - wgt_x;
4380 if (max_from_win > effective_w) effective_w = max_from_win;
4382 int lbbx = 0, lbby = 0, lbbw = 0, lbbh = 0;
4383 al_get_text_dimensions(font, lb->
text, &lbbx, &lbby, &lbbw, &lbbh);
4384 float tw = (float)lbbw;
4385 float max_text_w = effective_w - label_padding * 2;
4387 if (tw > max_text_w)
return ax + label_padding;
4388 return ax + (effective_w - tw) / 2.0f - (
float)lbbx;
4391 if (tw > max_text_w)
return ax + label_padding;
4392 return ax + effective_w - tw - label_padding - (float)lbbx;
4395 return ax + label_padding;
4400 float ax = ox + wgt->
x;
4401 float ay = oy + wgt->
y;
4402 ALLEGRO_FONT* font = wgt->
font ? wgt->
font : default_font;
4406 al_draw_scaled_bitmap(lb->
bg_bitmap, 0, 0,
4407 (
float)al_get_bitmap_width(lb->
bg_bitmap), (
float)al_get_bitmap_height(lb->
bg_bitmap),
4408 ax, ay, wgt->
w, wgt->
h, 0);
4411 if (!font || !lb->
text[0])
return;
4413 int lbbbx = 0, lbbby = 0, lbbbw = 0, lbbbh = 0;
4414 al_get_text_dimensions(font, lb->
text, &lbbbx, &lbbby, &lbbbw, &lbbbh);
4415 float fh = (float)lbbbh;
4418 float effective_w = wgt->
w;
4420 float max_from_win = win_w - wgt->
x;
4421 if (max_from_win > effective_w) effective_w = max_from_win;
4424 int is_link = (lb->
link[0] !=
'\0') ? 1 : 0;
4437 float tw = (float)lbbbw;
4443 float view_h = wgt->
h - lpad;
4445 int need_scrollbar = (content_h > view_h) ? 1 : 0;
4447 float text_w = max_text_w - sb_size;
4450 if (need_scrollbar) {
4451 float max_sy = content_h - view_h;
4452 if (max_sy < 0) max_sy = 0;
4460 int pcx, pcy, pcw, pch;
4461 al_get_clipping_rectangle(&pcx, &pcy, &pcw, &pch);
4464 float text_y = ay + lpad / 2.0f - lb->
scroll_y;
4475 float draw_w = tw < text_w ? tw : text_w;
4479 al_set_clipping_rectangle(pcx, pcy, pcw, pch);
4482 if (need_scrollbar) {
4488 float ty = ay + (wgt->
h - fh) / 2.0f - (
float)lbbby;
4491 if (tw > max_text_w) {
4495 tx = ax + (effective_w - tw) / 2.0f - (
float)lbbbx;
4496 al_draw_text(font, tc, tx, ty, 0, lb->
text);
4499 if (tw > max_text_w) {
4503 tx = ax + effective_w - tw - style->
label_padding - (float)lbbbx;
4504 al_draw_text(font, tc, tx, ty, 0, lb->
text);
4509 if (tw > max_text_w) {
4512 al_draw_text(font, tc, tx, ty, 0, lb->
text);
4520 size_t tlen = strlen(lb->
text);
4521 if ((
size_t)slo > tlen) slo = (int)tlen;
4522 if ((
size_t)shi > tlen) shi = (
int)tlen;
4524 memcpy(stmp, lb->
text, (
size_t)slo);
4526 float sx1 =
_text_w(font, stmp);
4527 memcpy(stmp, lb->
text, (
size_t)shi);
4529 float sx2 =
_text_w(font, stmp);
4535 float draw_w = tw < max_text_w ? tw : max_text_w;
4544 if (!wgt || !wgt->
visible)
return;
4547 int saved_state = wgt->
state;
4552 switch (wgt->
type) {
4581 _draw_label(wgt, ox, oy, default_font, win_w, style);
4592 wgt->
state = saved_state;
4593 float dx = ox + wgt->
x;
4594 float dy = oy + wgt->
y;
4595 al_draw_filled_rectangle(dx, dy, dx + wgt->
w, dy + wgt->
h,
4596 al_map_rgba(0, 0, 0, 120));
4604 ALLEGRO_FONT* font = win->
font ? win->
font : default_font;
4606 float tbh = frameless ? 0.0f : win->
titlebar_h;
4613 win->
x, win->
y, win->
w, tbh, 0);
4616 al_draw_filled_rounded_rectangle(win->
x, win->
y, win->
x + win->
w, win->
y + tbh,
4619 if (font && win->
title[0]) {
4620 int tbbx = 0, tbby = 0, tbbw = 0, tbbh = 0;
4621 al_get_text_dimensions(font, win->
title, &tbbx, &tbby, &tbbw, &tbbh);
4622 float fh = (float)tbbh;
4625 win->
x + style->
title_padding, win->
y + (tbh - fh) / 2.0f - (
float)tbby, max_title_w, win->
title);
4633 float body_y = win->
y + tbh;
4634 float body_h = win->
h - tbh;
4637 int need_vscroll = 0;
4638 int need_hscroll = 0;
4643 if (win->
content_h > body_h) need_vscroll = 1;
4644 if (win->
content_w > win->
w) need_hscroll = 1;
4646 if (need_vscroll && win->
content_w > (win->
w - scrollbar_size)) need_hscroll = 1;
4647 if (need_hscroll && win->
content_h > (body_h - scrollbar_size)) need_vscroll = 1;
4650 float content_area_w = win->
w - (need_vscroll ? scrollbar_size : 0);
4651 float content_area_h = body_h - (need_hscroll ? scrollbar_size : 0);
4655 float max_sy = win->
content_h - content_area_h;
4656 if (max_sy < 0) max_sy = 0;
4663 float max_sx = win->
content_w - content_area_w;
4664 if (max_sx < 0) max_sx = 0;
4675 al_draw_filled_rectangle(win->
x, body_y, win->
x + win->
w, win->
y + win->
h,
4678 al_draw_rounded_rectangle(win->
x, win->
y, win->
x + win->
w, win->
y + win->
h,
4687 int prev_cx, prev_cy, prev_cw, prev_ch;
4688 al_get_clipping_rectangle(&prev_cx, &prev_cy, &prev_cw, &prev_ch);
4697 float label_win_w = 0;
4701 label_win_w = win->
w;
4707 al_set_clipping_rectangle(prev_cx, prev_cy, prev_cw, prev_ch);
4711 float sb_x = win->
x + win->
w - scrollbar_size;
4712 float sb_y = body_y;
4713 float sb_h = content_area_h;
4718 al_draw_filled_rectangle(sb_x, sb_y, sb_x + scrollbar_size, sb_y + sb_h,
4722 float ratio = content_area_h / win->
content_h;
4723 if (ratio > 1.0f) ratio = 1.0f;
4724 float thumb_h = ratio * sb_h;
4726 float max_scroll = win->
content_h - content_area_h;
4727 float pos_ratio = (max_scroll > 0) ? win->
scroll_y / max_scroll : 0;
4728 float thumb_y = sb_y + pos_ratio * (sb_h - thumb_h);
4734 float sb_x = win->
x;
4735 float sb_y = win->
y + win->
h - scrollbar_size;
4736 float sb_w = content_area_w;
4741 al_draw_filled_rectangle(sb_x, sb_y, sb_x + sb_w, sb_y + scrollbar_size,
4745 float ratio = content_area_w / win->
content_w;
4746 if (ratio > 1.0f) ratio = 1.0f;
4747 float thumb_w = ratio * sb_w;
4749 float max_scroll = win->
content_w - content_area_w;
4750 float pos_ratio = (max_scroll > 0) ? win->
scroll_x / max_scroll : 0;
4751 float thumb_x = sb_x + pos_ratio * (sb_w - thumb_w);
4759 float rx = win->
x + win->
w;
4760 float ry = win->
y + win->
h;
4762 ALLEGRO_COLOR grip_color = style->
grip_color;
4765 al_draw_line(rx - grip_size, ry - 2, rx - 2, ry - grip_size, grip_color, grip_thick);
4766 al_draw_line(rx - grip_size + 4, ry - 2, rx - 2, ry - grip_size + 4, grip_color, grip_thick);
4767 al_draw_line(rx - grip_size + 8, ry - 2, rx - 2, ry - grip_size + 8, grip_color, grip_thick);
4770 al_draw_rounded_rectangle(win->
x, win->
y, win->
x + win->
w, win->
y + tbh,
4778 float max_r = 0, max_b = 0;
4782 float r = win->
x + win->
w;
4783 float b = win->
y + win->
h;
4784 if (r > max_r) max_r = r;
4785 if (b > max_b) max_b = b;
4808 int need_global_vscroll = 0;
4809 int need_global_hscroll = 0;
4816 if (eff_w > 0 && eff_h > 0) {
4817 if (ctx->
gui_bounds_h > eff_h) need_global_vscroll = 1;
4818 if (ctx->
gui_bounds_w > eff_w) need_global_hscroll = 1;
4820 if (need_global_vscroll && ctx->
gui_bounds_w > (eff_w - scrollbar_size)) need_global_hscroll = 1;
4821 if (need_global_hscroll && ctx->
gui_bounds_h > (eff_h - scrollbar_size)) need_global_vscroll = 1;
4824 float view_w = eff_w - (need_global_vscroll ? scrollbar_size : 0);
4825 float view_h = eff_h - (need_global_hscroll ? scrollbar_size : 0);
4826 if (need_global_vscroll) {
4828 if (max_sy < 0) max_sy = 0;
4834 if (need_global_hscroll) {
4836 if (max_sx < 0) max_sx = 0;
4845 ALLEGRO_TRANSFORM global_tf, prev_tf;
4846 al_copy_transform(&prev_tf, al_get_current_transform());
4847 al_identity_transform(&global_tf);
4853 al_compose_transform(&global_tf, &prev_tf);
4854 al_use_transform(&global_tf);
4857 int prev_cx, prev_cy, prev_cw, prev_ch;
4859 if (need_global_vscroll || need_global_hscroll) {
4860 al_get_clipping_rectangle(&prev_cx, &prev_cy, &prev_cw, &prev_ch);
4862 float clip_vw = eff_w - (need_global_vscroll ? scrollbar_size : 0);
4863 float clip_vh = eff_h - (need_global_hscroll ? scrollbar_size : 0);
4869 al_set_clipping_rectangle(0, 0, (
int)clip_vw, (
int)clip_vh);
4892 al_set_clipping_rectangle(prev_cx, prev_cy, prev_cw, prev_ch);
4897 if (need_global_vscroll || need_global_hscroll) {
4898 ALLEGRO_TRANSFORM sb_tf;
4899 al_identity_transform(&sb_tf);
4904 al_compose_transform(&sb_tf, &prev_tf);
4905 al_use_transform(&sb_tf);
4908 if (need_global_vscroll) {
4909 float sb_x = eff_w - scrollbar_size;
4911 float sb_h = eff_h - (need_global_hscroll ? scrollbar_size : 0);
4912 float view_h = sb_h;
4915 al_draw_filled_rectangle(sb_x, sb_y, sb_x + scrollbar_size, sb_y + sb_h,
4920 if (ratio > 1.0f) ratio = 1.0f;
4921 float thumb_h = ratio * sb_h;
4924 float pos_ratio = (max_scroll > 0) ? ctx->
global_scroll_y / max_scroll : 0;
4925 float thumb_y = sb_y + pos_ratio * (sb_h - thumb_h);
4933 if (need_global_hscroll) {
4935 float sb_y = eff_h - scrollbar_size;
4936 float sb_w = eff_w - (need_global_vscroll ? scrollbar_size : 0);
4937 float view_w = sb_w;
4940 al_draw_filled_rectangle(sb_x, sb_y, sb_x + sb_w, sb_y + scrollbar_size,
4945 if (ratio > 1.0f) ratio = 1.0f;
4946 float thumb_w = ratio * sb_w;
4949 float pos_ratio = (max_scroll > 0) ? ctx->
global_scroll_x / max_scroll : 0;
4950 float thumb_x = sb_x + pos_ratio * (sb_w - thumb_w);
4959 al_use_transform(&prev_tf);
4976 if ((w > 0) != (h > 0)) {
4977 n_log(
LOG_ERR,
"n_gui_set_virtual_size: both dimensions must be > 0 (got w=%.1f, h=%.1f); virtual canvas not changed", w, h);
5125 if (new_w <= 0 || new_h <= 0)
return;
5202 if (scale < 0.25f) scale = 0.25f;
5203 if (scale > 8.0f) scale = 8.0f;
5243 int logical_w = al_get_display_width(
display);
5244 int logical_h = al_get_display_height(
display);
5245 ALLEGRO_BITMAP* backbuffer = al_get_backbuffer(
display);
5246 if (backbuffer && logical_w > 0 && logical_h > 0) {
5247 int physical_w = al_get_bitmap_width(backbuffer);
5248 int physical_h = al_get_bitmap_height(backbuffer);
5249 float scale_x = (float)physical_w / (
float)logical_w;
5250 float scale_y = (float)physical_h / (
float)logical_h;
5251 scale = (scale_x > scale_y) ? scale_x : scale_y;
5252 if (scale < 0.5f) scale = 1.0f;
5255#ifdef ALLEGRO_ANDROID
5258 int dpi = al_get_display_option(
display, ALLEGRO_DEFAULT_DISPLAY_ADAPTER);
5262 float android_scale = (float)dpi / 160.0f;
5263 if (android_scale >= 0.5f) scale = android_scale;
5267 if (scale < 0.25f) scale = 0.25f;
5268 if (scale > 8.0f) scale = 8.0f;
5280 if (wgt->
w <= 0)
return;
5281 if (wgt->
h <= 0)
return;
5285 float ay = win_content_y + wgt->
y;
5286 float local_y = my - ay;
5288 ratio = 1.0 - (double)local_y / (
double)wgt->
h;
5290 float ax = win_x + wgt->
x;
5291 float local_x = mx - ax;
5292 ratio = (double)local_x / (
double)wgt->
w;
5294 if (ratio < 0.0) ratio = 0.0;
5295 if (ratio > 1.0) ratio = 1.0;
5301 if (new_val != sd->
value) {
5302 sd->
value = new_val;
5312 float ax = win_x + wgt->
x;
5313 float ay = win_content_y + wgt->
y;
5316 if (ratio_viewport > 1.0) ratio_viewport = 1.0;
5318 if (max_scroll < 0) max_scroll = 0;
5322 float thumb_h = (float)(ratio_viewport * (
double)wgt->
h);
5324 float track_range = wgt->
h - thumb_h;
5325 if (track_range <= 0)
return;
5326 pos_ratio = (double)(my - ay - thumb_h / 2.0f) / (double)track_range;
5328 float thumb_w = (float)(ratio_viewport * (
double)wgt->
w);
5330 float track_range = wgt->
w - thumb_w;
5331 if (track_range <= 0)
return;
5332 pos_ratio = (double)(mx - ax - thumb_w / 2.0f) / (double)track_range;
5334 if (pos_ratio < 0.0) pos_ratio = 0.0;
5335 if (pos_ratio > 1.0) pos_ratio = 1.0;
5349 out[0] = (char)(0xC0 | (cp >> 6));
5350 out[1] = (char)(0x80 | (cp & 0x3F));
5354 out[0] = (char)(0xE0 | (cp >> 12));
5355 out[1] = (char)(0x80 | ((cp >> 6) & 0x3F));
5356 out[2] = (char)(0x80 | (cp & 0x3F));
5359 if (cp < 0x110000) {
5360 out[0] = (char)(0xF0 | (cp >> 18));
5361 out[1] = (char)(0x80 | ((cp >> 12) & 0x3F));
5362 out[2] = (char)(0x80 | ((cp >> 6) & 0x3F));
5363 out[3] = (char)(0x80 | (cp & 0x3F));
5381 size_t del_len = hi - lo;
5394 size_t len = hi - lo;
5396 Malloc(tmp,
char, len + 1);
5398 memcpy(tmp, &td->
text[lo], len);
5400 al_set_clipboard_text(
display, tmp);
5416 int shift = (ev->keyboard.modifiers & ALLEGRO_KEYMOD_SHIFT) ? 1 : 0;
5417 int ctrl = (ev->keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) ? 1 : 0;
5420 if (ctrl && ev->keyboard.keycode == ALLEGRO_KEY_A) {
5428 if (ctrl && ev->keyboard.keycode == ALLEGRO_KEY_C) {
5436 if (ctrl && ev->keyboard.keycode == ALLEGRO_KEY_X) {
5445 if (ctrl && ev->keyboard.keycode == ALLEGRO_KEY_V) {
5447 char* clip = al_get_clipboard_text(ctx->
display);
5453 size_t clip_len = strlen(clip);
5458 for (
size_t ci = 0; ci < clip_len && flen <
N_GUI_TEXT_MAX - 1; ci++) {
5459 if (clip[ci] ==
'\r') {
5461 }
else if (clip[ci] ==
'\n') {
5462 if (td->
multiline) filtered[flen++] =
'\n';
5463 }
else if ((
unsigned char)clip[ci] >= 32 || ((
unsigned char)clip[ci] & 0xC0) == 0x80) {
5464 filtered[flen++] = clip[ci];
5465 }
else if (((
unsigned char)clip[ci] & 0xC0) == 0xC0) {
5466 filtered[flen++] = clip[ci];
5469 filtered[flen] =
'\0';
5470 if (flen > 0 && td->
text_len + flen <= td->char_limit) {
5484 if (ev->keyboard.keycode == ALLEGRO_KEY_BACKSPACE) {
5493 }
while (erase_start > 0 && cont_count <= 3 && ((
unsigned char)td->
text[erase_start] & 0xC0) == 0x80);
5494 size_t erase_len = td->
cursor_pos - erase_start;
5503 if (ev->keyboard.keycode == ALLEGRO_KEY_DELETE) {
5516 if (ev->keyboard.keycode == ALLEGRO_KEY_LEFT) {
5538 if (ev->keyboard.keycode == ALLEGRO_KEY_RIGHT) {
5559 if (ev->keyboard.keycode == ALLEGRO_KEY_HOME) {
5570 if (ev->keyboard.keycode == ALLEGRO_KEY_END) {
5583 if (ev->keyboard.keycode == ALLEGRO_KEY_UP || ev->keyboard.keycode == ALLEGRO_KEY_DOWN) {
5591 float base_wrap_w = wgt->
w - pad * 2;
5592 float text_area_h = wgt->
h - pad * 2;
5594 float wrap_w = (content_h > text_area_h)
5595 ? (wgt->
w - sb_size - pad * 2)
5601 int cursor_line = 0;
5604 for (
size_t i = 0; i < td->
text_len;) {
5609 if (td->
text[i] ==
'\n') {
5618 memcpy(ch, &td->
text[i], (
size_t)clen);
5621 if (cx + cw > wrap_w) {
5636 int total_lines = line;
5640 if (ev->keyboard.keycode == ALLEGRO_KEY_UP) {
5641 if (cursor_line <= 0) {
5645 target_line = cursor_line - 1;
5647 if (cursor_line >= total_lines) {
5651 target_line = cursor_line + 1;
5657 size_t best_pos = 0;
5658 float best_dist = 1e9f;
5661 if (line == target_line) {
5662 best_dist = (cursor_x >= 0) ? cursor_x : -cursor_x;
5666 for (
size_t i = 0; i < td->
text_len;) {
5667 if (td->
text[i] ==
'\n') {
5668 if (line >= target_line)
break;
5671 if (line == target_line) {
5672 float dist = (cursor_x >= cx) ? (cursor_x - cx) : (cx - cursor_x);
5673 if (dist < best_dist) {
5684 memcpy(ch, &td->
text[i], (
size_t)clen);
5687 if (cx + cw > wrap_w) {
5688 if (line >= target_line)
break;
5691 if (line == target_line) {
5692 float dist = (cursor_x >= cx) ? (cursor_x - cx) : (cx - cursor_x);
5693 if (dist < best_dist) {
5701 if (line == target_line) {
5702 float dist = (cursor_x >= cx) ? (cursor_x - cx) : (cx - cursor_x);
5703 if (dist < best_dist) {
5705 best_pos = i + (size_t)clen;
5720 if (ev->keyboard.keycode == ALLEGRO_KEY_ENTER) {
5739 if (ev->keyboard.unichar >= 32) {
5745 int utf8_len =
_utf8_encode(ev->keyboard.unichar, utf8);
5746 if (utf8_len > 0 && td->
text_len + (
size_t)utf8_len <= td->char_limit) {
5770 int event_consumed = 0;
5779 if (event.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT) {
5792 if (event.type == ALLEGRO_EVENT_MOUSE_AXES) {
5796 if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN && event.mouse.button == 1) {
5802 if (event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP && event.mouse.button == 1) {
5810 (event.type == ALLEGRO_EVENT_MOUSE_AXES || event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)) {
5812 if (drag_wgt && drag_wgt->
data) {
5813 float d_ox = 0, d_oy = 0;
5817 float d_fh = d_font ? (float)al_get_font_line_height(d_font) : 16.0f;
5818 float d_my = (float)ctx->
mouse_y;
5819 switch (drag_wgt->
type) {
5824 float d_ay = d_oy + drag_wgt->
y + drag_wgt->
h;
5834 float d_ay = d_oy + drag_wgt->
y + drag_wgt->
h;
5843 float d_ay = d_oy + drag_wgt->
y;
5844 int visible = (int)(drag_wgt->
h / ih);
5851 float d_ay = d_oy + drag_wgt->
y;
5852 int visible = (int)(drag_wgt->
h / ih);
5860 float ta_view_h = drag_wgt->
h - ta_pad * 2;
5862 float ta_text_w = drag_wgt->
w - ta_sb_w;
5864 float d_ay = d_oy + drag_wgt->
y + ta_pad;
5866 d_my, d_ay, ta_view_h,
5867 ta_view_h, ta_content_h,
5881 float screen_mx = (float)ctx->
mouse_x;
5882 float screen_my = (float)ctx->
mouse_y;
5884 float virtual_mx = screen_mx;
5885 float virtual_my = screen_my;
5894 event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)
5898 event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP)
5909 if (ctx->
mouse_b1 && (event.type == ALLEGRO_EVENT_MOUSE_AXES || event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN)) {
5913 int need_hscroll = (ctx->
gui_bounds_w > eff_w) ? 1 : 0;
5914 float sb_h = eff_h - (need_hscroll ? scrollbar_size : 0);
5915 float view_h = sb_h;
5917 if (ratio > 1.0f) ratio = 1.0f;
5918 float thumb_h = ratio * sb_h;
5921 float track_range = sb_h - thumb_h;
5922 if (track_range > 0 && max_scroll > 0) {
5923 float pos_ratio = (virtual_my - thumb_h / 2.0f) / track_range;
5924 if (pos_ratio < 0) pos_ratio = 0;
5925 if (pos_ratio > 1) pos_ratio = 1;
5930 int need_vscroll = (ctx->
gui_bounds_h > eff_h) ? 1 : 0;
5931 float sb_w = eff_w - (need_vscroll ? scrollbar_size : 0);
5932 float view_w = sb_w;
5934 if (ratio > 1.0f) ratio = 1.0f;
5935 float thumb_w = ratio * sb_w;
5938 float track_range = sb_w - thumb_w;
5939 if (track_range > 0 && max_scroll > 0) {
5940 float pos_ratio = (virtual_mx - thumb_w / 2.0f) / track_range;
5941 if (pos_ratio < 0) pos_ratio = 0;
5942 if (pos_ratio > 1) pos_ratio = 1;
5948 if (just_released) {
5956 if (just_pressed && eff_w > 0 && eff_h > 0) {
5959 int need_vscroll = (ctx->
gui_bounds_h > eff_h) ? 1 : 0;
5960 int need_hscroll = (ctx->
gui_bounds_w > eff_w) ? 1 : 0;
5961 if (need_vscroll && ctx->
gui_bounds_w > (eff_w - scrollbar_size)) need_hscroll = 1;
5962 if (need_hscroll && ctx->
gui_bounds_h > (eff_h - scrollbar_size)) need_vscroll = 1;
5965 float sb_x = eff_w - scrollbar_size;
5966 float sb_h = eff_h - (need_hscroll ? scrollbar_size : 0);
5967 if (
_point_in_rect(virtual_mx, virtual_my, sb_x, 0, scrollbar_size, sb_h)) {
5973 float sb_y = eff_h - scrollbar_size;
5974 float sb_w = eff_w - (need_vscroll ? scrollbar_size : 0);
5975 if (
_point_in_rect(virtual_mx, virtual_my, 0, sb_y, sb_w, scrollbar_size)) {
5985 if (cb_wgt && cb_wgt->
data) {
5989 float cb_ox = 0, cb_oy = 0;
5993 float cb_fh = cb_font ? (float)al_get_font_line_height(cb_font) : 16.0f;
5995 float cb_ax = cb_ox + cb_wgt->
x;
5996 float cb_ay = cb_oy + cb_wgt->
y + cb_wgt->
h;
6000 float cb_dd_h = (float)cb_vis * cb_ih;
6003 float cb_dd_w = cb_wgt->
w;
6005 float cb_max_tw = 0;
6006 for (
size_t ci = 0; ci < cbd->
nb_items; ci++) {
6008 if (tw > cb_max_tw) cb_max_tw = tw;
6010 float cb_needed = cb_max_tw + cb_pad * 2;
6011 if (cb_needed > cb_dd_w) cb_dd_w = cb_needed;
6014 if (cb_dd_w > cb_cap) cb_dd_w = cb_cap;
6016 cb_dd_w = (float)ctx->
display_w - cb_ax;
6017 if (cb_dd_w < cb_wgt->w) cb_dd_w = cb_wgt->
w;
6025 if (cb_sb_size < 10.0f) cb_sb_size = 10.0f;
6026 float cb_item_w = cb_need_sb ? cb_dd_w - cb_sb_size : cb_dd_w;
6028 if (cb_need_sb && mx >= cb_ax + cb_item_w) {
6036 int clicked_idx = cbd->
scroll_offset + (int)((my - cb_ay) / cb_ih);
6037 if (clicked_idx >= 0 && (
size_t)clicked_idx < cbd->
nb_items) {
6058 if (dm_wgt && dm_wgt->
data) {
6062 float dm_ox = 0, dm_oy = 0;
6066 float dm_fh = dm_font ? (float)al_get_font_line_height(dm_font) : 16.0f;
6068 float dm_ax = dm_ox + dm_wgt->
x;
6069 float dm_ay = dm_oy + dm_wgt->
y + dm_wgt->
h;
6072 float dm_panel_h = (float)dm_vis * dm_ih;
6078 if (dm_sb_size < 10.0f) dm_sb_size = 10.0f;
6079 float dm_item_w = dm_need_sb ? dm_wgt->
w - dm_sb_size : dm_wgt->
w;
6081 if (dm_need_sb && mx >= dm_ax + dm_item_w) {
6089 int clicked_idx = dmd->
scroll_offset + (int)((my - dm_ay) / dm_ih);
6090 if (clicked_idx >= 0 && (
size_t)clicked_idx < dmd->
nb_entries) {
6104 float dm_btn_ax = dm_ox + dm_wgt->
x;
6105 float dm_btn_ay = dm_oy + dm_wgt->
y;
6118 if (event.type == ALLEGRO_EVENT_MOUSE_AXES ||
6119 event.type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN ||
6120 event.type == ALLEGRO_EVENT_MOUSE_BUTTON_UP) {
6127 if (wgt) wgt->
state &= ~N_GUI_STATE_HOVER;
6137 captured_wnode = wnode;
6162 if (!captured_wnode) {
6174 if (prev_fw) prev_fw->
state &= ~N_GUI_STATE_FOCUSED;
6180 int auto_scrollbar_hit = 0;
6185 int need_vscroll = 0, need_hscroll = 0;
6187 if (win->
content_h > body_h) need_vscroll = 1;
6188 if (win->
content_w > win->
w) need_hscroll = 1;
6189 if (need_vscroll && win->
content_w > (win->
w - scrollbar_size)) need_hscroll = 1;
6190 if (need_hscroll && win->
content_h > (body_h - scrollbar_size)) need_vscroll = 1;
6191 float content_area_w = win->
w - (need_vscroll ? scrollbar_size : 0);
6192 float content_area_h = body_h - (need_hscroll ? scrollbar_size : 0);
6196 float sb_x = win->
x + win->
w - scrollbar_size;
6197 float sb_y = body_y;
6198 float sb_h = content_area_h;
6203 auto_scrollbar_hit = 1;
6208 if (need_hscroll && !auto_scrollbar_hit) {
6209 float sb_x = win->
x;
6210 float sb_y = win->
y + win->
h - scrollbar_size;
6211 float sb_w = content_area_w;
6216 auto_scrollbar_hit = 1;
6225 float rx = win->
x + win->
w - grip;
6226 float ry = win->
y + win->
h - grip;
6228 int in_resize_area = 0;
6232 int need_vscroll = 0, need_hscroll = 0;
6234 if (win->
content_h > body_h) need_vscroll = 1;
6235 if (win->
content_w > win->
w) need_hscroll = 1;
6236 if (need_vscroll && win->
content_w > (win->
w - scrollbar_size)) need_hscroll = 1;
6237 if (need_hscroll && win->
content_h > (body_h - scrollbar_size)) need_vscroll = 1;
6238 if (need_vscroll || need_hscroll) {
6240 float corner_x = win->
x + win->
w - scrollbar_size;
6241 float corner_y = win->
y + win->
h - scrollbar_size;
6242 in_resize_area = just_pressed &&
_point_in_rect(mx, my, corner_x, corner_y, scrollbar_size, scrollbar_size);
6244 in_resize_area = just_pressed &&
_point_in_rect(mx, my, rx, ry, grip, grip);
6247 in_resize_area = just_pressed &&
_point_in_rect(mx, my, rx, ry, grip, grip);
6249 if (in_resize_area) {
6267 float content_x = win->
x - win->
scroll_x;
6273 float ax = content_x + wgt->
x;
6274 float ay = content_y + wgt->
y;
6328 float ta_view_h = wgt->
h - text_pad * 2;
6330 int ta_need_sb = (ta_content_h > ta_view_h) ? 1 : 0;
6332 if (ta_need_sb && mx > ax + wgt->
w - ta_sb_w) {
6334 float ta_text_w = wgt->
w - ta_sb_w;
6337 my, ay + text_pad, ta_view_h,
6338 ta_view_h, ta_content_h,
6341 goto textarea_click_done;
6345 ax, ay, wgt->
w, wgt->
h,
6351 textarea_click_done:;
6371 float effective_w = wgt->
w;
6378 float mfw = lww - wgt->
x;
6379 if (mfw > effective_w) effective_w = mfw;
6381 float max_text_w = effective_w - lpad * 2;
6383 float view_h = wgt->
h - lpad;
6385 float tw2 = max_text_w - sb_sz;
6387 ax + lpad, ay + lpad / 2.0f, click_lb->
scroll_y, mx, my);
6395 float click_x = mx - text_ox;
6398 if (char_pos >= 0) {
6415 if (target < 0) target = 0;
6417 if (target > max_off) target = max_off;
6429 float lfh = lf ? (float)al_get_font_line_height(lf) : 16.0f;
6431 int lb_visible = (int)(wgt->
h / lih);
6432 int lb_need_sb = ((int)lbd->
nb_items > lb_visible) ? 1 : 0;
6435 int lb_max_off = (int)lbd->
nb_items - lb_visible;
6436 if (lb_max_off < 0) lb_max_off = 0;
6440 if (lb_need_sb && mx > ax + wgt->
w - lb_sb_w) {
6445 int clicked_idx = lbd->
scroll_offset + (int)((my - ay) / lih);
6446 if (clicked_idx >= 0 && (
size_t)clicked_idx < lbd->
nb_items) {
6462 float rfh = rf ? (float)al_get_font_line_height(rf) : 16.0f;
6464 int rl_visible = (int)(wgt->
h / rih);
6465 int rl_need_sb = ((int)rld->
nb_items > rl_visible) ? 1 : 0;
6468 int rl_max_off = (int)rld->
nb_items - rl_visible;
6469 if (rl_max_off < 0) rl_max_off = 0;
6473 if (rl_need_sb && mx > ax + wgt->
w - rl_sb_w) {
6478 int clicked_idx = rld->
scroll_offset + (int)((my - ay) / rih);
6479 if (clicked_idx >= 0 && (
size_t)clicked_idx < rld->
nb_items) {
6537 win->
state &= ~N_GUI_WIN_DRAGGING;
6545 float new_w = (mx - win->
x) + win->
drag_ox;
6546 float new_h = (my - win->
y) + win->
drag_oy;
6547 if (new_w < win->min_w) new_w = win->
min_w;
6548 if (new_h < win->min_h) new_h = win->
min_h;
6553 win->
state &= ~N_GUI_WIN_RESIZING;
6564 int need_vscroll = 0, need_hscroll = 0;
6566 if (win->
content_h > body_h) need_vscroll = 1;
6567 if (win->
content_w > win->
w) need_hscroll = 1;
6568 if (need_vscroll && win->
content_w > (win->
w - scrollbar_size)) need_hscroll = 1;
6569 if (need_hscroll && win->
content_h > (body_h - scrollbar_size)) need_vscroll = 1;
6570 float content_area_w = win->
w - (need_vscroll ? scrollbar_size : 0);
6571 float content_area_h = body_h - (need_hscroll ? scrollbar_size : 0);
6574 float sb_y = body_y;
6575 float sb_h = content_area_h;
6580 float sb_x = win->
x;
6581 float sb_w = content_area_w;
6594 if (fw) fw->
state &= ~N_GUI_STATE_FOCUSED;
6601 if (ctx->
mouse_b1 && event.type == ALLEGRO_EVENT_MOUSE_AXES) {
6605 float content_x = win->
x - win->
scroll_x;
6622 float lax = content_x + wgt->
x;
6623 float lay = content_y + wgt->
y;
6627 float effective_w = wgt->
w;
6634 float mfw = lww - wgt->
x;
6635 if (mfw > effective_w) effective_w = mfw;
6637 float max_text_w = effective_w - lpad * 2;
6639 float view_h = wgt->
h - lpad;
6641 float tw2 = max_text_w - sb_sz;
6643 lax + lpad, lay + lpad / 2.0f, drag_lb->
scroll_y, mx, my);
6651 float click_x = mx - text_ox;
6654 if (char_pos >= 0) {
6663 if (tf && drag_td) {
6664 float ta_ax = content_x + wgt->
x;
6665 float ta_ay = content_y + wgt->
y;
6668 ta_ax, ta_ay, wgt->
w, wgt->
h,
6681 if (just_released) {
6690 float content_x = win->
x - win->
scroll_x;
6692 float ax = content_x + wgt->
x;
6693 float ay = content_y + wgt->
y;
6708 wgt->
state &= ~N_GUI_STATE_ACTIVE;
6717 if (!event_consumed && event.type == ALLEGRO_EVENT_KEY_CHAR && event.keyboard.keycode == ALLEGRO_KEY_TAB) {
6718 int ctrl = (
event.keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) ? 1 : 0;
6719 int shift = (
event.keyboard.modifiers & ALLEGRO_KEYMOD_SHIFT) ? 1 : 0;
6722 int is_ctrl_tab_in_textarea = 0;
6726 is_ctrl_tab_in_textarea = 1;
6729 if (!is_ctrl_tab_in_textarea) {
6748 int focusable_ids[512];
6753 focusable_ids[nfocusable++] = w->
id;
6756 if (nfocusable > 0) {
6759 for (
int i = 0; i < nfocusable; i++) {
6767 next_idx = (cur_idx <= 0) ? nfocusable - 1 : cur_idx - 1;
6769 next_idx = (cur_idx < 0 || cur_idx >= nfocusable - 1) ? 0 : cur_idx + 1;
6779 if (!event_consumed && event.type == ALLEGRO_EVENT_KEY_CHAR && ctx->
focused_widget_id != -1) {
6792 if (!event_consumed && event.type == ALLEGRO_EVENT_KEY_CHAR && ctx->
focused_widget_id >= 0) {
6795 int kc =
event.keyboard.keycode;
6801 if (step <= 0) step = 1.0;
6802 double new_val = sd->
value;
6806 if (kc == ALLEGRO_KEY_RIGHT) {
6809 }
else if (kc == ALLEGRO_KEY_LEFT) {
6814 if (kc == ALLEGRO_KEY_UP) {
6817 }
else if (kc == ALLEGRO_KEY_DOWN) {
6822 if (kc == ALLEGRO_KEY_HOME) {
6825 }
else if (kc == ALLEGRO_KEY_END) {
6835 if (new_val != sd->
value) {
6836 sd->
value = new_val;
6851 for (
size_t i = 0; i < ld->
nb_items; i++) {
6858 int new_sel = cur_sel;
6859 if (kc == ALLEGRO_KEY_UP && cur_sel > 0) {
6860 new_sel = cur_sel - 1;
6862 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < (
int)ld->
nb_items - 1) {
6863 new_sel = cur_sel + 1;
6865 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < 0) {
6868 }
else if (kc == ALLEGRO_KEY_HOME) {
6871 }
else if (kc == ALLEGRO_KEY_END) {
6882 float lfh = lf ? (float)al_get_font_line_height(lf) : 16.0f;
6884 int visible = (int)(fw->
h / lih);
6885 if (new_sel < ld->scroll_offset) ld->
scroll_offset = new_sel;
6897 int new_sel = cur_sel;
6899 if (kc == ALLEGRO_KEY_UP && cur_sel > 0) {
6900 new_sel = cur_sel - 1;
6902 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < (
int)rd->
nb_items - 1) {
6903 new_sel = cur_sel + 1;
6905 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < 0) {
6908 }
else if (kc == ALLEGRO_KEY_HOME) {
6911 }
else if (kc == ALLEGRO_KEY_END) {
6916 if (handled && new_sel != cur_sel) {
6921 float rfh = rf ? (float)al_get_font_line_height(rf) : 16.0f;
6923 int visible = (int)(fw->
h / rih);
6924 if (new_sel < rd->scroll_offset) rd->
scroll_offset = new_sel;
6936 int new_sel = cur_sel;
6938 if (kc == ALLEGRO_KEY_UP && cur_sel > 0) {
6939 new_sel = cur_sel - 1;
6941 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < (
int)cd->
nb_items - 1) {
6942 new_sel = cur_sel + 1;
6944 }
else if (kc == ALLEGRO_KEY_DOWN && cur_sel < 0) {
6947 }
else if (kc == ALLEGRO_KEY_HOME) {
6950 }
else if (kc == ALLEGRO_KEY_END) {
6955 if (handled && new_sel != cur_sel) {
6967 if (max_scroll > 0) {
6968 double step = max_scroll / 20.0;
6969 if (step <= 0) step = 1.0;
6973 if (kc == ALLEGRO_KEY_UP) {
6976 }
else if (kc == ALLEGRO_KEY_DOWN) {
6981 if (kc == ALLEGRO_KEY_LEFT) {
6984 }
else if (kc == ALLEGRO_KEY_RIGHT) {
6989 if (kc == ALLEGRO_KEY_HOME) {
6992 }
else if (kc == ALLEGRO_KEY_END) {
6993 new_pos = max_scroll;
6998 if (new_pos < 0) new_pos = 0;
6999 if (new_pos > max_scroll) new_pos = max_scroll;
7011 if (kc == ALLEGRO_KEY_SPACE || kc == ALLEGRO_KEY_ENTER) {
7022 if (!event_consumed && event.type == ALLEGRO_EVENT_KEY_DOWN &&
7023 (event.keyboard.modifiers & ALLEGRO_KEYMOD_CTRL) &&
7024 event.keyboard.keycode == ALLEGRO_KEY_C && ctx->
display &&
7032 size_t tlen = strlen(lb->
text);
7033 if ((
size_t)slo > tlen) slo = (int)tlen;
7034 if ((
size_t)shi > tlen) shi = (
int)tlen;
7035 int len = shi - slo;
7038 memcpy(clip, &lb->
text[slo], (
size_t)len);
7040 al_set_clipboard_text(ctx->
display, clip);
7055 if (event.type == ALLEGRO_EVENT_KEY_DOWN && !event_consumed) {
7056 int kc =
event.keyboard.keycode;
7060 for (
LIST_NODE* wnode = ctx->
windows->
end; wnode && !event_consumed; wnode = wnode->prev) {
7103 if (!event_consumed) {
7104 int widget_focused = 0;
7111 if (ftd && (ftd->
multiline || kc != ALLEGRO_KEY_ENTER)) {
7119 if (!widget_focused) {
7122 for (
LIST_NODE* wnode = ctx->
windows->
end; wnode && !event_consumed; wnode = wnode->prev) {
7155 if (event.type == ALLEGRO_EVENT_MOUSE_AXES && event.mouse.dz != 0) {
7156 int scroll_consumed = 0;
7161 if (cb_wgt && cb_wgt->
data) {
7168 scroll_consumed = 1;
7175 if (dm_wgt && dm_wgt->
data) {
7181 if (max_off < 0) max_off = 0;
7183 scroll_consumed = 1;
7187 if (scroll_consumed)
return 1;
7193 float win_h = win->
h;
7196 float content_x = win->
x;
7197 float content_y = win->
y +
_win_tbh(win);
7203 float ax = content_x + wgt->
x - win->
scroll_x;
7204 float ay = content_y + wgt->
y - win->
scroll_y;
7212 float lfh = lf ? (float)al_get_font_line_height(lf) : 16.0f;
7214 int visible_count = (int)(wgt->
h / lih);
7215 int max_off = (int)ld->
nb_items - visible_count;
7216 if (max_off < 0) max_off = 0;
7218 scroll_consumed = 1;
7225 float rfh = rf ? (float)al_get_font_line_height(rf) : 16.0f;
7227 int visible_count = (int)(wgt->
h / rih);
7228 int max_off = (int)rd->
nb_items - visible_count;
7229 if (max_off < 0) max_off = 0;
7231 scroll_consumed = 1;
7237 std->
scroll_y -= (int)((
float)
event.mouse.dz * scroll_step);
7240 scroll_consumed = 1;
7246 if (step <= 0) step = 1.0;
7247 double new_val = sd->
value + (double)event.mouse.dz * step;
7252 if (new_val != sd->
value) {
7253 sd->
value = new_val;
7258 scroll_consumed = 1;
7263 if (max_scroll > 0) {
7264 double step = max_scroll / 20.0;
7265 if (step <= 0) step = 1.0;
7266 sbd->
scroll_pos -= (double)event.mouse.dz * step;
7272 scroll_consumed = 1;
7281 int max_off = (int)cbd->
nb_items - max_vis;
7282 if (max_off < 0) max_off = 0;
7284 scroll_consumed = 1;
7293 int max_off = (int)dmd->
nb_entries - max_vis;
7294 if (max_off < 0) max_off = 0;
7296 scroll_consumed = 1;
7303 sld->
scroll_y -= (float)event.mouse.dz * scroll_step;
7305 scroll_consumed = 1;
7314 win->
scroll_y -= (float)event.mouse.dz * scroll_step;
7316 scroll_consumed = 1;
7318 if (scroll_consumed) event_consumed = 1;
7324 if (!scroll_consumed && eff_w > 0 && eff_h > 0) {
7335 return event_consumed;
7346 float mx = (float)ctx->
mouse_x;
7347 float my = (float)ctx->
mouse_y;
7372 unsigned char r, g, b, a;
7373 al_unmap_rgba(c, &r, &g, &b, &a);
7374 cJSON* arr = cJSON_CreateArray();
7375 cJSON_AddItemToArray(arr, cJSON_CreateNumber(r));
7376 cJSON_AddItemToArray(arr, cJSON_CreateNumber(g));
7377 cJSON_AddItemToArray(arr, cJSON_CreateNumber(b));
7378 cJSON_AddItemToArray(arr, cJSON_CreateNumber(a));
7379 cJSON_AddItemToObject(parent, name, arr);
7383static ALLEGRO_COLOR
_json_get_color(cJSON* parent,
const char* name, ALLEGRO_COLOR fallback) {
7384 cJSON* arr = cJSON_GetObjectItemCaseSensitive(parent, name);
7385 if (!arr || !cJSON_IsArray(arr) || cJSON_GetArraySize(arr) < 4)
return fallback;
7386 int r = (int)cJSON_GetArrayItem(arr, 0)->valuedouble;
7387 int g = (int)cJSON_GetArrayItem(arr, 1)->valuedouble;
7388 int b = (int)cJSON_GetArrayItem(arr, 2)->valuedouble;
7389 int a = (int)cJSON_GetArrayItem(arr, 3)->valuedouble;
7390 return al_map_rgba((
unsigned char)r, (
unsigned char)g, (
unsigned char)b, (
unsigned char)a);
7395 cJSON* item = cJSON_GetObjectItemCaseSensitive(parent, name);
7396 if (!item || !cJSON_IsNumber(item))
return fallback;
7397 return (
float)item->valuedouble;
7402 cJSON* item = cJSON_GetObjectItemCaseSensitive(parent, name);
7403 if (!item || !cJSON_IsNumber(item))
return fallback;
7404 return (
int)item->valuedouble;
7417 cJSON* root = cJSON_CreateObject();
7418 if (!root)
return -1;
7421 cJSON* theme = cJSON_CreateObject();
7434 cJSON_AddNumberToObject(theme,
"corner_rx", t->
corner_rx);
7435 cJSON_AddNumberToObject(theme,
"corner_ry", t->
corner_ry);
7436 cJSON_AddItemToObject(root,
"theme", theme);
7439 cJSON* sty = cJSON_CreateObject();
7441 cJSON_AddNumberToObject(sty,
"titlebar_h", s->
titlebar_h);
7442 cJSON_AddNumberToObject(sty,
"min_win_w", s->
min_win_w);
7443 cJSON_AddNumberToObject(sty,
"min_win_h", s->
min_win_h);
7444 cJSON_AddNumberToObject(sty,
"title_padding", s->
title_padding);
7447 cJSON_AddNumberToObject(sty,
"scrollbar_size", s->
scrollbar_size);
7463 cJSON_AddNumberToObject(sty,
"grip_size", s->
grip_size);
7504 cJSON_AddNumberToObject(sty,
"label_padding", s->
label_padding);
7509 cJSON_AddNumberToObject(sty,
"scroll_step", s->
scroll_step);
7512 cJSON_AddItemToObject(root,
"style", sty);
7515 char* json_str = cJSON_Print(root);
7517 if (!json_str)
return -1;
7519 FILE* fp = fopen(filepath,
"w");
7521 cJSON_free(json_str);
7524 fprintf(fp,
"%s\n", json_str);
7526 cJSON_free(json_str);
7540 FILE* fp = fopen(filepath,
"r");
7543 fseek(fp, 0, SEEK_END);
7544 long fsize = ftell(fp);
7545 fseek(fp, 0, SEEK_SET);
7546 if (fsize <= 0 || fsize > 1024 * 1024) {
7552 Malloc(buf,
char, (
size_t)fsize + 1);
7557 size_t nread = fread(buf, 1, (
size_t)fsize, fp);
7561 cJSON* root = cJSON_Parse(buf);
7563 if (!root)
return -1;
7566 cJSON* theme = cJSON_GetObjectItemCaseSensitive(root,
"theme");
7589 if (wgt) wgt->
theme = *t;
7595 cJSON* sty = cJSON_GetObjectItemCaseSensitive(root,
"style");
7686 n_log(
LOG_ERR,
"n_gui_save_theme_json: cJSON not available (compile with -DHAVE_CJSON)");
7699 n_log(
LOG_ERR,
"n_gui_load_theme_json: cJSON not available (compile with -DHAVE_CJSON)");
7709 for (
int i = 0; i < panel->
nb_tabs; i++) {
7715 if (clicked < 0)
return;
7721 if (!ctx)
return NULL;
7724 if (!panel)
return NULL;
7745 float bx = panel->
x + (float)idx * panel->
button_w;
7750 if (btn_id < 0)
return -1;
7758 if (!panel || tab_index < 0 || tab_index >= panel->
nb_tabs)
return;
7763 if (!panel || index < 0 || index >= panel->
nb_tabs)
return;
7764 for (
int i = 0; i < panel->
nb_tabs; i++) {
7781 if (!panel || !*panel)
return;
7791 if (!tree || !selected)
return;
7792 if (index < 0 || index >= tree->
nb_visible)
return;
7794 if (node_idx < 0 || node_idx >= tree->
nb_nodes)
return;
7800 if (!ctx)
return NULL;
7803 if (!tree)
return NULL;
7829 if (parent_index >= tree->
nb_nodes)
return -1;
7832 snprintf(node->
label,
sizeof(node->
label),
"%s", label);
7837 if (parent_index < 0) {
7849 if (!tree || node_index < 0 || node_index >= tree->
nb_nodes)
return;
7859 for (
int i = 0; i < tree->
nb_nodes; i++) {
7864 int pad = node->
depth * 2;
7865 if (pad >= (
int)
sizeof(indent) - 1) pad = (int)
sizeof(indent) - 2;
7866 memset(indent,
' ', (
size_t)pad);
7879 if (!tree || !*tree)
return;
7896 for (
int i = 0; i < table->
nb_rows; i++) {
7907 for (
int i = 0; i < table->
nb_rows; i++) {
7925 if (add_w) add_w->
y = cy;
7929 if (!ctx)
return NULL;
7932 if (!table)
return NULL;
7933 memset(table, 0,
sizeof(*table));
7944 float hdr_h = 18.0f;
7948 float total_w = wptr ? (wptr->
w - padding * 2.0f) : 400.0f;
7950 float col_w = total_w * 0.27f;
7951 float key_w = col_w, val_w = col_w, desc_w = col_w;
7952 float chk_w = row_height - 4.0f;
7961 px + key_w + val_w + desc_w + gap, padding, chk_w, hdr_h,
N_GUI_ALIGN_LEFT);
7966 "+", px, btn_y, 30, row_height - 4.0f,
7975 const char* description,
7985 float total_w = wptr ? (wptr->
w - table->
padding * 2.0f) : 400.0f;
7987 float col_w = total_w * 0.27f;
7988 float key_w = col_w, val_w = col_w, desc_w = col_w, chk_w = rh, rem_w = rh;
8011 if (!table || row_index < 0 || row_index >= table->
nb_rows)
return;
8029 if (!table || !*table)
return;
ALLEGRO_DISPLAY * display
void on_link_click(int widget_id, const char *link, void *user_data)
void on_scroll(int widget_id, double pos, void *user_data)
#define FreeNoLog(__ptr)
Free Handler without log.
#define Malloc(__ptr, __struct, __size)
Malloc Handler to get errors and set to 0.
#define __n_assert(__ptr, __ret)
macro to assert things
#define Free(__ptr)
Free Handler to get errors.
char title[128]
window title
ALLEGRO_BITMAP * thumb_bitmap
optional bitmap for the thumb/handle, normal state (NULL = color theme)
float scroll_y
vertical scroll offset for overflowing text (pixels)
float scroll_y
vertical scroll offset for auto-scrollbar (pixels)
int visible
visibility flag
ALLEGRO_BITMAP * panel_bitmap
optional bitmap for the dropdown panel background (NULL = color theme)
ALLEGRO_BITMAP * handle_hover_bitmap
optional bitmap for the handle on hover (NULL = fallback to handle_bitmap)
float button_h
height of each tab button
int is_open
is dropdown currently open
int content_window_ids[16]
content window IDs (-1 = none)
int w
bounding box width in pixels
int open_combobox_id
id of the combobox whose dropdown is currently open, or -1
ALLEGRO_COLOR scrollbar_track_color
scrollbar track colour
int max_visible
max visible items in dropdown
int selected_index
currently selected index (-1 = none)
void(* on_remove)(int, void *)
callback on row removal
size_t items_capacity
allocated capacity
ALLEGRO_COLOR grip_color
grip line colour
float dropdown_border_thickness
dropdown panel border thickness
char * text
text content (dynamically allocated, text_alloc bytes)
int nb_rows
total rows (including removed)
float slider_handle_edge_offset
handle circle offset from edge
int parent_index
parent node index, or -1 for root
int shape
shape type: N_GUI_SHAPE_RECT or N_GUI_SHAPE_ROUNDED
float content_h
total content height computed from widgets (internal)
float global_scrollbar_thumb_padding
global thumb inset from track edge
char label[128]
menu label (shown on the button)
size_t items_capacity
allocated capacity
float x
position x on screen
float scrollbar_size
scrollbar track width/height
float norm_y
normalized position y (0.0–1.0 fraction of reference display height)
void(* on_select)(int widget_id, int index, void *user_data)
callback on selection change (widget_id, selected_index, user_data)
float scrollbar_thumb_corner_r
thumb corner radius
int is_dynamic
is this entry dynamic (rebuilt via callback each time menu opens)
float title_padding
padding left of title text
float min_win_h
minimum window height (for resize)
float dropdown_arrow_half_w
horizontal half-extent of the arrow chevron
int toggled
toggle state: 0 = off/unclicked, 1 = on/clicked (only used when toggle_mode=1)
int lbl_enabled
label widget ID for "On" header
ALLEGRO_BITMAP * item_bg_bitmap
optional bitmap for per-item background, normal state (NULL = color theme)
int scroll_offset
scroll offset in items
float ref_display_h
reference display height at the time normalized values were last captured
float slider_track_corner_r
track corner radius
int y
bounding box y offset from draw origin
float titlebar_h
title bar height
ALLEGRO_BITMAP * handle_active_bitmap
optional bitmap for the handle while dragging (NULL = fallback to handle_bitmap)
void(* on_tab_change)(int, void *)
callback on tab switch (may be NULL)
float norm_y
normalized position y (fraction of parent window h, used for SCALE resize)
int dropmenu_max_visible
default max visible items for dropmenu panel
float norm_x
normalized position x (0.0–1.0 fraction of reference display width)
float textarea_padding
inner padding
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the label background (NULL = no background)
float item_height
item height
N_GUI_THEME theme
color theme for this widget
float title_max_w_reserve
pixels reserved right of title for truncation
char text[128]
item display text
int nb_tabs
number of tabs
int key_sources[8]
widget IDs that can trigger this focused key binding.
float checkbox_label_gap
gap between box and label text
int open_dropmenu_id
id of the dropdown menu whose panel is currently open, or -1
float global_scrollbar_thumb_corner_r
global thumb corner radius
N_GUI_THEME default_theme
default theme (applied to new widgets/windows)
float dpi_scale
DPI scale factor (1.0 = normal, 1.25 = 125%, 2.0 = HiDPI, etc.)
void * user_data
user data for callback
int scroll_from_wheel
set to 1 when scroll was changed by mouse wheel, cleared on key input
int btn_add
button widget ID for "+" add row
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the window body background (NULL = color fill)
float scrollbar_thumb_padding
thumb inset from track edge
int visible_map[512]
visible row to node index
ALLEGRO_COLOR text_normal
text normal
int remove_id
button widget ID for the remove action
int combobox_max_visible
default max visible items for combobox dropdown
ALLEGRO_BITMAP * item_bg_bitmap
optional bitmap for per-item background, normal state (NULL = color theme)
float norm_h
normalized height (fraction of parent window h)
void * user_data
user data for callback
void(* on_open)(int widget_id, void *user_data)
callback to rebuild dynamic entries each time the menu is opened.
int mouse_b1_prev
previous mouse button 1 state
int listbox_id
listbox widget ID
float corner_rx
corner radius for rounded shapes
ALLEGRO_COLOR selection_color
text selection highlight colour (semi-transparent recommended)
ALLEGRO_BITMAP * bitmap
optional bitmap for the button (NULL = color theme)
int flags
bitmask of N_GUI_COMBOBOX_* flags
float gui_offset_x
horizontal letterbox offset for virtual canvas
int key_focus_only
if 1, keycode fires only when the button itself or a source widget has focus.
N_GUI_KV_ROW rows[128]
row storage
int type
widget type (N_GUI_TYPE_*)
int state
current state flags (N_GUI_STATE_*)
float scroll_step
pixels per mouse wheel notch for window auto-scroll
int multiline
0 = single line, 1 = multiline
int key_id
textarea widget ID for the key
ALLEGRO_BITMAP * bitmap_hover
optional bitmap for hover state
int button_ids[16]
toggle button widget IDs
int max_visible
max visible items
float gui_bounds_w
total bounding box width of all windows (computed internally)
float x
position x relative to parent window
float global_scrollbar_size
global scrollbar track width/height
float textarea_cursor_width
cursor width in pixels
char text[4096]
text to display
void * user_data
user data for callback
float norm_h
normalized height (fraction of reference display height, used in SCALE mode)
float min_win_w
minimum window width (for resize)
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the combobox background (NULL = color theme)
void(* on_change)(int widget_id, double value, void *user_data)
callback on value change
float item_height
item height in pixels
float drag_ox
drag offset x (internal)
void(* on_click)(int widget_id, void *user_data)
callback on click (widget_id passed)
float row_height
height of each row in pixels
int expanded
1 if expanded
N_GUI_LISTITEM * items
dynamic array of items
char label[128]
display text
N_GUI_THEME theme
color theme for this window chrome
int desc_id
textarea widget ID for the description
int next_window_id
next window id to assign
float item_selection_inset
item highlight inset from left/right edges
ALLEGRO_COLOR border_normal
border normal
void(* on_click)(int widget_id, int entry_index, int tag, void *user_data)
callback when this entry is clicked (widget_id, entry_index, tag, user_data)
int has_children
1 if node has children
void * user_data
user data for callback
int scroll_offset
scroll offset
ALLEGRO_COLOR text_hover
text hover
float dropdown_arrow_half_h
vertical half-extent of the arrow chevron
int z_value
z-value for N_GUI_ZORDER_FIXED mode (lower = behind, higher = on top)
ALLEGRO_BITMAP * item_bg_bitmap
optional bitmap for per-item background, normal state (NULL = color theme)
double cursor_time
timestamp of last cursor activity (for blink reset)
size_t entries_capacity
allocated capacity
int mouse_y
mouse state tracking
float grip_line_thickness
grip line thickness
int lbl_key
label widget ID for "Key" header
size_t sel_end
selection end position (tracks cursor during selection)
int resize_policy
per-window resize policy: N_GUI_WIN_RESIZE_NONE, _MOVE, or _SCALE
size_t nb_items
number of items
void * user_data
user data for callback
float padding
horizontal padding
void(* on_select)(int, void *)
selection callback
int shape
shape type: N_GUI_SHAPE_RECT, N_GUI_SHAPE_ROUNDED, N_GUI_SHAPE_BITMAP
int z_order
z-order mode (N_GUI_ZORDER_NORMAL, _ALWAYS_ON_TOP, _ALWAYS_BEHIND, _FIXED)
int tag
user-defined tag for the entry (e.g.
int mouse_b1
mouse button 1 state
float button_w
width of each tab button
float display_w
display/viewport width (set via n_gui_set_display_size)
N_GUI_LISTITEM * items
dynamic array of items
ALLEGRO_BITMAP * box_hover_bitmap
optional bitmap for the checkbox square on hover (NULL = fallback to box_bitmap/box_checked_bitmap)
int h
bounding box height in pixels
float grip_size
grip area size
float radio_label_gap
gap between circle and label text
N_GUI_CTX * ctx
GUI context.
ALLEGRO_DISPLAY * display
display pointer for clipboard operations (set via n_gui_set_display)
float norm_w
normalized width (fraction of reference display width, used in SCALE mode)
float content_w
total content width computed from widgets (internal)
void * user_data
user data for this node
float norm_x
normalized position x (fraction of parent window w, used for SCALE resize)
ALLEGRO_COLOR global_scrollbar_thumb_color
global scrollbar thumb colour
int selected_label_id
id of the label widget with the most recent text selection, or -1
float radio_inner_offset
inner filled circle shrink from outer
int selected_index
currently selected index (-1 = none)
ALLEGRO_BITMAP * track_bitmap
optional bitmap for the track/rail background (NULL = color theme)
int mouse_x
mouse state tracking
ALLEGRO_COLOR scrollbar_thumb_color
scrollbar thumb colour
int active_tab
currently active tab index
float checkbox_max_size
maximum box size
size_t nb_entries
number of entries
float checkbox_mark_thickness
checkmark line thickness
int sel_start
selection start byte offset (-1 = no selection)
ALLEGRO_BITMAP * bitmap
bitmap to display (not owned, not freed)
ALLEGRO_BITMAP * fill_bitmap
optional bitmap for the filled portion of the track (NULL = color theme)
float header_height
height of the column header row
float global_scroll_step
pixels per mouse wheel notch for global scroll
float item_height
item height in dropdown
void * user_data
user data for callback
double scroll_pos
current scroll position
void(* on_change)(int widget_id, const char *text, void *user_data)
callback on text change
ALLEGRO_BITMAP * item_selected_bitmap
optional bitmap for per-item background, selected/highlighted (NULL = color theme)
void * user_data
user data for callback
float autofit_origin_y
original insertion point y for N_GUI_AUTOFIT_CENTER (set by n_gui_window_set_autofit)
int scroll_offset
scroll offset in items
int selected
selection state (for listbox)
ALLEGRO_BITMAP * item_hover_bitmap
optional bitmap for the hovered entry background (NULL = color theme)
float virtual_h
virtual canvas height (0 = disabled / identity transform)
N_GUI_CTX * ctx
GUI context.
float global_scroll_x
global horizontal scroll offset (when GUI exceeds display)
float checkbox_mark_margin
checkmark inset from box edge
ALLEGRO_BITMAP * track_bitmap
optional bitmap for the scrollbar track background (NULL = color theme)
float checkbox_label_offset
horizontal offset from box edge to label text
ALLEGRO_BITMAP * titlebar_bitmap
optional bitmap for the titlebar background (NULL = color fill)
int scroll_offset
scroll offset in dropdown
N_GUI_CTX * ctx
GUI context.
void * data
widget-specific data (union via void pointer)
double value
current value (always snapped to step)
N_GUI_DROPMENU_ENTRY * entries
dynamic array of entries
float drag_oy
drag offset y (internal)
float slider_track_border_thickness
track outline thickness
int nb_active
active row count
ALLEGRO_COLOR link_color_normal
link colour (normal)
float slider_handle_min_r
minimum handle circle radius
int scale_mode
scale mode: N_GUI_IMAGE_FIT, N_GUI_IMAGE_STRETCH, N_GUI_IMAGE_CENTER
ALLEGRO_BITMAP * item_selected_bitmap
optional bitmap for per-item background, selected/highlighted (NULL = color theme)
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the radiolist background (NULL = color theme)
N_GUI_TREE_NODE nodes[512]
node storage
ALLEGRO_BITMAP * box_bitmap
optional bitmap for the checkbox square, unchecked state (NULL = color theme)
void * on_open_user_data
user data for on_open callback
float y
y origin of tab button row
float dropdown_arrow_reserve
horizontal space reserved for the arrow on the right
float y
position y relative to parent window
int lbl_desc
label widget ID for "Description" header
int orientation
orientation: N_GUI_SLIDER_H or N_GUI_SLIDER_V
float global_scrollbar_border_thickness
global thumb border thickness
int state
state flags (N_GUI_WIN_*)
int flags
feature flags (N_GUI_WIN_AUTO_SCROLLBAR, N_GUI_WIN_RESIZABLE, etc.)
void * user_data
user data for callback
int active
1 if active, 0 if removed
double max_val
maximum value
HASH_TABLE * widgets_by_id
hash table for fast widget lookup by id
float border_thickness
border thickness
float link_underline_thickness
link underline thickness
ALLEGRO_BITMAP * box_checked_bitmap
optional bitmap for the checkbox square, checked state (NULL = color theme)
int autofit_flags
bitmask of N_GUI_AUTOFIT_* flags (0 = no auto-fitting)
N_GUI_STYLE style
configurable style (sizes, colours, paddings)
ALLEGRO_FONT * font
font for the title bar
void * user_data
user data for callback
ALLEGRO_COLOR text_active
text active
float ref_display_w
reference display width at the time normalized values were last captured
int nb_nodes
number of nodes
size_t items_capacity
allocated capacity
int parent_window_id
parent window for tab buttons
double content_size
total content size
float autofit_origin_x
original insertion point x for N_GUI_AUTOFIT_CENTER (set by n_gui_window_set_autofit)
void * user_data
user data for callback
double step
step increment (0 is treated as 1).
float global_scroll_y
global vertical scroll offset (when GUI exceeds display)
int scrollbar_drag_widget_id
id of the widget whose scrollbar is being dragged, or -1
float min_h
minimum height
float listbox_default_item_height
default item height for listbox
ALLEGRO_COLOR global_scrollbar_thumb_border_color
global scrollbar thumb border colour
ALLEGRO_COLOR border_active
border active
float scroll_x
horizontal scroll offset for single-line text (pixels)
float label_padding
horizontal padding each side
float titlebar_h
title bar height
float gui_offset_y
vertical letterbox offset for virtual canvas
ALLEGRO_BITMAP * item_selected_bitmap
optional bitmap for per-item background, selected/highlighted (NULL = color theme)
void(* on_select)(int widget_id, int index, int selected, void *user_data)
callback on selection change (widget_id, item_index, selected, user_data)
float item_height
item height in pixels
void(* on_select)(int widget_id, int index, void *user_data)
callback on selection change (widget_id, selected_index, user_data)
int focused_widget_id
id of the widget that currently has focus, or -1
int global_vscroll_drag
1 if global scrollbar vertical thumb is being dragged
int sel_dragging
1 if mouse is actively dragging to select text
int nb_visible
number of visible rows
ALLEGRO_COLOR global_scrollbar_track_color
global scrollbar track colour
float item_height_pad
min padding added to font height for item height
float display_h
display/viewport height
N_GUI_LISTITEM * items
dynamic array of items
void * user_data
user data for callback
float corner_ry
corner radius Y for rounded shapes
int sel_end
selection end byte offset (-1 = no selection)
int bg_scale_mode
scale mode for bg_bitmap: N_GUI_IMAGE_FIT, N_GUI_IMAGE_STRETCH, or N_GUI_IMAGE_CENTER
size_t text_alloc
allocated buffer size (char_limit + 1)
ALLEGRO_COLOR bg_normal
background normal
ALLEGRO_BITMAP * handle_bitmap
optional bitmap for the draggable handle, normal state (NULL = color theme)
ALLEGRO_FONT * font
font used by this widget (NULL = context default)
float norm_w
normalized width (fraction of parent window w)
int resize_mode
context resize mode: N_GUI_RESIZE_VIRTUAL or N_GUI_RESIZE_ADAPTIVE
float virtual_w
virtual canvas width (0 = disabled / identity transform)
float radiolist_default_item_height
default item height for radiolist
char label[128]
label displayed next to the checkbox
float slider_value_label_offset
gap between slider end and value label
size_t nb_items
number of items
int depth
depth in tree (0 = root)
ALLEGRO_COLOR bg_active
background active/pressed
char mask_char
mask character for password fields (0 = no masking, e.g.
int global_hscroll_drag
1 if global scrollbar horizontal thumb is being dragged
LIST * windows
ordered list of N_GUI_WINDOW* (back to front)
char text[128]
display text
int scroll_y
scroll offset for long text
void(* on_scroll)(int widget_id, double scroll_pos, void *user_data)
callback on scroll
int x
bounding box x offset from draw origin
float gui_bounds_h
total bounding box height of all windows (computed internally)
double min_val
minimum value
float textarea_cursor_blink_period
cursor blink period in seconds (full cycle on+off)
int lbl_value
label widget ID for "Value" header
void(* on_toggle)(int widget_id, int checked, void *user_data)
callback on toggle
ALLEGRO_COLOR bg_hover
background hover
size_t sel_start
selection anchor position (where shift-click/shift-arrow started).
int window_id
parent window
float dropdown_arrow_thickness
arrow stroke thickness
void * user_data
user data for callback
ALLEGRO_COLOR link_color_hover
link colour (hover)
float radio_circle_min_r
minimum outer circle radius
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the listbox background (NULL = color theme)
float item_text_padding
text padding inside list/combo/dropmenu items
int next_widget_id
next widget id to assign
int enabled_id
checkbox widget ID for the enabled toggle
float autofit_border
padding/border around content for auto-fit (pixels, applied on each side)
float gui_scale
computed uniform scale factor for virtual canvas
size_t cursor_pos
cursor position in text
void * user_data
user data for callback
int mode
mode: N_GUI_SLIDER_VALUE or N_GUI_SLIDER_PERCENT
size_t char_limit
maximum character limit (0 = N_GUI_TEXT_MAX)
int toggle_mode
toggle mode: 0 = momentary (default), 1 = toggle (stays clicked/unclicked)
int is_open
is the menu currently open
ALLEGRO_COLOR border_hover
border hover
float scrollbar_thumb_min
minimum thumb dimension
float x
x origin of first tab button
ALLEGRO_BITMAP * thumb_hover_bitmap
optional bitmap for the thumb on hover (NULL = fallback to thumb_bitmap)
int value_id
textarea widget ID for the value
char label[128]
label displayed on the button
ALLEGRO_BITMAP * bg_bitmap
optional bitmap for the text area background (NULL = color theme)
int align
text alignment: N_GUI_ALIGN_LEFT, N_GUI_ALIGN_CENTER, N_GUI_ALIGN_RIGHT
size_t text_len
current text length
char link[4096]
optional hyperlink URL (empty string = no link)
int orientation
orientation: N_GUI_SCROLLBAR_H or N_GUI_SCROLLBAR_V
float global_scrollbar_thumb_min
global minimum thumb dimension
double viewport_size
visible viewport size
void(* on_link_click)(int widget_id, const char *link, void *user_data)
callback when link is clicked (widget_id, link_url, user_data)
ALLEGRO_FONT * default_font
default font (must be set before adding widgets)
float combobox_max_dropdown_width
maximum dropdown width for N_GUI_COMBOBOX_AUTO_WIDTH (0 = display width)
int selection_mode
selection mode: N_GUI_SELECT_NONE, N_GUI_SELECT_SINGLE, N_GUI_SELECT_MULTIPLE
size_t nb_items
number of items
int key_modifiers
required modifier key flags for the keybind (0 = no modifier requirement, matches any modifier state ...
LIST * widgets
list of N_GUI_WIDGET* contained in this window
float y
position y on screen
ALLEGRO_BITMAP * bitmap_active
optional bitmap for active/pressed state
int enabled
enabled flag (1 = enabled, 0 = disabled: drawn dimmed and ignores input)
float radio_circle_border_thickness
outer circle border thickness
int keycode
bound keyboard keycode (0 = none).
ALLEGRO_BITMAP * thumb_active_bitmap
optional bitmap for the thumb while dragging (NULL = fallback to thumb_bitmap)
float slider_track_size
track width (vertical) or height (horizontal)
float scroll_x
horizontal scroll offset for auto-scrollbar (pixels)
float slider_handle_border_thickness
handle border thickness
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
Set the display (viewport) size for global scrollbar computation.
void n_gui_combobox_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bg, ALLEGRO_BITMAP *item_bg, ALLEGRO_BITMAP *item_selected)
Set optional bitmap overlays on a combobox widget.
int n_gui_load_theme_json(N_GUI_CTX *ctx, const char *filepath)
load a theme and style from a JSON file
int n_gui_listbox_get_scroll_offset(N_GUI_CTX *ctx, int widget_id)
get the current scroll offset (in items)
void n_gui_dropmenu_clear(N_GUI_CTX *ctx, int widget_id)
Remove all entries from a dropdown menu.
#define N_GUI_STATE_HOVER
mouse is hovering the widget
void n_gui_update_transform(N_GUI_CTX *ctx)
Recalculate scale and offset from virtual canvas to physical display.
int n_gui_button_is_toggled(N_GUI_CTX *ctx, int widget_id)
Check if a toggle button is currently in the "on" state.
float n_gui_detect_dpi_scale(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Detect and apply DPI scale from an Allegro display.
void n_gui_toggle_window(N_GUI_CTX *ctx, int window_id)
Toggle window visibility (show if hidden, hide if shown)
#define N_GUI_TREE_MAX
maximum number of nodes in a tree view
#define N_GUI_ZORDER_NORMAL
default z-order: window participates in normal raise/lower ordering
#define N_GUI_ALIGN_LEFT
left aligned text
void n_gui_raise_window(N_GUI_CTX *ctx, int window_id)
Bring a window to the front (top of draw order).
int n_gui_add_slider(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void(*on_change)(int, double, void *), void *user_data)
Add a slider widget.
void n_gui_combobox_set_flags(N_GUI_CTX *ctx, int widget_id, int flags)
Set combobox feature flags.
void n_gui_window_apply_autofit(N_GUI_CTX *ctx, int window_id)
Trigger auto-fit recalculation for a window.
void n_gui_textarea_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text content of a textarea widget
#define N_GUI_SLIDER_PERCENT
slider uses 0-100 percentage
double n_gui_scrollbar_get_pos(N_GUI_CTX *ctx, int widget_id)
get the current scroll position of a scrollbar widget
N_GUI_KVTABLE * n_gui_kvtable_create(N_GUI_CTX *ctx, int window_id, float row_height, float padding, void(*on_remove)(int, void *), void *user_data)
create a KV table in an existing window
int n_gui_add_vslider(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, double min_val, double max_val, double initial, int mode, void(*on_change)(int, double, void *), void *user_data)
Add a vertical slider widget.
void n_gui_set_display(N_GUI_CTX *ctx, ALLEGRO_DISPLAY *display)
Set the display pointer for clipboard operations (copy/paste).
void n_gui_window_set_bitmaps(N_GUI_CTX *ctx, int window_id, ALLEGRO_BITMAP *bg, ALLEGRO_BITMAP *titlebar, int bg_scale_mode)
Set optional bitmap overlays on a window's body and titlebar.
void n_gui_set_widget_theme(N_GUI_CTX *ctx, int widget_id, N_GUI_THEME theme)
Override the theme of a specific widget.
int n_gui_wants_mouse(N_GUI_CTX *ctx)
Check if the mouse is currently over any open GUI window.
int n_gui_dropmenu_get_count(N_GUI_CTX *ctx, int widget_id)
Get number of entries in a dropdown menu.
int n_gui_radiolist_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a radiolist widget
#define N_GUI_WIN_VSCROLL_DRAG
window vertical auto-scrollbar is being dragged
void n_gui_lower_window(N_GUI_CTX *ctx, int window_id)
Lower a window to the bottom of the draw order.
void n_gui_label_set_text(N_GUI_CTX *ctx, int widget_id, const char *text)
set the text of a label widget
int n_gui_add_listbox(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int selection_mode, void(*on_select)(int, int, int, void *), void *user_data)
Add a listbox widget.
#define N_GUI_COMBOBOX_AUTO_WIDTH
dropdown panel expands to fit the longest item text
#define N_GUI_TYPE_LABEL
widget type: static text label (with optional hyperlink)
#define N_GUI_WIN_RESIZE_SCALE
reposition AND resize proportionally, child widgets scale too
#define N_GUI_TYPE_CHECKBOX
widget type: checkbox
#define N_GUI_KV_MAX
maximum number of rows in a KV table
#define N_GUI_TYPE_DROPMENU
widget type: dropdown menu with static and dynamic entries
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
int n_gui_window_get_flags(N_GUI_CTX *ctx, int window_id)
Get feature flags of a window.
void n_gui_minimize_window(N_GUI_CTX *ctx, int window_id)
Minimise a window (show title bar only)
int n_gui_add_label(N_GUI_CTX *ctx, int window_id, const char *text, float x, float y, float w, float h, int align)
Add a static text label.
void n_gui_set_widget_enabled(N_GUI_CTX *ctx, int widget_id, int enabled)
Enable or disable a widget.
int n_gui_add_radiolist(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, int, void *), void *user_data)
Add a radio list widget (single selection with radio bullets)
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
void n_gui_tab_free(N_GUI_TAB_PANEL **panel)
free a tab panel (does not destroy the N_GUI widgets)
void n_gui_close_window(N_GUI_CTX *ctx, int window_id)
Close (hide) a window.
void n_gui_listbox_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bg, ALLEGRO_BITMAP *item_bg, ALLEGRO_BITMAP *item_selected)
Set optional bitmap overlays on a listbox widget.
void n_gui_label_set_link(N_GUI_CTX *ctx, int widget_id, const char *link)
set the link URL of a label widget
#define N_GUI_RESIZE_VIRTUAL
fixed virtual canvas with uniform scaling (default/existing behavior)
void n_gui_kvtable_free(N_GUI_KVTABLE **table)
free a KV table (does not destroy the N_GUI widgets)
void n_gui_window_set_resize_policy(N_GUI_CTX *ctx, int window_id, int policy)
Set per-window resize policy (N_GUI_WIN_RESIZE_NONE / _MOVE / _SCALE).
void n_gui_button_set_keycode_focused(N_GUI_CTX *ctx, int widget_id, int keycode, int modifiers, const int *sources, int source_count)
Set a focused key binding on a button.
void n_gui_radiolist_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a radiolist widget
void n_gui_listbox_set_selected(N_GUI_CTX *ctx, int widget_id, int index, int selected)
set the selection state of a listbox item
#define N_GUI_SELECT_NONE
no selection allowed (display only)
int n_gui_add_window_auto(N_GUI_CTX *ctx, const char *title, float x, float y)
Add a window with automatic sizing (use n_gui_window_autosize after adding widgets)
double n_gui_slider_get_value(N_GUI_CTX *ctx, int widget_id)
get the current value of a slider widget
int n_gui_listbox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a listbox widget
void n_gui_set_virtual_size(N_GUI_CTX *ctx, float w, float h)
Set the virtual canvas size for resolution-independent scaling.
float n_gui_get_dpi_scale(const N_GUI_CTX *ctx)
Get current DPI scale factor.
N_GUI_THEME n_gui_default_theme(void)
Build a sensible default colour theme.
#define N_GUI_WIN_RESIZE_NONE
no adaptation: absolute position and size unchanged
void n_gui_window_set_zorder(N_GUI_CTX *ctx, int window_id, int z_mode, int z_value)
Set window z-order mode and value.
int n_gui_radiolist_get_selected(N_GUI_CTX *ctx, int widget_id)
get the selected item index in a radiolist widget
void n_gui_tab_set_content_window(N_GUI_TAB_PANEL *panel, int tab_index, int window_id)
associate a content window with a tab
#define N_GUI_TYPE_SLIDER
widget type: slider
#define N_GUI_WIN_OPEN
window is visible
int n_gui_save_theme_json(N_GUI_CTX *ctx, const char *filepath)
save the current theme and style to a JSON file
#define N_GUI_SELECT_SINGLE
single item selection
int n_gui_combobox_add_item(N_GUI_CTX *ctx, int widget_id, const char *text)
add an item to a combo box
void n_gui_window_autosize(N_GUI_CTX *ctx, int window_id)
Recompute and apply minimum-fit size for a window based on its current widgets.
void n_gui_set_focus(N_GUI_CTX *ctx, int widget_id)
Set keyboard focus to a specific widget.
void n_gui_tree_rebuild(N_GUI_TREE *tree)
rebuild the listbox to reflect current tree state
void n_gui_textarea_set_selection(N_GUI_CTX *ctx, int widget_id, size_t start, size_t end)
set the text selection range (byte offsets into text content)
#define N_GUI_SELECT_MULTIPLE
multiple item selection
void n_gui_scrollbar_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *track, ALLEGRO_BITMAP *thumb, ALLEGRO_BITMAP *thumb_hover, ALLEGRO_BITMAP *thumb_active)
Set optional bitmap overlays on a scrollbar widget.
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
int n_gui_add_combobox(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, int, void *), void *user_data)
Add a combo box widget (dropdown selector)
void n_gui_button_set_toggled(N_GUI_CTX *ctx, int widget_id, int toggled)
Set the toggle state of a button.
void n_gui_tab_set_active(N_GUI_TAB_PANEL *panel, int index)
set the active tab (toggles buttons, opens/closes content windows)
void n_gui_slider_set_step(N_GUI_CTX *ctx, int widget_id, double step)
set slider step increment.
int n_gui_add_toggle_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, int initial_state, void(*on_click)(int, void *), void *user_data)
Add a toggle button widget (stays clicked/unclicked on single click)
void n_gui_kvtable_remove_row(N_GUI_KVTABLE *table, int row_index)
remove a row by index (hides widgets, marks inactive)
int n_gui_add_label_link(N_GUI_CTX *ctx, int window_id, const char *text, const char *link, float x, float y, float w, float h, int align, void(*on_link_click)(int, const char *, void *), void *user_data)
Add a static text label with hyperlink.
void n_gui_textarea_set_mask_char(N_GUI_CTX *ctx, int widget_id, char mask)
Set a mask character for password-style input.
int n_gui_listbox_get_selected(N_GUI_CTX *ctx, int widget_id)
get the index of the first selected item in a listbox
#define N_GUI_AUTOFIT_EXPAND_LEFT
expand leftward instead of rightward when adjusting width
int n_gui_add_image(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, ALLEGRO_BITMAP *bitmap, int scale_mode)
Add an image display widget.
void n_gui_label_set_bitmap(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bg)
Set optional background bitmap on a label widget.
void n_gui_draw(N_GUI_CTX *ctx)
Draw all visible windows and their widgets.
#define N_GUI_WIN_MINIMISED
window is minimised (title bar only)
#define N_GUI_SLIDER_H
horizontal slider (default)
#define N_GUI_AUTOFIT_CENTER
center the window on its insertion point after auto-fit (overrides EXPAND_LEFT/EXPAND_UP for centerin...
int n_gui_tab_add(N_GUI_TAB_PANEL *panel, const char *label)
add a tab to the panel, returns tab index
void n_gui_slider_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *track, ALLEGRO_BITMAP *fill, ALLEGRO_BITMAP *handle, ALLEGRO_BITMAP *handle_hover, ALLEGRO_BITMAP *handle_active)
Set optional bitmap overlays on a slider widget.
int n_gui_window_get_zorder(N_GUI_CTX *ctx, int window_id)
Get window z-order mode.
void n_gui_tree_free(N_GUI_TREE **tree)
free a tree view (does not destroy the N_GUI listbox)
#define N_GUI_TYPE_IMAGE
widget type: image display
int n_gui_window_get_resize_policy(N_GUI_CTX *ctx, int window_id)
Get per-window resize policy.
void n_gui_checkbox_set_checked(N_GUI_CTX *ctx, int widget_id, int checked)
set the checked state of a checkbox widget
int n_gui_dropmenu_add_dynamic_entry(N_GUI_CTX *ctx, int widget_id, const char *text, int tag, void(*on_click)(int, int, int, void *), void *user_data)
Add a dynamic entry (rebuilt each time menu opens)
void n_gui_apply_adaptive_resize(N_GUI_CTX *ctx, float new_w, float new_h)
Apply adaptive resize: reposition/resize all windows according to their policies for the new display ...
int n_gui_is_widget_enabled(N_GUI_CTX *ctx, int widget_id)
Check if a widget is enabled.
void n_gui_button_set_keycode(N_GUI_CTX *ctx, int widget_id, int keycode, int modifiers)
Bind a keyboard key with optional modifier requirements to a button.
int n_gui_window_is_open(N_GUI_CTX *ctx, int window_id)
Check if a window is currently visible.
void n_gui_set_dpi_scale(N_GUI_CTX *ctx, float scale)
Set DPI scale factor manually (default 1.0)
#define N_GUI_TYPE_RADIOLIST
widget type: radio list (single select radio buttons)
#define N_GUI_SLIDER_V
vertical slider
int n_gui_get_resize_mode(N_GUI_CTX *ctx)
Get current resize mode.
N_GUI_WIDGET * n_gui_get_widget(N_GUI_CTX *ctx, int widget_id)
Get a widget pointer by id.
int n_gui_dropmenu_add_entry(N_GUI_CTX *ctx, int widget_id, const char *text, int tag, void(*on_click)(int, int, int, void *), void *user_data)
Add a static entry to a dropdown menu.
#define N_GUI_ZORDER_ALWAYS_BEHIND
always drawn behind normal windows, cannot be raised above them
#define N_GUI_IMAGE_STRETCH
stretch to fill bounds
N_GUI_WINDOW * n_gui_get_window(N_GUI_CTX *ctx, int window_id)
Get a window pointer by id.
#define N_GUI_ZORDER_ALWAYS_ON_TOP
always drawn on top of normal windows, cannot be lowered behind them
void n_gui_dropmenu_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *panel, ALLEGRO_BITMAP *item_hover)
Set optional bitmap overlays on a dropmenu widget.
int n_gui_add_window(N_GUI_CTX *ctx, const char *title, float x, float y, float w, float h)
Add a new pseudo-window to the context.
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
void n_gui_slider_set_range(N_GUI_CTX *ctx, int widget_id, double min_val, double max_val)
set slider min/max range, clamping the current value if needed
const char * n_gui_listbox_get_item_text(N_GUI_CTX *ctx, int widget_id, int index)
get the text of a listbox item
void n_gui_radiolist_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bg, ALLEGRO_BITMAP *item_bg, ALLEGRO_BITMAP *item_selected)
Set optional bitmap overlays on a radiolist widget.
N_GUI_TREE * n_gui_tree_create(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, void *), void *user_data)
create a tree view in an existing window
int n_gui_add_button_bitmap(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, ALLEGRO_BITMAP *normal, ALLEGRO_BITMAP *hover, ALLEGRO_BITMAP *active, void(*on_click)(int, void *), void *user_data)
Add a bitmap-based button.
#define N_GUI_IMAGE_CENTER
draw at original size, centered
void n_gui_set_resize_mode(N_GUI_CTX *ctx, int mode)
Set context-level resize mode: N_GUI_RESIZE_VIRTUAL (default) or N_GUI_RESIZE_ADAPTIVE.
void n_gui_button_set_toggle_mode(N_GUI_CTX *ctx, int widget_id, int toggle_mode)
Enable or disable toggle mode on a button.
#define N_GUI_ALIGN_CENTER
center aligned text
void n_gui_tree_toggle_expand(N_GUI_TREE *tree, int node_index)
toggle expand/collapse of a node
#define N_GUI_TYPE_SCROLLBAR
widget type: scrollbar
void n_gui_textarea_scroll_to_offset(N_GUI_CTX *ctx, int widget_id, size_t byte_offset)
scroll a multiline textarea so that the given byte offset is vertically centered
void n_gui_textarea_set_bitmap(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bg)
Set optional background bitmap on a textarea widget.
void n_gui_listbox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a listbox widget
#define N_GUI_TYPE_BUTTON
widget type: button
#define N_GUI_TYPE_COMBOBOX
widget type: combo box (dropdown)
int n_gui_add_scrollbar(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int orientation, int shape, double content_size, double viewport_size, void(*on_scroll)(int, double, void *), void *user_data)
Add a scrollbar widget.
N_GUI_TEXT_DIMS n_gui_get_text_dims(ALLEGRO_FONT *font, const char *text)
get the bounding box dimensions of text rendered with the given font.
int n_gui_kvtable_get_count(N_GUI_KVTABLE *table)
get the number of active rows
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
#define N_GUI_ZORDER_FIXED
fixed z-value: window is sorted within a dedicated group between ALWAYS_BEHIND and NORMAL windows.
const char * n_gui_textarea_get_text(N_GUI_CTX *ctx, int widget_id)
get the text content of a textarea widget
int n_gui_tab_get_active(N_GUI_TAB_PANEL *panel)
get the active tab index
int n_gui_kvtable_add_row(N_GUI_KVTABLE *table, const char *key, const char *value, const char *description, int enabled)
add a row to the KV table
#define N_GUI_WIN_HSCROLL_DRAG
window horizontal auto-scrollbar is being dragged
void n_gui_checkbox_set_bitmaps(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *box, ALLEGRO_BITMAP *box_checked, ALLEGRO_BITMAP *box_hover)
Set optional bitmap overlays on a checkbox widget.
void n_gui_listbox_set_scroll_offset(N_GUI_CTX *ctx, int widget_id, int offset)
set the scroll offset (in items) — clamps to valid range
#define N_GUI_TEXT_MAX
maximum length for textarea content
#define N_GUI_STATE_IDLE
widget is idle / normal state
void n_gui_textarea_scroll_to_bottom(N_GUI_CTX *ctx, int widget_id)
scroll a multiline textarea to the bottom
size_t n_gui_textarea_get_text_length(N_GUI_CTX *ctx, int widget_id)
return the current text length in bytes
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
int n_gui_window_get_zvalue(N_GUI_CTX *ctx, int window_id)
Get window z-value (for N_GUI_ZORDER_FIXED mode).
void n_gui_dropmenu_set_entry_text(N_GUI_CTX *ctx, int widget_id, int index, const char *text)
Update text of an existing entry.
int n_gui_tree_add_node(N_GUI_TREE *tree, const char *label, int parent_index, void *user_data)
add a node to the tree
int n_gui_listbox_get_count(N_GUI_CTX *ctx, int widget_id)
get the number of items in a listbox widget
int n_gui_checkbox_is_checked(N_GUI_CTX *ctx, int widget_id)
check if a checkbox widget is checked
#define N_GUI_WIN_RESIZE_MOVE
reposition proportionally, keep pixel size
#define N_GUI_STATE_ACTIVE
widget is being pressed / dragged
int n_gui_combobox_get_selected(N_GUI_CTX *ctx, int widget_id)
get the selected item index in a combobox widget
void n_gui_image_set_bitmap(N_GUI_CTX *ctx, int widget_id, ALLEGRO_BITMAP *bitmap)
set the bitmap of an image widget
void n_gui_slider_set_value(N_GUI_CTX *ctx, int widget_id, double value)
set the value of a slider widget
#define N_GUI_AUTOFIT_EXPAND_UP
expand upward instead of downward when adjusting height
#define N_GUI_SHAPE_BITMAP
bitmap-based rendering
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
#define N_GUI_ALIGN_RIGHT
right aligned text
#define N_GUI_AUTOFIT_H
auto-adjust window height to content
void n_gui_radiolist_set_selected(N_GUI_CTX *ctx, int widget_id, int index)
set the selected item in a radiolist widget
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
#define N_GUI_KEY_MOD_MASK
mask of supported modifier flags for button keybind matching
#define N_GUI_ALIGN_JUSTIFIED
justified text (spread words to fill width)
void n_gui_set_widget_visible(N_GUI_CTX *ctx, int widget_id, int visible)
Show or hide a widget.
N_GUI_TAB_PANEL * n_gui_tab_create(N_GUI_CTX *ctx, int window_id, float x, float y, float button_w, float button_h, void(*on_tab_change)(int, void *), void *user_data)
create a tab panel in an existing window
#define N_GUI_ID_MAX
maximum length for widget id/name strings
N_GUI_STYLE n_gui_default_style(void)
Build sensible default style (all configurable sizes, colours, paddings)
#define N_GUI_TAB_MAX
maximum number of tabs in a single tab panel
#define N_GUI_AUTOFIT_W
auto-adjust window width to content
void n_gui_scrollbar_set_sizes(N_GUI_CTX *ctx, int widget_id, double content_size, double viewport_size)
set the content and viewport sizes of a scrollbar widget
#define N_GUI_WIN_AUTO_SCROLLBAR
enable automatic scrollbars when content exceeds window size
#define N_GUI_WIN_RESIZING
window is being resized
#define N_GUI_RESIZE_ADAPTIVE
virtual size tracks display; windows adapt per their resize_policy
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
#define N_GUI_TYPE_LISTBOX
widget type: listbox (selectable list)
int n_gui_listbox_is_selected(N_GUI_CTX *ctx, int widget_id, int index)
check if a listbox item is selected
void n_gui_window_update_normalized(N_GUI_CTX *ctx, int window_id)
Recapture normalized coordinates for a window from its current absolute position/size.
int n_gui_add_checkbox(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int initial_checked, void(*on_toggle)(int, int, void *), void *user_data)
Add a checkbox widget.
void n_gui_combobox_clear(N_GUI_CTX *ctx, int widget_id)
remove all items from a combobox widget
#define N_GUI_SHAPE_RECT
rectangle shape (default)
void n_gui_combobox_set_selected(N_GUI_CTX *ctx, int widget_id, int index)
set the selected item in a combobox widget
int n_gui_listbox_remove_item(N_GUI_CTX *ctx, int widget_id, int index)
remove an item from a listbox widget
#define N_GUI_SCROLLBAR_V
vertical scrollbar
N_GUI_THEME n_gui_make_theme(ALLEGRO_COLOR bg, ALLEGRO_COLOR bg_hover, ALLEGRO_COLOR bg_active, ALLEGRO_COLOR border, ALLEGRO_COLOR border_hover, ALLEGRO_COLOR border_active, ALLEGRO_COLOR text, ALLEGRO_COLOR text_hover, ALLEGRO_COLOR text_active, float border_thickness, float corner_rx, float corner_ry)
Create a custom theme with explicit colours.
int n_gui_add_dropmenu(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, void(*on_open)(int, void *), void *on_open_user_data)
Add a dropdown menu widget.
void n_gui_screen_to_virtual(const N_GUI_CTX *ctx, float sx, float sy, float *vx, float *vy)
Convert physical screen coordinates to virtual canvas coordinates.
int n_gui_add_button(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int shape, void(*on_click)(int, void *), void *user_data)
Add a button widget to a window.
#define N_GUI_KEY_SOURCES_MAX
maximum number of source widgets for a focused key binding
#define N_GUI_WIN_RESIZABLE
enable user-resizable window with a drag handle at bottom-right
#define N_GUI_STATE_FOCUSED
widget has keyboard focus
#define N_GUI_WIN_DRAGGING
window is being dragged
#define N_GUI_TYPE_TEXTAREA
widget type: text area
void n_gui_window_set_autofit(N_GUI_CTX *ctx, int window_id, int autofit_flags, float border)
Configure auto-fit behavior for a dialog window.
void n_gui_scrollbar_set_pos(N_GUI_CTX *ctx, int widget_id, double pos)
set the scroll position of a scrollbar widget
void n_gui_dropmenu_clear_dynamic(N_GUI_CTX *ctx, int widget_id)
Remove all dynamic entries (keep static ones)
The top-level GUI context that holds all windows.
dropdown menu specific data
A single entry in a dropdown menu (static or dynamic)
image widget specific data
a single row in the KV table
key-value table built from textareas, checkboxes, and buttons
static text label specific data
a single item in a list/radio/combo widget
Global style holding every configurable layout constant.
tab panel built from toggle buttons with content window management
bounding box dimensions returned by n_gui_get_text_dims()
Color theme for a widget.
tree view built from an N_GUI listbox
a single node in the tree view
A pseudo window that contains widgets.
int ht_get_ptr(HASH_TABLE *table, const char *key, void **val)
get pointer at 'key' from 'table'
int destroy_ht(HASH_TABLE **table)
empty a table and destroy it
HASH_TABLE * new_ht(size_t size)
Create a hash table with the given size.
int ht_put_ptr(HASH_TABLE *table, const char *key, void *ptr, void(*destructor)(void *ptr), void *(*duplicator)(void *ptr))
put an arbitrary pointer value with given key in the targeted hash table
LIST_NODE * end
pointer to the end of the list
void * ptr
void pointer to store
LIST_NODE * start
pointer to the start of the list
size_t nb_items
number of item currently in the list
void(* destroy_func)(void *ptr)
pointer to destructor function if any, else NULL
#define UNLIMITED_LIST_ITEMS
flag to pass to new_generic_list for an unlimited number of item in the list.
int list_push(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer to the end of the list.
#define list_foreach(__ITEM_, __LIST_)
ForEach macro helper, safe for node removal during iteration.
int list_unshift(LIST *list, void *ptr, void(*destructor)(void *ptr))
Add a pointer at the start of the list.
int list_destroy(LIST **list)
Empty and Free a list container.
void * remove_list_node_f(LIST *list, LIST_NODE *node)
Internal function called each time we need to get a node out of a list.
LIST * new_generic_list(size_t max_items)
Initialiaze a generic list container to max_items pointers.
Structure of a generic list node.
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
#define LOG_ERR
error conditions
#define LOG_WARNING
warning conditions
static void _draw_justified_selection(ALLEGRO_FONT *font, float x, float y, float max_w, float max_h, const char *text, int sel_start, int sel_end, ALLEGRO_COLOR sel_color)
helper: draw selection highlight rectangles over justified/wrapped text.
static size_t _textarea_pos_from_mouse(const N_GUI_TEXTAREA_DATA *td, ALLEGRO_FONT *font, float mx, float my, float ax, float ay, float widget_w, float widget_h, float pad, float scrollbar_size)
helper: find the byte position in a textarea closest to the given mouse coordinates (mx,...
static float _text_w(ALLEGRO_FONT *font, const char *text)
helper: get the advance width of text.
static void _register_widget(N_GUI_CTX *ctx, N_GUI_WIDGET *w)
helper: register a widget in the hash table
static float _json_get_float(cJSON *parent, const char *name, float fallback)
helper: read a float from JSON, return fallback if missing
static N_GUI_WINDOW * _find_focused_window(N_GUI_CTX *ctx)
helper: find the window containing a focused widget
static ALLEGRO_COLOR _bg_for_state(const N_GUI_THEME *t, int state)
helper: pick colour based on widget state
static void _n_gui_tree_listbox_selected(int widget_id, int index, int selected, void *user_data)
static void _draw_text_truncated(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, float max_w, const char *text)
helper: draw text truncated to a maximum pixel width.
static void _draw_textarea(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, N_GUI_STYLE *style)
draw a textarea widget
static void _normalize_crlf(char *s)
helper: strip CR from a string in-place, normalizing CRLF to LF.
static int _is_focusable_type(int type)
helper: check if a widget type is focusable via Tab navigation
static void _draw_slider(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, N_GUI_STYLE *style)
draw a slider widget (horizontal or vertical)
static void _draw_image(N_GUI_WIDGET *wgt, float ox, float oy)
draw an image widget
static int _zorder_group(const N_GUI_WINDOW *w)
static void _draw_themed_rect(N_GUI_THEME *t, int state, float x, float y, float w, float h, int rounded)
draw a themed rectangle or rounded rectangle.
static void _draw_dropmenu(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, const N_GUI_STYLE *style)
draw a dropdown menu button (closed state)
static float _label_content_height(const char *text, ALLEGRO_FONT *font, float max_w)
helper: compute the total content height of justified/wrapped label text, in pixels.
static N_GUI_WINDOW * _find_widget_window(N_GUI_CTX *ctx, int wgt_id, float *ox, float *oy)
helper: find the parent window of a widget by id, returning the window's content origin (accounting f...
static ALLEGRO_BITMAP * _select_state_bitmap(int state, ALLEGRO_BITMAP *normal_bmp, ALLEGRO_BITMAP *hover_bmp, ALLEGRO_BITMAP *active_bmp)
helper: select per-state bitmap with fallback chain.
static void _set_clipping_rect_transformed(int wx, int wy, int ww, int wh)
helper: set clipping rectangle in world space, transforming to screen space.
static int _n_gui_tree_node_visible(N_GUI_TREE *tree, int node_index)
static void _draw_dropmenu_panel(N_GUI_CTX *ctx)
draw the dropdown menu panel overlay (called after all windows)
static void _draw_widget(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, float win_w, N_GUI_STYLE *style)
draw a single widget.
static void _window_update_content_size(N_GUI_WINDOW *win, ALLEGRO_FONT *default_font)
helper: recompute content extents for a window (for scrollbar support)
static void _n_gui_window_capture_normalized(const N_GUI_CTX *ctx, N_GUI_WINDOW *win)
capture normalized coords for a window and its widgets from current absolutes
static float _min_thickness(float requested)
helper: compute minimum line thickness so it stays >= 1 physical pixel.
static float _scrollbar_calc_scroll(float mouse, float track_start, float track_length, float viewport, float content, float thumb_min)
helper: compute scroll position from mouse coordinate using thumb-aware math.
static void _textarea_clear_selection(N_GUI_TEXTAREA_DATA *td)
clear the selection (set both anchors to cursor_pos)
static void _draw_scrollbar(N_GUI_WIDGET *wgt, float ox, float oy, const N_GUI_STYLE *style)
draw a scrollbar widget
static void _textarea_sel_range(const N_GUI_TEXTAREA_DATA *td, size_t *lo, size_t *hi)
get the ordered selection range (lo, hi)
static void _draw_widget_vscrollbar(float area_x, float area_y, float area_w, float view_h, float content_h, float scroll_y, N_GUI_STYLE *style)
helper: draw a mini vertical scrollbar inside a widget.
static void _sort_windows_by_zorder(N_GUI_CTX *ctx)
Sort windows list by z-order: ALWAYS_BEHIND first, then FIXED (by z_value), then NORMAL,...
static int _justified_char_at_pos(const char *text, ALLEGRO_FONT *font, float max_w, float text_x, float text_y, float scroll_y, float click_mx, float click_my)
helper: find the byte offset in justified text closest to a click position.
static float _textarea_content_height(const N_GUI_TEXTAREA_DATA *td, ALLEGRO_FONT *font, float widget_w, float pad)
helper: compute the total content height of multiline textarea text (with line-wrap),...
static int _textarea_handle_key(N_GUI_WIDGET *wgt, ALLEGRO_EVENT *ev, ALLEGRO_FONT *font, float pad, float sb_size, N_GUI_CTX *ctx)
handle textarea key input.
static void _draw_combobox(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, const N_GUI_STYLE *style)
draw a combobox widget (closed state)
static int _items_grow(N_GUI_LISTITEM **items, const size_t *nb, size_t *cap)
helper: ensure items array has room for one more, returns 1 on success
static int _scrollbar_calc_scroll_int(float mouse, float track_start, float track_length, int visible_items, int total_items, float thumb_min)
helper: integer variant of _scrollbar_calc_scroll for item-based widgets.
static void _destroy_widget(void *ptr)
helper: destroy a single widget (called by list destructor)
static int _point_in_rect(float px, float py, float rx, float ry, float rw, float rh)
helper: test if point is inside a rectangle
static ALLEGRO_COLOR _text_for_state(const N_GUI_THEME *t, int state)
static double _clamp(double v, double lo, double hi)
helper: clamp a double between lo and hi
static void _textarea_delete_selection(N_GUI_WIDGET *wgt)
delete the currently selected text, move cursor to selection start
static int _json_get_int(cJSON *parent, const char *name, int fallback)
helper: read an int from JSON, return fallback if missing
static float _win_tbh(N_GUI_WINDOW *win)
helper: effective title bar height (0 for frameless windows)
static N_GUI_WIDGET * _new_widget(N_GUI_CTX *ctx, int type, float x, float y, float w, float h)
helper: allocate and initialise a base widget
static double _slider_snap_value(double val, double min_val, double max_val, double step)
helper: snap a slider value to the nearest valid step from min_val.
static void _draw_radiolist(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, const N_GUI_STYLE *style)
draw a radiolist widget
static void _draw_bitmap_scaled(ALLEGRO_BITMAP *bmp, float dx, float dy, float dw, float dh, int mode)
helper: draw a bitmap into a rectangle, respecting the given scale mode.
static void _draw_checkbox(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, N_GUI_STYLE *style)
draw a checkbox widget
static void _draw_combobox_dropdown(N_GUI_CTX *ctx)
draw the combobox dropdown overlay (called after all windows)
static void _n_gui_kv_add_clicked(int widget_id, void *user_data)
static void _n_gui_tab_button_clicked(int widget_id, void *user_data)
static int _textarea_has_selection(const N_GUI_TEXTAREA_DATA *td)
return 1 if the textarea has an active selection
static ALLEGRO_COLOR _border_for_state(const N_GUI_THEME *t, int state)
static LIST_NODE * _find_window_node(N_GUI_CTX *ctx, int window_id)
helper: find a window node in the context list by id
static void _draw_text_justified(ALLEGRO_FONT *font, ALLEGRO_COLOR color, float x, float y, float max_w, float max_h, const char *text)
helper: draw justified text within a given width, with multi-line word wrapping.
static int _dropmenu_entries_grow(N_GUI_DROPMENU_ENTRY **entries, const size_t *nb, size_t *cap)
helper: ensure dropmenu entries array has room for one more
static void _draw_listbox(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, N_GUI_STYLE *style)
draw a listbox widget
static int _textarea_copy_to_clipboard(const N_GUI_TEXTAREA_DATA *td, ALLEGRO_DISPLAY *display)
copy selected text to clipboard.
static void _slider_update_from_mouse(N_GUI_WIDGET *wgt, float mx, float my, float win_x, float win_content_y)
handle slider drag (horizontal or vertical)
static void _draw_button(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, const N_GUI_STYLE *style)
draw a button widget
static void _scrollbar_update_from_mouse(N_GUI_WIDGET *wgt, float mx, float my, float win_x, float win_content_y, const N_GUI_STYLE *style)
handle scrollbar drag
static void _draw_window(N_GUI_WINDOW *win, ALLEGRO_FONT *default_font, N_GUI_STYLE *style)
draw a window chrome + its widgets
static float _label_text_origin_x(N_GUI_LABEL_DATA *lb, ALLEGRO_FONT *font, float ax, float wgt_w, float win_w, float wgt_x, float label_padding)
helper: compute the screen-space x origin where label text starts, accounting for alignment.
static void _compute_gui_bounds(N_GUI_CTX *ctx)
helper: compute the bounding box of all open windows
static void _draw_label(N_GUI_WIDGET *wgt, float ox, float oy, ALLEGRO_FONT *default_font, float win_w, N_GUI_STYLE *style)
static void _n_gui_widget_capture_normalized(const N_GUI_WINDOW *win, N_GUI_WIDGET *wgt)
capture normalized coords for a single widget relative to its parent window
static void _destroy_window(void *ptr)
helper: destroy a single window (called by list destructor)
static ALLEGRO_COLOR _json_get_color(cJSON *parent, const char *name, ALLEGRO_COLOR fallback)
helper: read an RGBA colour from a JSON array [r,g,b,a]
static int _utf8_char_len(unsigned char c)
return the byte length of a UTF-8 character from its lead byte
static void _json_add_color(cJSON *parent, const char *name, ALLEGRO_COLOR c)
helper: add an RGBA colour as a JSON array [r,g,b,a] (0-255)
static int _label_char_at_x(const N_GUI_LABEL_DATA *lb, ALLEGRO_FONT *font, float click_x)
draw a label widget.
static void _n_gui_kv_remove_clicked(int widget_id, void *user_data)
static void _n_gui_kv_reposition(N_GUI_KVTABLE *table)
static int _utf8_encode(int cp, char *out)
encode a Unicode code point into UTF-8, return the number of bytes written (0 on error)
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.