Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
n_gui_popup.c
Go to the documentation of this file.
1/*
2 * Nilorea Library
3 * Copyright (C) 2005-2026 Castagnier Mickael
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
14 * implied. See the License for the specific language governing
15 * permissions and limitations under the License.
16 *
17 * SPDX-License-Identifier: Apache-2.0
18 */
19
27
28#ifdef HAVE_ALLEGRO
29
30#include <stdlib.h>
31#include <string.h>
32
33#include <allegro5/allegro_primitives.h>
34
35#include "nilorea/n_common.h"
36#include "nilorea/n_log.h"
37#include "nilorea/n_gui_popup.h"
38
40#define N_GUI_POPUP_PAD_X 10.0f
42#define N_GUI_POPUP_PAD_Y 4.0f
44#define N_GUI_POPUP_SEP_H 7.0f
46#define N_GUI_POPUP_ARROW_W 16.0f
48#define N_GUI_POPUP_MIN_W 120.0f
51#define N_GUI_POPUP_OVERLAP 2.0f
52
54#define _POPUP_KIND_ROOT 0
56#define _POPUP_KIND_ITEM 1
58#define _POPUP_KIND_SUB 2
60#define _POPUP_KIND_SEP 3
61
63typedef struct {
67 int entry;
69
72typedef struct {
74 int kind;
76 int parent;
78 char* text;
80 char* tip;
82 int tag;
84 int shown;
88 int window;
90 int widget;
94 int hover;
96 int child;
98 int* rows;
102 float* row_y;
104 float w, h;
106 float x, y;
108
127
129static int _popup_valid(const N_GUI_POPUP* popup, int id) {
130 return popup && id >= 0 && id < popup->nb_entries;
131}
132
134static int _popup_is_level(const N_GUI_POPUP* popup, int id) {
135 return _popup_valid(popup, id) && (popup->entries[id].kind == _POPUP_KIND_ROOT || popup->entries[id].kind == _POPUP_KIND_SUB);
136}
137
139static ALLEGRO_FONT* _popup_font(const N_GUI_POPUP* popup) {
140 return popup->ctx ? popup->ctx->default_font : NULL;
141}
142
144static float _popup_row_h(const N_GUI_POPUP* popup) {
145 ALLEGRO_FONT* font = _popup_font(popup);
146 float line = font ? (float)al_get_font_line_height(font) : 12.0f;
147
148 return line + 2.0f * N_GUI_POPUP_PAD_Y * popup->scale;
149}
150
152static int _popup_append(N_GUI_POPUP* popup, int kind, int parent, const char* text, const char* tip, int tag) {
153 _N_GUI_POPUP_ENTRY* e = NULL;
154
155 if (popup->nb_entries == popup->capacity) {
156 int wanted = popup->capacity ? popup->capacity * 2 : 16;
157 _N_GUI_POPUP_ENTRY* grown = (_N_GUI_POPUP_ENTRY*)realloc(popup->entries, (size_t)wanted * sizeof(*grown));
158
159 if (!grown) {
160 n_log(LOG_ERR, "popup: out of memory for an entry");
161 return -1;
162 }
163 popup->entries = grown;
164 popup->capacity = wanted;
165 }
166 e = &popup->entries[popup->nb_entries];
167 memset(e, 0, sizeof(*e));
168 e->kind = kind;
169 e->parent = parent;
170 e->tag = tag;
171 e->shown = 1;
172 e->enabled = 1;
173 e->window = -1;
174 e->widget = -1;
175 e->hover = -1;
176 e->child = -1;
177 if (text && text[0]) {
178 e->text = strdup(text);
179 if (!e->text) return -1;
180 }
181 if (tip && tip[0]) {
182 e->tip = strdup(tip);
183 if (!e->tip) {
184 FreeNoLog(e->text);
185 return -1;
186 }
187 }
188 return popup->nb_entries++;
189}
190
194static int _popup_has_rows(const N_GUI_POPUP* popup, int level) {
195 int i = 0;
196
197 for (i = 1; i < popup->nb_entries; i++) {
198 const _N_GUI_POPUP_ENTRY* e = &popup->entries[i];
199
200 if (e->parent != level || !e->shown || e->kind == _POPUP_KIND_SEP) continue;
201 if (e->kind == _POPUP_KIND_SUB && !_popup_has_rows(popup, i)) continue;
202 return 1;
203 }
204 return 0;
205}
206
208static int _popup_row_shows(const N_GUI_POPUP* popup, int id) {
209 const _N_GUI_POPUP_ENTRY* e = &popup->entries[id];
210
211 if (!e->shown) return 0;
212 if (e->kind == _POPUP_KIND_SUB) return _popup_has_rows(popup, id);
213 return 1;
214}
215
219static int _popup_layout(N_GUI_POPUP* popup, int level) {
220 _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
221 ALLEGRO_FONT* font = _popup_font(popup);
222 float s = popup->scale, row_h = _popup_row_h(popup), y = 0.0f, widest = 0.0f;
223 int i = 0, n = 0, pending_sep = -1, has_sub = 0;
224
225 FreeNoLog(lv->rows);
226 FreeNoLog(lv->row_y);
227 lv->nb_rows = 0;
228 lv->rows = (int*)calloc((size_t)popup->nb_entries + 1, sizeof(int));
229 lv->row_y = (float*)calloc((size_t)popup->nb_entries + 1, sizeof(float));
230 if (!lv->rows || !lv->row_y) {
231 FreeNoLog(lv->rows);
232 FreeNoLog(lv->row_y);
233 return -1;
234 }
235 /* the heading, on the top level only, takes a row and a separator */
236 if (level == N_GUI_POPUP_ROOT && lv->text) {
237 y += row_h + N_GUI_POPUP_SEP_H * s;
238 if (font) widest = (float)al_get_text_width(font, lv->text);
239 }
240 for (i = 1; i < popup->nb_entries; i++) {
241 const _N_GUI_POPUP_ENTRY* e = &popup->entries[i];
242
243 if (e->parent != level) continue;
244 if (e->kind == _POPUP_KIND_SEP) {
245 if (e->shown && n > 0) pending_sep = i;
246 continue;
247 }
248 if (!_popup_row_shows(popup, i)) continue;
249 if (pending_sep >= 0) {
250 lv->rows[n] = pending_sep;
251 lv->row_y[n] = y;
252 y += N_GUI_POPUP_SEP_H * s;
253 n++;
254 pending_sep = -1;
255 }
256 lv->rows[n] = i;
257 lv->row_y[n] = y;
258 y += row_h;
259 n++;
260 if (e->kind == _POPUP_KIND_SUB) has_sub = 1;
261 if (font && e->text) {
262 float w = (float)al_get_text_width(font, e->text);
263
264 if (w > widest) widest = w;
265 }
266 }
267 lv->nb_rows = n;
268 lv->w = widest + 2.0f * N_GUI_POPUP_PAD_X * s + (has_sub ? N_GUI_POPUP_ARROW_W * s : 0.0f);
269 if (lv->w < N_GUI_POPUP_MIN_W * s) lv->w = N_GUI_POPUP_MIN_W * s;
270 /* a little room under the last row */
271 lv->h = y + N_GUI_POPUP_PAD_Y * s;
272 return n > 0 ? 0 : -1;
273}
274
276static int _popup_row_at(const N_GUI_POPUP* popup, int level, float ly) {
277 const _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
278 float row_h = _popup_row_h(popup), sep_h = N_GUI_POPUP_SEP_H * popup->scale;
279 int r = 0;
280
281 for (r = 0; r < lv->nb_rows; r++) {
282 float h = popup->entries[lv->rows[r]].kind == _POPUP_KIND_SEP ? sep_h : row_h;
283
284 if (ly >= lv->row_y[r] && ly < lv->row_y[r] + h) return r;
285 }
286 return -1;
287}
288
290static void _popup_close_level(N_GUI_POPUP* popup, int level) {
291 _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
292
293 if (lv->child >= 0) _popup_close_level(popup, lv->child);
294 lv->child = -1;
295 lv->hover = -1;
296 if (lv->window >= 0) n_gui_close_window(popup->ctx, lv->window);
297}
298
299static void _popup_draw(N_GUI_WIDGET* widget, float x, float y, ALLEGRO_FONT* font, void* user_data);
300static int _popup_mouse(int widget_id, const N_GUI_MOUSE_EVENT* ev, void* user_data);
301
303static int _popup_make_window(N_GUI_POPUP* popup, int level) {
304 _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
305
306 if (lv->window >= 0) return 0;
307 if (!lv->hook) {
308 lv->hook = (_N_GUI_POPUP_LEVEL*)calloc(1, sizeof(*lv->hook));
309 if (!lv->hook) return -1;
310 lv->hook->popup = popup;
311 lv->hook->entry = level;
312 }
313 lv->window = n_gui_add_window(popup->ctx, "", 0.0f, 0.0f, lv->w, lv->h);
314 if (lv->window < 0) return -1;
315 /* Frameless and fixed: a menu with a title bar is a dialog, and one that
316 * can be dragged away from what it is about is worse than one that
317 * cannot. */
320 lv->widget = n_gui_add_custom(popup->ctx, lv->window, 0.0f, 0.0f, lv->w, lv->h, _popup_draw, lv->hook);
321 if (lv->widget < 0) {
322 n_gui_close_window(popup->ctx, lv->window);
323 return -1;
324 }
326 n_gui_close_window(popup->ctx, lv->window);
327 return 0;
328}
329
332static int _popup_show_level(N_GUI_POPUP* popup, int level, float x, float y) {
333 _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
334 float dw = popup->ctx->display_w, dh = popup->ctx->display_h;
335
336 if (_popup_layout(popup, level) != 0) return -1;
337 if (_popup_make_window(popup, level) != 0) return -1;
338 if (dw > 0.0f && x + lv->w > dw) x = dw - lv->w;
339 if (dh > 0.0f && y + lv->h > dh) y = dh - lv->h;
340 if (x < 0.0f) x = 0.0f;
341 if (y < 0.0f) y = 0.0f;
342 lv->x = x;
343 lv->y = y;
344 lv->hover = -1;
345 lv->child = -1;
346 n_gui_window_set_rect(popup->ctx, lv->window, x, y, lv->w, lv->h);
347 n_gui_widget_set_rect(popup->ctx, lv->widget, 0.0f, 0.0f, lv->w, lv->h);
348 n_gui_set_widget_tooltip(popup->ctx, lv->widget, NULL);
349 n_gui_open_window(popup->ctx, lv->window);
350 n_gui_raise_window(popup->ctx, lv->window);
351 n_gui_mark_dirty(popup->ctx);
352 return 0;
353}
354
358static void _popup_open_child(N_GUI_POPUP* popup, int level, int row) {
359 _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
360 int sub = lv->rows[row];
361 float s = popup->scale, x = 0.0f, y = 0.0f, dw = popup->ctx->display_w;
362
363 if (lv->child == sub) return;
364 if (lv->child >= 0) _popup_close_level(popup, lv->child);
365 lv->child = -1;
366 if (_popup_layout(popup, sub) != 0) return;
367 x = lv->x + lv->w - N_GUI_POPUP_OVERLAP * s;
368 if (dw > 0.0f && x + popup->entries[sub].w > dw) x = lv->x - popup->entries[sub].w + N_GUI_POPUP_OVERLAP * s;
369 /* its first row level with the row that opened it */
370 y = lv->y + lv->row_y[row];
371 if (_popup_show_level(popup, sub, x, y) == 0) lv->child = sub;
372}
373
375static int _popup_activate(N_GUI_POPUP* popup, int level, int row) {
376 const _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
377 const _N_GUI_POPUP_ENTRY* e = NULL;
378
379 if (row < 0 || row >= lv->nb_rows) return 0;
380 e = &popup->entries[lv->rows[row]];
381 if (!e->enabled) return 0;
382 if (e->kind == _POPUP_KIND_SUB) {
383 _popup_open_child(popup, level, row);
384 return 1;
385 }
386 if (e->kind == _POPUP_KIND_ITEM) {
387 int tag = e->tag;
388
389 /* closed first, so a dialog the callback opens is not under it */
390 n_gui_popup_close(popup);
391 if (popup->on_pick) popup->on_pick(popup, tag, popup->user_data);
392 return 1;
393 }
394 return 0;
395}
396
398// cppcheck-suppress constParameterCallback ; callback signature must match N_GUI_CUSTOM_DRAW
399// cppcheck-suppress constParameter ; the same, under the id cppcheck 2.10 uses
400static void _popup_draw(N_GUI_WIDGET* widget, float x, float y, ALLEGRO_FONT* font, void* user_data) {
401 const _N_GUI_POPUP_LEVEL* hook = (const _N_GUI_POPUP_LEVEL*)user_data;
402 const N_GUI_POPUP* popup = NULL;
403 const _N_GUI_POPUP_ENTRY* lv = NULL;
404 const N_GUI_THEME* th = NULL;
405 float s = 1.0f, row_h = 0.0f, pad_x = 0.0f, pad_y = 0.0f;
406 ALLEGRO_COLOR dim;
407 int r = 0;
408
409 if (!widget || !hook || !hook->popup || !font) return;
410 popup = hook->popup;
411 lv = &popup->entries[hook->entry];
412 th = &widget->theme;
413 s = popup->scale;
414 row_h = _popup_row_h(popup);
415 pad_x = N_GUI_POPUP_PAD_X * s;
416 pad_y = N_GUI_POPUP_PAD_Y * s;
417 /* greyed text: the text colour halfway to the background */
418 {
419 float tr = 0, tg = 0, tb = 0, ta = 0, br = 0, bg = 0, bb = 0, ba = 0;
420
421 al_unmap_rgba_f(th->text_normal, &tr, &tg, &tb, &ta);
422 al_unmap_rgba_f(th->bg_normal, &br, &bg, &bb, &ba);
423 dim = al_map_rgba_f((tr + br) * 0.5f, (tg + bg) * 0.5f, (tb + bb) * 0.5f, ta);
424 }
425 al_draw_filled_rectangle(x, y, x + widget->w, y + widget->h, th->bg_normal);
426 al_draw_rectangle(x + 0.5f, y + 0.5f, x + widget->w - 0.5f, y + widget->h - 0.5f, th->border_normal, 1.0f);
427 if (hook->entry == N_GUI_POPUP_ROOT && lv->text) {
428 float my = y + row_h + N_GUI_POPUP_SEP_H * s * 0.5f;
429
430 al_draw_text(font, dim, x + pad_x, y + pad_y, 0, lv->text);
431 al_draw_line(x + pad_x * 0.5f, my, x + widget->w - pad_x * 0.5f, my, th->border_normal, 1.0f);
432 }
433 for (r = 0; r < lv->nb_rows; r++) {
434 const _N_GUI_POPUP_ENTRY* e = &popup->entries[lv->rows[r]];
435 float ry = y + lv->row_y[r];
436 int lit = 0;
437 ALLEGRO_COLOR ink;
438
439 if (e->kind == _POPUP_KIND_SEP) {
440 float my = ry + N_GUI_POPUP_SEP_H * s * 0.5f;
441
442 al_draw_line(x + pad_x * 0.5f, my, x + widget->w - pad_x * 0.5f, my, th->border_normal, 1.0f);
443 continue;
444 }
445 /* lit under the pointer, and while its submenu is open, so the way
446 * back to it shows */
447 lit = e->enabled && (r == lv->hover || lv->rows[r] == lv->child);
448 if (lit) al_draw_filled_rectangle(x + 1.0f, ry, x + widget->w - 1.0f, ry + row_h, th->bg_hover);
449 ink = !e->enabled ? dim : (lit ? th->text_hover : th->text_normal);
450 if (e->text) al_draw_text(font, ink, x + pad_x, ry + pad_y, 0, e->text);
451 if (e->kind == _POPUP_KIND_SUB) {
452 /* a small triangle pointing right, drawn rather than typed */
453 float ax = x + widget->w - pad_x - 4.0f * s, ay = ry + row_h * 0.5f, a = 3.5f * s;
454
455 al_draw_filled_triangle(ax - a, ay - a, ax - a, ay + a, ax + a * 0.5f, ay, ink);
456 }
457 }
458}
459
461// cppcheck-suppress constParameterCallback ; callback signature must match N_GUI_CUSTOM_MOUSE
462static int _popup_mouse(int widget_id, const N_GUI_MOUSE_EVENT* ev, void* user_data) {
463 const _N_GUI_POPUP_LEVEL* hook = (const _N_GUI_POPUP_LEVEL*)user_data;
464 N_GUI_POPUP* popup = NULL;
465 _N_GUI_POPUP_ENTRY* lv = NULL;
466 int level = 0, row = -1;
467
468 if (!hook || !hook->popup || !ev) return 0;
469 popup = hook->popup;
470 level = hook->entry;
471 lv = &popup->entries[level];
472 row = _popup_row_at(popup, level, ev->y);
473 if (ev->type == N_GUI_MOUSE_MOVE) {
474 if (row == lv->hover) return 1;
475 lv->hover = row;
476 n_gui_set_widget_tooltip(popup->ctx, widget_id, row >= 0 ? popup->entries[lv->rows[row]].tip : NULL);
477 if (row >= 0) {
478 const _N_GUI_POPUP_ENTRY* e = &popup->entries[lv->rows[row]];
479
480 if (e->kind == _POPUP_KIND_SUB && e->enabled) {
481 _popup_open_child(popup, level, row);
482 } else if (e->kind != _POPUP_KIND_SEP && lv->child >= 0) {
483 /* another row of this level: whatever was open beside it
484 * goes. A separator keeps it, so crossing one on the way to
485 * the submenu does not shut it. */
486 _popup_close_level(popup, lv->child);
487 lv->child = -1;
488 }
489 }
490 n_gui_mark_dirty(popup->ctx);
491 return 1;
492 }
493 if (ev->type == N_GUI_MOUSE_DOWN) {
494 /* Either button: a menu opened with the right one is often picked
495 * with it too. A press anywhere on a level is the popup's, even on a
496 * separator, so it never falls through to what is under it. */
497 _popup_activate(popup, level, row);
498 return 1;
499 }
500 return ev->type == N_GUI_MOUSE_UP;
501}
502
503N_GUI_POPUP* n_gui_popup_new(N_GUI_CTX* ctx, float scale, N_GUI_POPUP_FN on_pick, void* user_data) {
504 N_GUI_POPUP* popup = NULL;
505
506 __n_assert(ctx, return NULL);
507 __n_assert(on_pick, return NULL);
508 popup = (N_GUI_POPUP*)calloc(1, sizeof(*popup));
509 __n_assert(popup, return NULL);
510 popup->ctx = ctx;
511 popup->scale = scale > 0.0f ? scale : 1.0f;
512 popup->on_pick = on_pick;
513 popup->user_data = user_data;
514 if (_popup_append(popup, _POPUP_KIND_ROOT, -1, NULL, NULL, 0) != N_GUI_POPUP_ROOT) {
515 free(popup->entries);
516 free(popup);
517 return NULL;
518 }
519 return popup;
520}
521
522int n_gui_popup_add_item(N_GUI_POPUP* popup, int parent, const char* text, int tag, const char* tip) {
523 if (!_popup_is_level(popup, parent)) return -1;
524 return _popup_append(popup, _POPUP_KIND_ITEM, parent, text, tip, tag);
525}
526
527int n_gui_popup_add_submenu(N_GUI_POPUP* popup, int parent, const char* text, const char* tip) {
528 if (!_popup_is_level(popup, parent)) return -1;
529 return _popup_append(popup, _POPUP_KIND_SUB, parent, text, tip, 0);
530}
531
532int n_gui_popup_add_separator(N_GUI_POPUP* popup, int parent) {
533 if (!_popup_is_level(popup, parent)) return -1;
534 return _popup_append(popup, _POPUP_KIND_SEP, parent, NULL, NULL, 0);
535}
536
537void n_gui_popup_set_title(N_GUI_POPUP* popup, const char* text) {
538 _N_GUI_POPUP_ENTRY* root = NULL;
539
540 if (!popup || !popup->entries) return;
541 root = &popup->entries[N_GUI_POPUP_ROOT];
542 FreeNoLog(root->text);
543 /* a failed copy leaves no heading, which is the only harm it can do */
544 if (text && text[0]) root->text = strdup(text);
545}
546
547void n_gui_popup_set_shown(N_GUI_POPUP* popup, int entry, int shown) {
548 if (!_popup_valid(popup, entry) || entry == N_GUI_POPUP_ROOT) return;
549 popup->entries[entry].shown = shown ? 1 : 0;
550}
551
552void n_gui_popup_set_enabled(N_GUI_POPUP* popup, int entry, int enabled) {
553 if (!_popup_valid(popup, entry) || entry == N_GUI_POPUP_ROOT) return;
554 popup->entries[entry].enabled = enabled ? 1 : 0;
555}
556
557int n_gui_popup_open(N_GUI_POPUP* popup, float x, float y) {
558 if (!popup) return -1;
559 n_gui_popup_close(popup);
560 return _popup_show_level(popup, N_GUI_POPUP_ROOT, x, y);
561}
562
564 if (!popup || !popup->entries) return;
566 n_gui_mark_dirty(popup->ctx);
567}
568
570 if (!popup || !popup->entries || popup->entries[N_GUI_POPUP_ROOT].window < 0) return 0;
572}
573
574int n_gui_popup_hit(const N_GUI_POPUP* popup, float x, float y) {
575 int level = N_GUI_POPUP_ROOT;
576
577 if (!n_gui_popup_is_open(popup)) return 0;
578 /* the open levels are a chain from the root, each one's child */
579 while (level >= 0) {
580 const _N_GUI_POPUP_ENTRY* lv = &popup->entries[level];
581
582 if (x >= lv->x && x < lv->x + lv->w && y >= lv->y && y < lv->y + lv->h) return 1;
583 level = lv->child;
584 }
585 return 0;
586}
587
588int n_gui_popup_window(const N_GUI_POPUP* popup, int level) {
589 if (!_popup_is_level(popup, level)) return -1;
590 return popup->entries[level].window;
591}
592
593int n_gui_popup_press(N_GUI_POPUP* popup, int entry) {
594 const _N_GUI_POPUP_ENTRY* lv = NULL;
595 int level = 0, r = 0;
596
597 if (!_popup_valid(popup, entry) || entry == N_GUI_POPUP_ROOT) return 0;
598 level = popup->entries[entry].parent;
599 lv = &popup->entries[level];
600 if (lv->window < 0 || !n_gui_window_is_open(popup->ctx, lv->window)) return 0;
601 for (r = 0; r < lv->nb_rows; r++) {
602 if (lv->rows[r] == entry) return _popup_activate(popup, level, r);
603 }
604 return 0;
605}
606
608 int i = 0;
609
610 if (!popup || !*popup) return;
611 n_gui_popup_close(*popup);
612 for (i = 0; i < (*popup)->nb_entries; i++) {
613 _N_GUI_POPUP_ENTRY* e = &(*popup)->entries[i];
614
615 /* the widgets outlive the handle: hidden, and deaf, so nothing in
616 * them reaches the freed state */
617 if (e->widget >= 0) {
618 n_gui_custom_set_mouse((*popup)->ctx, e->widget, NULL, NULL);
619 n_gui_set_widget_visible((*popup)->ctx, e->widget, 0);
620 }
621 FreeNoLog(e->text);
622 FreeNoLog(e->tip);
623 FreeNoLog(e->rows);
624 FreeNoLog(e->row_y);
625 FreeNoLog(e->hook);
626 }
627 free((*popup)->entries);
628 free(*popup);
629 *popup = NULL;
630}
631
632#endif /* HAVE_ALLEGRO */
static int row_y(int row)
The screen y of the middle of one row of the open list.
static void ink(N_GUI_CTX *gui, int x0, int y0, int w, int h, int *bands, int *widest_gap, int *first_right)
draw the context into a memory bitmap and describe the ink inside a rectangle: how many separate band...
static void on_pick(N_GUI_POPUP *popup, int tag, void *user_data)
the pick callback
static const char * row(N_GUI_TREE *tree, int index)
the text of a visible row, "" past the end
void line(BITMAP *bmp, int x1, int y1, int x2, int y2, int color)
draw a line
FONT * font
the default 8x8 font, valid after allegro_init()
#define FreeNoLog(__ptr)
Free Handler without log.
Definition n_common.h:272
#define __n_assert(__ptr, __ret)
macro to assert things
Definition n_common.h:279
float h
height
Definition n_gui.h:1104
float y
pointer y from the widget's top edge; outside 0..h during a DRAG
Definition n_gui.h:1208
N_GUI_THEME theme
color theme for this widget
Definition n_gui.h:1118
float w
width
Definition n_gui.h:1102
ALLEGRO_COLOR text_normal
text normal
Definition n_gui.h:398
ALLEGRO_COLOR border_normal
border normal
Definition n_gui.h:392
ALLEGRO_COLOR text_hover
text hover
Definition n_gui.h:400
float display_w
display/viewport width (set via n_gui_set_display_size)
Definition n_gui.h:1745
int type
N_GUI_MOUSE_DOWN, _UP, _MOVE, _DRAG or _WHEEL.
Definition n_gui.h:1202
float display_h
display/viewport height
Definition n_gui.h:1747
ALLEGRO_COLOR bg_normal
background normal
Definition n_gui.h:386
ALLEGRO_COLOR bg_hover
background hover
Definition n_gui.h:388
ALLEGRO_FONT * default_font
default font.
Definition n_gui.h:1681
#define N_GUI_MOUSE_DOWN
custom widget mouse event: a press inside it, button 1 or 2
Definition n_gui.h:1187
void n_gui_raise_window(N_GUI_CTX *ctx, int window_id)
Bring a window to the front (top of draw order).
Definition n_gui.c:2058
void n_gui_set_widget_tooltip(N_GUI_CTX *ctx, int widget_id, const char *text)
Set (or clear) the tooltip text of any widget.
Definition n_gui.c:2880
void n_gui_window_set_rect(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h)
Move and resize a window at runtime, reflowing its widgets.
Definition n_gui.c:12196
void n_gui_widget_set_rect(N_GUI_CTX *ctx, int widget_id, float x, float y, float w, float h)
Move and resize a widget after creation.
Definition n_gui.c:3854
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
Definition n_gui.h:274
void n_gui_close_window(N_GUI_CTX *ctx, int window_id)
Close (hide) a window.
Definition n_gui.c:1406
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.
Definition n_gui.c:2403
#define N_GUI_WIN_FRAMELESS
frameless window: no title bar drawn, drag via window body unless N_GUI_WIN_FIXED_POSITION is also se...
Definition n_gui.h:276
void n_gui_custom_set_mouse(N_GUI_CTX *ctx, int widget_id, N_GUI_CUSTOM_MOUSE on_mouse, void *user_data)
give a custom widget mouse input
Definition n_gui.c:5730
int n_gui_window_is_open(N_GUI_CTX *ctx, int window_id)
Check if a window is currently visible.
Definition n_gui.c:2506
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.
Definition n_gui.c:1277
#define N_GUI_MOUSE_MOVE
custom widget mouse event: the pointer moved over it with no button held
Definition n_gui.h:1192
#define N_GUI_MOUSE_UP
custom widget mouse event: the release of a button 1 press that began in it, wherever the pointer is ...
Definition n_gui.h:1190
void n_gui_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
Definition n_gui.c:1436
#define N_GUI_ZORDER_POPUP
popup/modal dialogs: drawn above EVERYTHING including ALWAYS_ON_TOP windows.
Definition n_gui.h:369
void n_gui_window_set_flags(N_GUI_CTX *ctx, int window_id, int flags)
Set feature flags on a window.
Definition n_gui.c:2515
void n_gui_set_widget_visible(N_GUI_CTX *ctx, int widget_id, int visible)
Show or hide a widget.
Definition n_gui.c:3828
void n_gui_mark_dirty(N_GUI_CTX *ctx)
Mark the GUI as needing a redraw.
Definition n_gui.c:1188
int n_gui_add_custom(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, N_GUI_CUSTOM_DRAW draw, void *user_data)
add an owner-draw custom widget; returns the widget id or -1
Definition n_gui.c:5698
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
A mouse event handed to a custom widget, in the widget's own coordinates.
Definition n_gui.h:1200
Color theme for a widget.
Definition n_gui.h:384
A single GUI widget.
Definition n_gui.h:1092
#define n_log(__LEVEL__,...)
Logging function wrapper to get line and func.
Definition n_log.h:113
#define LOG_ERR
error conditions
Definition n_log.h:100
int n_gui_popup_window(const N_GUI_POPUP *popup, int level)
The window of a level, for an application that wants to test it.
void n_gui_popup_set_title(N_GUI_POPUP *popup, const char *text)
Set the heading line over the top level.
void n_gui_popup_close(N_GUI_POPUP *popup)
Close every level without picking anything.
int n_gui_popup_press(N_GUI_POPUP *popup, int entry)
Press the row of an entry, as a click on it would.
int n_gui_popup_open(N_GUI_POPUP *popup, float x, float y)
Open the top level with its corner at a point.
int n_gui_popup_add_item(N_GUI_POPUP *popup, int parent, const char *text, int tag, const char *tip)
Add an item at the end of a level.
int n_gui_popup_add_separator(N_GUI_POPUP *popup, int parent)
Add a separator at the end of a level.
int n_gui_popup_hit(const N_GUI_POPUP *popup, float x, float y)
Is a point on any of its open levels.
#define N_GUI_POPUP_ROOT
the top level, as a parent for n_gui_popup_add_item() and the others
Definition n_gui_popup.h:68
void n_gui_popup_set_shown(N_GUI_POPUP *popup, int entry, int shown)
Show or hide an entry.
int n_gui_popup_add_submenu(N_GUI_POPUP *popup, int parent, const char *text, const char *tip)
Add a submenu at the end of a level.
N_GUI_POPUP * n_gui_popup_new(N_GUI_CTX *ctx, float scale, N_GUI_POPUP_FN on_pick, void *user_data)
Make an empty popup menu in ctx, closed.
void(* N_GUI_POPUP_FN)(N_GUI_POPUP *popup, int tag, void *user_data)
called when an item is picked, after the popup has closed.
Definition n_gui_popup.h:78
int n_gui_popup_is_open(const N_GUI_POPUP *popup)
Is its top level on screen.
void n_gui_popup_set_enabled(N_GUI_POPUP *popup, int entry, int enabled)
Enable or grey out an entry.
void n_gui_popup_free(N_GUI_POPUP **popup)
Close the popup and release the handle.
Common headers and low-level functions & define.
int shown
shown when non-zero
Definition n_gui_popup.c:84
int widget
the level's custom widget
Definition n_gui_popup.c:90
int hover
the row under the pointer, an index into rows, or -1
Definition n_gui_popup.c:94
N_GUI_POPUP_FN on_pick
called on a pick
#define N_GUI_POPUP_PAD_Y
padding above and below a row's text, at scale 1
Definition n_gui_popup.c:42
static ALLEGRO_FONT * _popup_font(const N_GUI_POPUP *popup)
helper: the font rows are measured with
static int _popup_mouse(int widget_id, const N_GUI_MOUSE_EVENT *ev, void *user_data)
custom mouse callback: hover opens submenus, a press acts
char * tip
tooltip; owned, may be NULL
Definition n_gui_popup.c:80
static void _popup_draw(N_GUI_WIDGET *widget, float x, float y, ALLEGRO_FONT *font, void *user_data)
custom draw callback: the rows of one level
static int _popup_append(N_GUI_POPUP *popup, int kind, int parent, const char *text, const char *tip, int tag)
helper: append an entry, returning its id or -1
static int _popup_row_at(const N_GUI_POPUP *popup, int level, float ly)
helper: the row index at a level-local y, or -1
static int _popup_make_window(N_GUI_POPUP *popup, int level)
helper: make a level's window the first time it opens
_N_GUI_POPUP_LEVEL * hook
what the widget is handed; owned
Definition n_gui_popup.c:92
static int _popup_is_level(const N_GUI_POPUP *popup, int id)
helper: is this id a level (the root or a submenu)
float x
the level's position when last opened
static float _popup_row_h(const N_GUI_POPUP *popup)
helper: height of an item row
int entry
the entry that is this level
Definition n_gui_popup.c:67
int enabled
enabled when non-zero
Definition n_gui_popup.c:86
int tag
handed to on_pick
Definition n_gui_popup.c:82
#define N_GUI_POPUP_ARROW_W
room kept at the right of a level for the submenu arrow, at scale 1
Definition n_gui_popup.c:46
static int _popup_activate(N_GUI_POPUP *popup, int level, int row)
helper: act on a row: pick an item, open a submenu.
static int _popup_layout(N_GUI_POPUP *popup, int level)
helper: the rows of a level, and its size.
static void _popup_close_level(N_GUI_POPUP *popup, int level)
helper: close a level and everything open beside it
char * text
row text, or the heading for the root; owned, may be NULL
Definition n_gui_popup.c:78
#define N_GUI_POPUP_SEP_H
height of a separator row, at scale 1
Definition n_gui_popup.c:44
static int _popup_valid(const N_GUI_POPUP *popup, int id)
helper: is this id an entry
#define _POPUP_KIND_SUB
a submenu, which is also a level
Definition n_gui_popup.c:58
int child
the submenu open beside it, an entry id, or -1
Definition n_gui_popup.c:96
int nb_rows
how many rows
static void _popup_open_child(N_GUI_POPUP *popup, int level, int row)
helper: open a submenu beside its row.
int capacity
allocated
void * user_data
handed to on_pick
float scale
interface scale
N_GUI_CTX * ctx
the context
int * rows
entry ids of the rows as laid out at its last opening; owned
Definition n_gui_popup.c:98
int nb_entries
how many
N_GUI_POPUP * popup
the popup
Definition n_gui_popup.c:65
static int _popup_show_level(N_GUI_POPUP *popup, int level, float x, float y)
helper: lay a level out, place it with its corner at a point pulled back inside the display,...
#define _POPUP_KIND_SEP
a separator
Definition n_gui_popup.c:60
int parent
the level it is in; -1 for the root
Definition n_gui_popup.c:76
int kind
POPUP_KIND*
Definition n_gui_popup.c:74
static int _popup_has_rows(const N_GUI_POPUP *popup, int level)
helper: does a level have at least one row that would be shown.
int window
the level's window, or -1 until first opened
Definition n_gui_popup.c:88
#define _POPUP_KIND_ROOT
the top level
Definition n_gui_popup.c:54
#define N_GUI_POPUP_PAD_X
padding left and right of a row's text, at scale 1
Definition n_gui_popup.c:40
#define N_GUI_POPUP_MIN_W
narrowest a level is, at scale 1
Definition n_gui_popup.c:48
_N_GUI_POPUP_ENTRY * entries
the entries; 0 is the root.
static int _popup_row_shows(const N_GUI_POPUP *popup, int id)
helper: is an entry a row that shows, separators aside
#define N_GUI_POPUP_OVERLAP
how far a submenu overlaps its parent, at scale 1, so the pointer does not cross a gap on its way in
Definition n_gui_popup.c:51
float * row_y
top of each row from the level's top; owned, nb_rows long
float w
the level's size when last laid out
#define _POPUP_KIND_ITEM
an item, picked with a tag
Definition n_gui_popup.c:56
what a level's widget is handed: stable while the entries array moves
Definition n_gui_popup.c:63
the popup
A popup menu with submenus for N_GUI, the kind a right click opens.
Generic log system.