Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_grid.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_grid.h"
30
31static int log_level = LOG_ERR;
32static int failures = 0;
33
35static int cells_drawn = 0, backgrounds = 0, presses = 0, pressed_button = 0;
36static size_t first_seen = 0, last_seen = 0, pressed_index = 0;
37static float pressed_x = 0.0f, pressed_y = 0.0f;
39static int wheels = 0, wheel_mods = 0;
40static float zoom_cell = 50.0f;
42static N_GUI_CTX* zoom_gui = NULL;
43
45static void expect_true(const char* what, int ok) {
46 if (!ok) {
47 n_log(LOG_ERR, "FAIL: %s", what);
48 failures++;
49 } else {
50 n_log(LOG_INFO, "ok: %s", what);
51 }
52}
53
55static int process_args(int argc, char** argv) {
56 int opt = 0;
57
58 while ((opt = getopt(argc, argv, "V:")) != -1) {
59 if (opt == 'V') {
60 log_level = atoi(optarg);
61 } else {
62 return -1;
63 }
64 }
65 return 0;
66}
67
69static void draw_cell(int widget_id, size_t index, float x, float y, float w, float h, void* user_data) {
70 (void)widget_id;
71 (void)index;
72 (void)user_data;
74 al_draw_filled_rectangle(x, y, x + w, y + h, al_map_rgb(90, 90, 90));
75}
76
78static void draw_background(int widget_id, float x, float y, float w, float h, void* user_data) {
79 (void)widget_id;
80 (void)user_data;
82 al_draw_filled_rectangle(x, y, x + w, y + h, al_map_rgb(20, 20, 20));
83}
84
86static void visible(int widget_id, size_t first, size_t last, void* user_data) {
87 (void)widget_id;
88 (void)user_data;
89 first_seen = first;
91}
92
94static void press(int widget_id, size_t index, int button, float x, float y, void* user_data) {
95 (void)widget_id;
96 (void)user_data;
97 presses++;
98 pressed_index = index;
99 pressed_button = button;
100 pressed_x = x;
101 pressed_y = y;
102}
103
105static int wheel(int widget_id, int dz, int modifiers, void* user_data) {
106 (void)user_data;
107 wheels++;
108 wheel_mods = modifiers;
109 if (!(modifiers & ALLEGRO_KEYMOD_CTRL)) return 0;
110 zoom_cell += 10.0f * (float)dz;
111 n_gui_grid_set_cell_size(zoom_gui, widget_id, zoom_cell, zoom_cell + 10.0f, 10.0f);
112 return 1;
113}
114
116static void feed_key(N_GUI_CTX* gui, int type, int keycode) {
117 ALLEGRO_EVENT ev;
118
119 memset(&ev, 0, sizeof(ev));
120 ev.type = (ALLEGRO_EVENT_TYPE)type;
121 ev.keyboard.keycode = keycode;
123}
124
126static void feed(N_GUI_CTX* gui, int type, float x, float y, int button, int dz) {
127 ALLEGRO_EVENT ev;
128
129 memset(&ev, 0, sizeof(ev));
130 ev.type = (ALLEGRO_EVENT_TYPE)type;
131 ev.mouse.x = (int)x;
132 ev.mouse.y = (int)y;
133 ev.mouse.dx = (type == ALLEGRO_EVENT_MOUSE_AXES && dz == 0) ? 1 : 0;
134 ev.mouse.dz = dz;
135 ev.mouse.button = (unsigned int)button;
137}
138
140static void draw(N_GUI_CTX* gui) {
141 ALLEGRO_BITMAP* target = NULL;
142 int old = al_get_new_bitmap_flags();
143
144 al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
145 target = al_create_bitmap(400, 300);
146 al_set_new_bitmap_flags(old);
147 al_set_target_bitmap(target);
148 al_clear_to_color(al_map_rgb(0, 0, 0));
149 cells_drawn = 0;
151 al_set_target_bitmap(NULL);
152 al_destroy_bitmap(target);
153}
154
155int main(int argc, char** argv) {
157 size_t at = 0;
158 float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;
159
161 if (process_args(argc, argv) != 0) {
162 fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
163 return 2;
164 }
166 if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
167 n_log(LOG_ERR, "allegro init failed");
168 return 1;
169 }
170 ALLEGRO_FONT* font = al_create_builtin_font();
171 if (!font) return 1;
173 if (!gui) {
174 al_destroy_font(font);
175 return 1;
176 }
177 n_gui_set_display_size(gui, 400.0f, 300.0f);
178 int win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
181
182 memset(&cb, 0, sizeof(cb));
183 expect_true("a grid without draw_cell is refused", n_gui_grid_new(gui, win, &cb, NULL) < 0);
184 cb.draw_cell = draw_cell;
186 cb.visible = visible;
187 cb.press = press;
188 int grid = n_gui_grid_new(gui, win, &cb, NULL);
189 expect_true("a grid is made", grid >= 0);
190 n_gui_grid_set_cell_size(gui, grid, 50.0f, 60.0f, 10.0f);
191 n_gui_grid_set_rect(gui, grid, 10.0f, 10.0f, 300.0f, 200.0f, 12.0f);
192
193 /* empty */
194 draw(gui);
195 expect_true("an empty grid draws its background and no cell", backgrounds == 1 && cells_drawn == 0);
196 expect_true("and has no scrollbar", n_gui_grid_scrollbar_showing(gui, grid) == 0);
197 expect_true("and no cell anywhere", n_gui_grid_index_at(gui, grid, 30.0f, 30.0f, &at) == FALSE);
198
199 /* twenty cells: five across at 300 wide would be 290 tall, over 200, so
200 the bar shows; 288 wide then holds four across */
201 n_gui_grid_set_count(gui, grid, 20);
202 expect_true("rows that overflow show the scrollbar", n_gui_grid_scrollbar_showing(gui, grid) == 1);
203 expect_true("and the grid narrows for it", n_gui_grid_columns(gui, grid) == 4);
204 draw(gui);
205 expect_true("only the rows on screen are drawn", cells_drawn == 16);
206 expect_true("and the range to prepare is one row further", first_seen == 0 && last_seen == 20);
207
208 /* scrolling */
209 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, -1);
210 expect_true("a wheel notch scrolls one row", n_gui_grid_get_scroll(gui, grid) == 70.0f);
211 expect_true("and the bar follows", n_gui_scrollbar_get_pos(gui, n_gui_grid_scrollbar(gui, grid)) == 70.0);
212 n_gui_grid_scroll_to(gui, grid, 0);
213 expect_true("scrolling to the first cell goes back to the top", n_gui_grid_get_scroll(gui, grid) == 0.0f);
214 n_gui_grid_scroll_to(gui, grid, 19);
215 expect_true("scrolling to the last shows its whole row", n_gui_grid_get_scroll(gui, grid) == 160.0f);
216 n_gui_grid_set_scroll(gui, grid, 1000.0f);
217 expect_true("the view never goes past the end", n_gui_grid_get_scroll(gui, grid) == 160.0f);
218 n_gui_grid_set_scroll(gui, grid, 0.0f);
219
220 /* hit test: the grid is at 10,10, cells start one gap in */
221 expect_true("a point on a cell is that cell", n_gui_grid_index_at(gui, grid, 25.0f, 25.0f, &at) && at == 0);
222 expect_true("the gap after a cell is that cell", n_gui_grid_index_at(gui, grid, 75.0f, 25.0f, &at) && at == 0);
223 expect_true("the gap below a row is that row", n_gui_grid_index_at(gui, grid, 25.0f, 85.0f, &at) && at == 0);
224 expect_true("the right edge is the last column", n_gui_grid_index_at(gui, grid, 295.0f, 25.0f, &at) && at == 3);
225 expect_true("outside the grid is no cell", n_gui_grid_index_at(gui, grid, 5.0f, 25.0f, &at) == FALSE);
226 expect_true("cell 5 is where the layout says", n_gui_grid_cell_rect(gui, grid, 5, &x, &y, &w, &h) && x == 80.0f && y == 90.0f && w == 50.0f && h == 60.0f);
227 expect_true("a cell scrolled out of view says so", n_gui_grid_cell_rect(gui, grid, 19, NULL, NULL, NULL, NULL) == FALSE);
228
229 /* presses */
230 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 105.0f, 120.0f, 0, 0);
231 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 105.0f, 120.0f, 1, 0);
232 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 105.0f, 120.0f, 1, 0);
233 expect_true("a press on a cell is handed on with its index", presses == 1 && pressed_index == 5 && pressed_button == 1);
234 expect_true("and where it was, on screen", pressed_x == 105.0f && pressed_y == 120.0f);
235 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 105.0f, 120.0f, 2, 0);
236 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 105.0f, 120.0f, 2, 0);
237 expect_true("a right press too, as button 2", presses == 2 && pressed_button == 2);
238 n_gui_grid_set_count(gui, grid, 18);
239 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 245.0f, 190.0f, 0, 0);
240 n_gui_grid_scroll_to(gui, grid, 17);
241 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 245.0f, 170.0f, 1, 0);
242 feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 245.0f, 170.0f, 1, 0);
243 expect_true("a press past the last cell is not", presses == 2);
244
245 /* fewer cells, then hidden */
246 n_gui_grid_set_count(gui, grid, 3);
247 expect_true("rows that fit hide the scrollbar, go back to the top and widen again",
248 n_gui_grid_scrollbar_showing(gui, grid) == 0 && n_gui_grid_get_scroll(gui, grid) == 0.0f && n_gui_grid_columns(gui, grid) == 5);
249 n_gui_grid_set_visible(gui, grid, 0);
250 expect_true("a hidden grid has no cell anywhere", n_gui_grid_index_at(gui, grid, 25.0f, 25.0f, &at) == FALSE);
251 expect_true("an id that is not a grid answers nothing", n_gui_grid_columns(gui, win + 1000) == 0);
252
253 /* the wheel callback, and the modifiers the context follows */
254 memset(&cb, 0, sizeof(cb));
255 cb.draw_cell = draw_cell;
256 cb.wheel = wheel;
257 zoom_gui = gui;
258 int zoomed = n_gui_grid_new(gui, win, &cb, NULL);
259 expect_true("a grid with a wheel callback is made", zoomed >= 0);
260 n_gui_grid_set_cell_size(gui, zoomed, zoom_cell, zoom_cell + 10.0f, 10.0f);
261 n_gui_grid_set_rect(gui, zoomed, 10.0f, 10.0f, 300.0f, 200.0f, 12.0f);
262 n_gui_grid_set_count(gui, zoomed, 40);
263 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, -1);
264 expect_true("the plain wheel is offered to the callback with no modifier", wheels == 1 && wheel_mods == 0);
265 expect_true("and, refused, scrolls", n_gui_grid_get_scroll(gui, zoomed) == 70.0f);
266 feed_key(gui, ALLEGRO_EVENT_KEY_DOWN, ALLEGRO_KEY_RCTRL);
267 expect_true("a Ctrl pressed is held", n_gui_get_key_modifiers(gui) == ALLEGRO_KEYMOD_CTRL);
268 feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, 2);
269 expect_true("Ctrl+wheel reaches the callback with Ctrl", wheels == 2 && (wheel_mods & ALLEGRO_KEYMOD_CTRL));
270 expect_true("which takes it: the cells grow and nothing scrolls", zoom_cell == 70.0f && n_gui_grid_get_scroll(gui, zoomed) == 70.0f);
271 feed_key(gui, ALLEGRO_EVENT_KEY_DOWN, ALLEGRO_KEY_LCTRL);
272 feed_key(gui, ALLEGRO_EVENT_KEY_UP, ALLEGRO_KEY_RCTRL);
273 expect_true("one Ctrl let go while the other is held keeps Ctrl", n_gui_get_key_modifiers(gui) == ALLEGRO_KEYMOD_CTRL);
274 feed_key(gui, ALLEGRO_EVENT_DISPLAY_SWITCH_OUT, 0);
275 expect_true("losing the focus forgets every key", n_gui_get_key_modifiers(gui) == 0);
276
278 al_destroy_font(font);
279 if (failures) {
280 fprintf(stderr, "ex_gui_grid: %d failure(s)\n", failures);
281 return 1;
282 }
283 printf("ex_gui_grid: ok\n");
284 return 0;
285}
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_MOUSE_EVENT last
the last event the widget was told of, and how many
static N_GUI_CTX * gui
static void wheel(N_GUI_CTX *gui, int dz)
turn the mouse wheel over the display
static size_t last_seen
Definition ex_gui_grid.c:36
static void press(int widget_id, size_t index, int button, float x, float y, void *user_data)
a press on a cell
Definition ex_gui_grid.c:94
static int backgrounds
Definition ex_gui_grid.c:35
static float pressed_x
Definition ex_gui_grid.c:37
static int wheel_mods
Definition ex_gui_grid.c:39
static size_t pressed_index
Definition ex_gui_grid.c:36
static int cells_drawn
what the callbacks saw
Definition ex_gui_grid.c:35
static void draw(N_GUI_CTX *gui)
draw the whole context into a memory bitmap
static size_t first_seen
Definition ex_gui_grid.c:36
static float pressed_y
Definition ex_gui_grid.c:37
static int presses
Definition ex_gui_grid.c:35
static int pressed_button
Definition ex_gui_grid.c:35
static N_GUI_CTX * zoom_gui
the context the wheel callback resizes the grid in
Definition ex_gui_grid.c:42
static float zoom_cell
Definition ex_gui_grid.c:40
static void visible(int widget_id, size_t first, size_t last, void *user_data)
the range to prepare
Definition ex_gui_grid.c:86
static int wheels
what the wheel callback saw, and the cell size it keeps
Definition ex_gui_grid.c:39
static void draw_background(int widget_id, float x, float y, float w, float h, void *user_data)
draw behind
Definition ex_gui_grid.c:78
static void draw_cell(int widget_id, size_t index, float x, float y, float w, float h, void *user_data)
draw one cell
Definition ex_gui_grid.c:69
static void feed_key(N_GUI_CTX *gui, int type, int keycode)
feed one key event
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()
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
double n_gui_scrollbar_get_pos(N_GUI_CTX *ctx, int widget_id)
get the current scroll position of a scrollbar widget
Definition n_gui.c:4293
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
#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_draw(N_GUI_CTX *ctx)
draw all visible GUI windows and widgets
Definition n_gui.c:11735
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_open_window(N_GUI_CTX *ctx, int window_id)
Open (show) a window.
Definition n_gui.c:1436
int n_gui_get_key_modifiers(const N_GUI_CTX *ctx)
the modifier keys held down now, as the GUI has seen them
Definition n_gui.c:13281
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
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
#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
void(* draw_background)(int widget_id, float x, float y, float w, float h, void *user_data)
Draw behind the cells, over the whole grid, before any cell; with no cells, the only thing drawn.
Definition n_gui_grid.h:75
void(* press)(int widget_id, size_t index, int button, float x, float y, void *user_data)
A press of button (1 or 2) on cell index, at screen x, y.
Definition n_gui_grid.h:81
void(* draw_cell)(int widget_id, size_t index, float x, float y, float w, float h, void *user_data)
Draw cell index in the box at x, y, w by h, on screen.
Definition n_gui_grid.h:72
int(* wheel)(int widget_id, int dz, int modifiers, void *user_data)
A wheel turn of dz notches over the grid, positive away from the user, with the modifier keys held (A...
Definition n_gui_grid.h:87
void(* visible)(int widget_id, size_t first, size_t last, void *user_data)
Called after each draw with the cells drawn and one row beyond, first included and last excluded: wha...
Definition n_gui_grid.h:78
void n_gui_grid_set_rect(N_GUI_CTX *ctx, int widget_id, float x, float y, float w, float h, float bar_w)
Place the grid and its scrollbar in a rectangle of the window.
Definition n_gui_grid.c:321
void n_gui_grid_set_scroll(N_GUI_CTX *ctx, int widget_id, float scroll)
Set the scroll offset, clamped to the content.
Definition n_gui_grid.c:373
int n_gui_grid_new(N_GUI_CTX *ctx, int window_id, const N_GUI_GRID_CALLBACKS *callbacks, void *user_data)
Add a grid, and its scrollbar, to a window.
Definition n_gui_grid.c:281
int n_gui_grid_index_at(N_GUI_CTX *ctx, int widget_id, float x, float y, size_t *index)
Which cell is at a point on screen.
Definition n_gui_grid.c:400
void n_gui_grid_set_count(N_GUI_CTX *ctx, int widget_id, size_t count)
Set how many cells there are.
Definition n_gui_grid.c:343
int n_gui_grid_columns(N_GUI_CTX *ctx, int widget_id)
How many cells a row holds at the grid's width.
Definition n_gui_grid.c:361
int n_gui_grid_cell_rect(N_GUI_CTX *ctx, int widget_id, size_t index, float *x, float *y, float *w, float *h)
Where a cell is on screen, whether or not it is scrolled into view.
Definition n_gui_grid.c:411
void n_gui_grid_scroll_to(N_GUI_CTX *ctx, int widget_id, size_t index)
Scroll the least that brings a cell's whole row into view.
Definition n_gui_grid.c:383
int n_gui_grid_scrollbar_showing(N_GUI_CTX *ctx, int widget_id)
Whether the scrollbar is shown, which is whether the rows overflow.
Definition n_gui_grid.c:434
float n_gui_grid_get_scroll(N_GUI_CTX *ctx, int widget_id)
The scroll offset, in pixels from the top of the content.
Definition n_gui_grid.c:367
void n_gui_grid_set_cell_size(N_GUI_CTX *ctx, int widget_id, float cell_w, float cell_h, float gap)
Set the size of a cell and the gap around cells.
Definition n_gui_grid.c:333
void n_gui_grid_set_visible(N_GUI_CTX *ctx, int widget_id, int visible)
Show or hide the grid and its scrollbar together.
Definition n_gui_grid.c:351
int n_gui_grid_scrollbar(N_GUI_CTX *ctx, int widget_id)
The grid's scrollbar widget.
Definition n_gui_grid.c:428
What the grid asks of its caller.
Definition n_gui_grid.h:69
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
A virtualised grid of cells for N_GUI, scrolled vertically.
Generic log system.