Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_popup.c
Go to the documentation of this file.
1
16
17#define ALLEGRO_UNSTABLE 1
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <unistd.h>
23
24#include <allegro5/allegro_primitives.h>
25
26#include "nilorea/n_common.h"
27#include "nilorea/n_log.h"
28#include "nilorea/n_gui.h"
29#include "nilorea/n_gui_popup.h"
30
31static int log_level = LOG_ERR;
32static int failures = 0;
33
35static int picks = 0, picked_tag = 0;
36
38static void expect_true(const char* what, int ok) {
39 if (!ok) {
40 n_log(LOG_ERR, "FAIL: %s", what);
41 failures++;
42 } else {
43 n_log(LOG_INFO, "ok: %s", what);
44 }
45}
46
48static int process_args(int argc, char** argv) {
49 int opt = 0;
50
51 while ((opt = getopt(argc, argv, "V:")) != -1) {
52 if (opt == 'V') {
53 log_level = atoi(optarg);
54 } else {
55 return -1;
56 }
57 }
58 return 0;
59}
60
62// cppcheck-suppress constParameterCallback ; callback signature must match N_GUI_POPUP_FN
63static void on_pick(N_GUI_POPUP* popup, int tag, void* user_data) {
64 (void)user_data;
65 picks++;
66 picked_tag = tag;
67 expect_true("the popup is closed before the callback", !n_gui_popup_is_open(popup));
68}
69
71static void feed(N_GUI_CTX* gui, int type, float x, float y, int button) {
72 ALLEGRO_EVENT ev;
73
74 memset(&ev, 0, sizeof(ev));
75 ev.type = (ALLEGRO_EVENT_TYPE)type;
76 ev.mouse.x = (int)x;
77 ev.mouse.y = (int)y;
78 ev.mouse.dx = type == ALLEGRO_EVENT_MOUSE_AXES ? 1 : 0;
79 ev.mouse.button = (unsigned int)button;
81}
82
84static void draw(N_GUI_CTX* gui) {
85 ALLEGRO_BITMAP* target = NULL;
86 int old = al_get_new_bitmap_flags();
87
88 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
89 target = al_create_bitmap(400, 300);
90 al_set_new_bitmap_flags(old);
91 al_set_target_bitmap(target);
92 al_clear_to_color(al_map_rgb(0, 0, 0));
94 al_set_target_bitmap(NULL);
95 al_destroy_bitmap(target);
96}
97
99static const N_GUI_WINDOW* level_window(N_GUI_CTX* gui, const N_GUI_POPUP* popup, int level) {
100 int id = n_gui_popup_window(popup, level);
101
102 if (id < 0 || !n_gui_window_is_open(gui, id)) return NULL;
103 return n_gui_get_window(gui, id);
104}
105
106int main(int argc, char** argv) {
107 const N_GUI_WINDOW* root = NULL;
108 const N_GUI_WINDOW* sub = NULL;
109 float row_h = 0.0f, root_h = 0.0f;
110
112 if (process_args(argc, argv) != 0) {
113 fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
114 return 2;
115 }
117 if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
118 n_log(LOG_ERR, "allegro init failed");
119 return 1;
120 }
121 ALLEGRO_FONT* font = al_create_builtin_font();
122 if (!font) return 1;
124 if (!gui) {
125 al_destroy_font(font);
126 return 1;
127 }
128 n_gui_set_display_size(gui, 400.0f, 300.0f);
129 /* a row is the font's line and four pixels above and below it */
130 row_h = (float)al_get_font_line_height(font) + 8.0f;
131
132 expect_true("a popup needs a callback", n_gui_popup_new(gui, 1.0f, NULL, NULL) == NULL);
133 N_GUI_POPUP* popup = n_gui_popup_new(gui, 1.0f, on_pick, NULL);
134 if (!popup) return 1;
135 int a = n_gui_popup_add_item(popup, N_GUI_POPUP_ROOT, "Alpha", 1, "the first");
137 int s = n_gui_popup_add_submenu(popup, N_GUI_POPUP_ROOT, "Some more", NULL);
138 int b = n_gui_popup_add_item(popup, s, "Bravo", 2, NULL);
139 int c = n_gui_popup_add_item(popup, s, "Charlie", 3, NULL);
140 int h = n_gui_popup_add_item(popup, N_GUI_POPUP_ROOT, "Hidden", 4, NULL);
141 int e = n_gui_popup_add_submenu(popup, N_GUI_POPUP_ROOT, "Empty", NULL);
142 int x = n_gui_popup_add_item(popup, e, "X-ray", 5, NULL);
143 int trailing = n_gui_popup_add_separator(popup, N_GUI_POPUP_ROOT);
144 expect_true("entries are made", a > 0 && s > 0 && b > 0 && c > 0 && h > 0 && e > 0 && x > 0 && trailing > 0);
145 expect_true("an item is not a level", n_gui_popup_add_item(popup, a, "no", 9, NULL) < 0);
146 n_gui_popup_set_enabled(popup, c, 0);
147 n_gui_popup_set_shown(popup, h, 0);
148 n_gui_popup_set_shown(popup, x, 0);
149
150 expect_true("closed before it is opened", !n_gui_popup_is_open(popup) && n_gui_popup_window(popup, N_GUI_POPUP_ROOT) < 0);
151 expect_true("it opens", n_gui_popup_open(popup, 10.0f, 10.0f) == 0 && n_gui_popup_is_open(popup));
152 root = level_window(gui, popup, N_GUI_POPUP_ROOT);
153 expect_true("where it was asked", root && root->x == 10.0f && root->y == 10.0f);
154 /* Alpha, the separator (7), Some more; not Hidden, not Empty whose one
155 * entry is hidden, not the trailing separator; 4 below the last row */
156 root_h = row_h + 7.0f + row_h + 4.0f;
157 expect_true("hidden entries, an empty submenu and a trailing separator take no room", root && root->h == root_h);
158 draw(gui);
159
160 /* rest on Some more: its level opens beside it, level with the row */
161 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 20.0f, 10.0f + row_h + 7.0f + 4.0f, 0);
162 sub = level_window(gui, popup, s);
163 expect_true("resting on a submenu opens it", sub != NULL);
164 expect_true("to the right, overlapping by two", sub && root && sub->x == root->x + root->w - 2.0f);
165 expect_true("its first row level with the row that opened it", sub && sub->y == 10.0f + row_h + 7.0f);
166 expect_true("the hit test covers the submenu", sub && n_gui_popup_hit(popup, sub->x + 5.0f, sub->y + 5.0f));
167 expect_true("and nothing else", !n_gui_popup_hit(popup, 399.0f, 299.0f));
168 draw(gui);
169
170 /* crossing the separator keeps it, another row closes it */
171 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 20.0f, 10.0f + row_h + 3.0f, 0);
172 expect_true("a separator keeps the submenu open", level_window(gui, popup, s) != NULL);
173 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 20.0f, 10.0f + 4.0f, 0);
174 expect_true("another row closes it", level_window(gui, popup, s) == NULL);
175
176 /* a disabled item and a hidden submenu do nothing */
177 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 20.0f, 10.0f + row_h + 7.0f + 4.0f, 0);
178 expect_true("a disabled item is not pressed", n_gui_popup_press(popup, c) == 0 && picks == 0);
179 expect_true("an entry that is not shown is not pressed", n_gui_popup_press(popup, e) == 0);
180
181 /* a press on Bravo picks it */
182 sub = level_window(gui, popup, s);
183 if (sub) feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, sub->x + 10.0f, sub->y + 4.0f, 1);
184 expect_true("a press on an item picks it", picks == 1 && picked_tag == 2);
185 expect_true("and closes every level", !n_gui_popup_is_open(popup) && level_window(gui, popup, s) == NULL);
186 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 0.0f, 0.0f, 1);
187
188 /* near the corner: pulled back inside, and the submenu goes left */
189 n_gui_popup_set_title(popup, "3 frames");
190 n_gui_popup_set_shown(popup, h, 1);
191 expect_true("it opens near the corner", n_gui_popup_open(popup, 395.0f, 295.0f) == 0);
192 root = level_window(gui, popup, N_GUI_POPUP_ROOT);
193 expect_true("pulled back inside the display", root && root->x + root->w <= 400.0f && root->y + root->h <= 300.0f);
194 expect_true("the heading and the shown entry take their rows", root && root->h == root_h + row_h + 7.0f + row_h);
195 expect_true("a pressed submenu opens", n_gui_popup_press(popup, s) == 1);
196 sub = level_window(gui, popup, s);
197 expect_true("on the left when the right has no room", sub && root && sub->x < root->x);
198 draw(gui);
199 expect_true("an item pressed through the API is picked", n_gui_popup_press(popup, a) == 1 && picks == 2 && picked_tag == 1);
200
201 n_gui_popup_open(popup, 10.0f, 10.0f);
202 n_gui_popup_close(popup);
203 expect_true("close closes", !n_gui_popup_is_open(popup));
204 n_gui_popup_free(&popup);
205 expect_true("free clears the handle", popup == NULL);
206 draw(gui);
207
209 al_destroy_font(font);
210 if (failures) {
211 fprintf(stderr, "ex_gui_popup: %d failure(s)\n", failures);
212 return 1;
213 }
214 printf("ex_gui_popup: ok\n");
215 return 0;
216}
void process_args(int argc, char **argv)
static int failures
Definition ex_atlas.c:51
int main(void)
Definition ex_atlas.c:117
static void expect_true(const char *label, int cond)
Definition ex_atlas.c:53
static int log_level
static void feed(N_GUI_CTX *gui, int type, int x, int y, int button)
feed one mouse event at display coordinates
static N_GUI_CTX * gui
static void draw(N_GUI_CTX *gui)
draw the whole context into a memory bitmap
static int picks
what the callback saw
static int picked_tag
static void on_pick(N_GUI_POPUP *popup, int tag, void *user_data)
the pick callback
static const N_GUI_WINDOW * level_window(N_GUI_CTX *gui, const N_GUI_POPUP *popup, int level)
a level's window, or NULL when it is not open
static void type(N_GUI_CTX *gui, const char *s)
Type a whole string into whatever has focus.
FONT * font
the default 8x8 font, valid after allegro_init()
float x
position x on screen
Definition n_gui.h:1312
float w
window width
Definition n_gui.h:1316
float h
window height
Definition n_gui.h:1318
float y
position y on screen
Definition n_gui.h:1314
void n_gui_set_display_size(N_GUI_CTX *ctx, float w, float h)
set the display (viewport) size so global scrollbars can be computed.
Definition n_gui.c:12348
int n_gui_process_event(N_GUI_CTX *ctx, ALLEGRO_EVENT event)
Process an allegro event through the GUI system.
Definition n_gui.c:13611
void n_gui_draw(N_GUI_CTX *ctx)
draw all visible GUI windows and widgets
Definition n_gui.c:11735
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
N_GUI_WINDOW * n_gui_get_window(N_GUI_CTX *ctx, int window_id)
Get a window pointer by id.
Definition n_gui.c:1397
N_GUI_CTX * n_gui_new_ctx(ALLEGRO_FONT *default_font)
Create a new GUI context.
Definition n_gui.c:1103
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
A pseudo window that contains widgets.
Definition n_gui.h:1298
#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
void set_log_level(const int log_level)
Set the global log level value ( static int LOG_LEVEL ).
Definition n_log.c:123
#define LOG_INFO
informational
Definition n_log.h:106
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.
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.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
the popup
A popup menu with submenus for N_GUI, the kind a right click opens.
Generic log system.