Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_button_keys.c
Go to the documentation of this file.
1
24
25#define ALLEGRO_UNSTABLE 1
26
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31
32#include "nilorea/n_common.h"
33#include "nilorea/n_log.h"
34#include "nilorea/n_gui.h"
35
36#include <allegro5/allegro_primitives.h>
37
38static int log_level = LOG_ERR;
39static int failures = 0;
41static int pressed[64];
42
44static void expect_true(const char* what, int ok) {
45 if (!ok) {
46 n_log(LOG_ERR, "FAIL: %s", what);
47 failures++;
48 } else {
49 n_log(LOG_INFO, "ok: %s", what);
50 }
51}
52
54static int process_args(int argc, char** argv) {
55 int opt = 0;
56
57 while ((opt = getopt(argc, argv, "V:")) != -1) {
58 if (opt == 'V') {
59 log_level = atoi(optarg);
60 } else {
61 return -1;
62 }
63 }
64 return 0;
65}
66
68static void on_click(int widget_id, void* user_data) {
69 (void)user_data;
70 if (widget_id >= 0 && widget_id < 64) pressed[widget_id]++;
71}
72
74static void key(N_GUI_CTX* gui, int keycode, int modifiers, int repeat) {
75 ALLEGRO_EVENT ev;
76
77 memset(&ev, 0, sizeof(ev));
78 ev.keyboard.keycode = keycode;
79 ev.keyboard.modifiers = (unsigned)modifiers;
80 if (!repeat) {
81 ev.type = ALLEGRO_EVENT_KEY_DOWN;
83 }
84 ev.type = ALLEGRO_EVENT_KEY_CHAR;
85 ev.keyboard.repeat = repeat ? true : false;
87}
88
89int main(int argc, char** argv) {
91 if (process_args(argc, argv) != 0) {
92 fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
93 return 2;
94 }
96
97 if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
98 n_log(LOG_ERR, "allegro init failed");
99 return 1;
100 }
101 ALLEGRO_FONT* font = al_create_builtin_font();
102 if (!font) return 1;
104 if (!gui) {
105 al_destroy_font(font);
106 return 1;
107 }
108 n_gui_set_display_size(gui, 800.0f, 600.0f);
109 int win = n_gui_add_window(gui, "Question", 10, 10, 400, 200);
110 int field = n_gui_add_textarea(gui, win, 10, 10, 300, 24, 0, 64, NULL, NULL);
111 /* added out of order on purpose: the arrows follow the screen, not the list */
112 int go = n_gui_add_button(gui, win, "Delete", 250, 50, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
113 int other = n_gui_add_button(gui, win, "Other", 10, 50, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
114 int cancel = n_gui_add_button(gui, win, "Cancel", 130, 51, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
115 int below = n_gui_add_button(gui, win, "Help", 10, 90, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
116 int hidden = n_gui_add_button(gui, win, "Hidden", 10, 130, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
117
118 if (field < 0 || go < 0 || other < 0 || cancel < 0 || below < 0 || hidden < 0 || below >= 64) {
119 n_log(LOG_ERR, "cannot build the window");
120 return 1;
121 }
122 n_gui_set_widget_visible(gui, hidden, 0);
123 /* Help answers Enter globally, as a default button would. */
124 n_gui_button_set_keycode(gui, below, ALLEGRO_KEY_ENTER, 0);
125
126 /* Tab reaches the field, then the buttons, in the window's order. */
127 key(gui, ALLEGRO_KEY_TAB, 0, 0);
128 expect_true("Tab focuses the field first", n_gui_get_focus(gui) == field);
129 key(gui, ALLEGRO_KEY_TAB, 0, 0);
130 expect_true("Tab goes on to a button", n_gui_get_focus(gui) == go);
131 key(gui, ALLEGRO_KEY_TAB, ALLEGRO_KEYMOD_SHIFT, 0);
132 expect_true("Shift+Tab comes back to the field", n_gui_get_focus(gui) == field);
133
134 /* The arrows, in reading order: Other, Cancel (a pixel lower, same row),
135 * Delete, then Help on the next row, then round; the hidden one never. */
136 n_gui_set_focus(gui, other);
137 key(gui, ALLEGRO_KEY_RIGHT, 0, 0);
138 expect_true("Right: Other to Cancel", n_gui_get_focus(gui) == cancel);
139 key(gui, ALLEGRO_KEY_RIGHT, 0, 0);
140 expect_true("Right: Cancel to Delete", n_gui_get_focus(gui) == go);
141 key(gui, ALLEGRO_KEY_DOWN, 0, 0);
142 expect_true("Down: Delete to Help, on the next row", n_gui_get_focus(gui) == below);
143 key(gui, ALLEGRO_KEY_RIGHT, 0, 0);
144 expect_true("Right from the last goes round to the first", n_gui_get_focus(gui) == other);
145 key(gui, ALLEGRO_KEY_LEFT, 0, 0);
146 expect_true("Left from the first goes round to the last", n_gui_get_focus(gui) == below);
147 key(gui, ALLEGRO_KEY_UP, 0, 0);
148 expect_true("Up: Help back to Delete", n_gui_get_focus(gui) == go);
149
150 /* Enter and Space press the focused button, and only it. */
151 key(gui, ALLEGRO_KEY_ENTER, 0, 0);
152 expect_true("Enter presses the focused button", pressed[go] == 1);
153 expect_true("and not the one bound to Enter", pressed[below] == 0);
154 key(gui, ALLEGRO_KEY_SPACE, 0, 0);
155 expect_true("Space presses it too", pressed[go] == 2);
156 key(gui, ALLEGRO_KEY_ENTER, 0, 1);
157 expect_true("a held key's repeats are not more presses", pressed[go] == 2);
158 key(gui, ALLEGRO_KEY_PAD_ENTER, 0, 0);
159 expect_true("the keypad's Enter presses it", pressed[go] == 3);
160
161 expect_true("a focused button keeps Enter", n_gui_focus_takes_key(gui, ALLEGRO_KEY_ENTER));
162 expect_true("and the arrows", n_gui_focus_takes_key(gui, ALLEGRO_KEY_LEFT));
163 expect_true("but not a letter", !n_gui_focus_takes_key(gui, ALLEGRO_KEY_A));
164
165 /* With nothing focused the binding answers Enter again. */
166 n_gui_set_focus(gui, -1);
167 key(gui, ALLEGRO_KEY_ENTER, 0, 0);
168 expect_true("with no focus, Enter is the bound button's", pressed[below] == 1 && pressed[go] == 3);
169
170 /* A mouse press on another button takes the keyboard back, as a press
171 * anywhere else does: a button takes no focus from a press, and the
172 * outline must not stay on a button nobody is using. */
173 {
174 float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
175 ALLEGRO_EVENT ev;
176 int before = pressed[other];
177
178 n_gui_set_focus(gui, go);
179 if (n_gui_widget_screen_rect(gui, other, &x, &y, &w, &h)) {
180 memset(&ev, 0, sizeof(ev));
181 ev.type = ALLEGRO_EVENT_MOUSE_AXES;
182 ev.mouse.x = (int)(x + w * 0.5f);
183 ev.mouse.y = (int)(y + h * 0.5f);
185 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_DOWN;
186 ev.mouse.button = 1;
188 ev.type = ALLEGRO_EVENT_MOUSE_BUTTON_UP;
190 }
191 expect_true("the clicked button is pressed", pressed[other] == before + 1);
192 expect_true("and the keyboard leaves the button it was on", n_gui_get_focus(gui) == -1);
193 }
194
195 /* The focus drawn: a slider reached by Tab, and a button, show an
196 * outline in the active border colour just inside their edge; without
197 * the focus that pixel is something else. */
198 {
199 ALLEGRO_COLOR mark = al_map_rgb(255, 0, 255);
200 N_GUI_THEME theme = n_gui_make_theme(al_map_rgb(40, 40, 40), al_map_rgb(50, 50, 50), al_map_rgb(60, 60, 60),
201 al_map_rgb(90, 90, 90), al_map_rgb(100, 100, 100), mark,
202 al_map_rgb(220, 220, 220), al_map_rgb(230, 230, 230),
203 al_map_rgb(240, 240, 240), 1.0f, 0.0f, 0.0f);
204 int win2 = n_gui_add_window(gui, "Focus", 420, 10, 300, 160);
205 int slider = 0, button = 0;
206 ALLEGRO_BITMAP* target = NULL;
207 int old = al_get_new_bitmap_flags();
208 float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
209 unsigned char r = 0, g = 0, b = 0;
210
212 slider = n_gui_add_slider(gui, win2, 10, 10, 200, 24, 0.0, 100.0, 50.0, N_GUI_SLIDER_VALUE, NULL, NULL);
213 button = n_gui_add_button(gui, win2, "Go", 10, 50, 80, 24, N_GUI_SHAPE_RECT, on_click, NULL);
214 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
215 target = al_create_bitmap(800, 600);
216 al_set_new_bitmap_flags(old);
217 if (!target || slider < 0 || button < 0) {
218 expect_true("a bitmap to draw into", 0);
219 } else {
220 int s_on = 0, s_off = 0, b_on = 0;
221
222 n_gui_raise_window(gui, win2);
223 al_set_target_bitmap(target);
224 /* not focused */
225 n_gui_set_focus(gui, -1);
226 al_clear_to_color(al_map_rgb(0, 0, 0));
228 n_gui_widget_screen_rect(gui, slider, &x, &y, &w, &h);
229 al_unmap_rgb(al_get_pixel(target, (int)x + 1, (int)(y + h * 0.5f)), &r, &g, &b);
230 s_off = (r == 255 && g == 0 && b == 255);
231 /* the slider, by Tab from the window's start */
232 n_gui_set_focus(gui, slider);
233 al_clear_to_color(al_map_rgb(0, 0, 0));
235 al_unmap_rgb(al_get_pixel(target, (int)x + 1, (int)(y + h * 0.5f)), &r, &g, &b);
236 s_on = (r == 255 && g == 0 && b == 255);
237 n_gui_set_focus(gui, button);
238 al_clear_to_color(al_map_rgb(0, 0, 0));
240 n_gui_widget_screen_rect(gui, button, &x, &y, &w, &h);
241 al_unmap_rgb(al_get_pixel(target, (int)x + 1, (int)(y + h * 0.5f)), &r, &g, &b);
242 b_on = (r == 255 && g == 0 && b == 255);
243 al_set_target_bitmap(NULL);
244 expect_true("a slider without the focus has no outline", !s_off);
245 expect_true("a focused slider is outlined in the active border colour", s_on);
246 expect_true("and so is a focused button", b_on);
247 }
248 if (target) al_destroy_bitmap(target);
249 }
250
252 al_destroy_font(font);
253 n_log(LOG_NOTICE, "ex_gui_button_keys: %s", failures ? "FAILED" : "ok");
254 return failures ? 1 : 0;
255}
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 on_click(int widget_id, void *user_data)
a button's click: counted
static int pressed[64]
presses per button, indexed by widget id
static N_GUI_CTX * gui
volatile char key[227]
TRUE while the key is held.
FONT * font
the default 8x8 font, valid after allegro_init()
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_widget_screen_rect(N_GUI_CTX *ctx, int widget_id, float *x, float *y, float *w, float *h)
Where a widget is on screen, rather than inside its window.
Definition n_gui.c:2091
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
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.
Definition n_gui.c:3106
int n_gui_focus_takes_key(const N_GUI_CTX *ctx, int keycode)
Would the focused widget do something with this particular key.
Definition n_gui.c:2284
#define N_GUI_SLIDER_VALUE
slider uses raw start/end values
Definition n_gui.h:178
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_set_focus(N_GUI_CTX *ctx, int widget_id)
Set keyboard focus to a specific widget.
Definition n_gui.c:3908
void n_gui_draw(N_GUI_CTX *ctx)
draw all visible GUI windows and widgets
Definition n_gui.c:11735
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.
Definition n_gui.c:3075
int n_gui_get_focus(const N_GUI_CTX *ctx)
Which widget has the keyboard, or -1 when none has.
Definition n_gui.c:2168
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
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
void n_gui_set_default_theme(N_GUI_CTX *ctx, N_GUI_THEME theme)
Set the default theme on a context and sync scrollbar style colors.
Definition n_gui.c:919
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
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.
Definition n_gui.c:3162
#define N_GUI_SHAPE_RECT
rectangle shape (default)
Definition n_gui.h:170
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.
Definition n_gui.c:825
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.
Definition n_gui.c:2720
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
Color theme for a widget.
Definition n_gui.h:384
#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_NOTICE
normal but significant condition
Definition n_log.h:104
#define LOG_INFO
informational
Definition n_log.h:106
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.