7 ex__gui__grid_8c
gullradriel edited this page 2026-10-12 07:59:13 +02:00

examples/ex_gui_grid.c

Functions

Name
int wheel(int widget_id, int dz, int modifiers, void * user_data)
Ctrl+wheel resizes the cells; the plain wheel is left to scroll.
void visible(int widget_id, size_t first, size_t last, void * user_data)
the range to prepare
int process_args(int argc, char ** argv)
read -V LOGLEVEL
void press(int widget_id, size_t index, int button, float x, float y, void * user_data)
a press on a cell
int main(int argc, char ** argv)
void feed_key(N_GUI_CTX * gui, int type, int keycode)
feed one key event
void feed(N_GUI_CTX * gui, int type, float x, float y, int button, int dz)
feed one mouse event
void expect_true(const char * what, int ok)
record one check
void draw_cell(int widget_id, size_t index, float x, float y, float w, float h, void * user_data)
draw one cell
void draw_background(int widget_id, float x, float y, float w, float h, void * user_data)
draw behind
void draw(N_GUI_CTX * gui)
draw the whole context into a memory bitmap

Attributes

Name
N_GUI_CTX * zoom_gui
the context the wheel callback resizes the grid in
float zoom_cell
int wheels
what the wheel callback saw, and the cell size it keeps
int wheel_mods
int presses
float pressed_y
float pressed_x
size_t pressed_index
int pressed_button
int log_level
size_t last_seen
size_t first_seen
int failures
int cells_drawn
what the callbacks saw
int backgrounds

Defines

Name
ALLEGRO_UNSTABLE

Functions Documentation

function wheel

static int wheel(
    int widget_id,
    int dz,
    int modifiers,
    void * user_data
)

Ctrl+wheel resizes the cells; the plain wheel is left to scroll.

function visible

static void visible(
    int widget_id,
    size_t first,
    size_t last,
    void * user_data
)

the range to prepare

function process_args

static int process_args(
    int argc,
    char ** argv
)

read -V LOGLEVEL

function press

static void press(
    int widget_id,
    size_t index,
    int button,
    float x,
    float y,
    void * user_data
)

a press on a cell

function main

int main(
    int argc,
    char ** argv
)

function feed_key

static void feed_key(
    N_GUI_CTX * gui,
    int type,
    int keycode
)

feed one key event

function feed

static void feed(
    N_GUI_CTX * gui,
    int type,
    float x,
    float y,
    int button,
    int dz
)

feed one mouse event

function expect_true

static void expect_true(
    const char * what,
    int ok
)

record one check

function draw_cell

static void draw_cell(
    int widget_id,
    size_t index,
    float x,
    float y,
    float w,
    float h,
    void * user_data
)

draw one cell

function draw_background

static void draw_background(
    int widget_id,
    float x,
    float y,
    float w,
    float h,
    void * user_data
)

draw behind

function draw

static void draw(
    N_GUI_CTX * gui
)

draw the whole context into a memory bitmap

Attributes Documentation

variable zoom_gui

static N_GUI_CTX * zoom_gui = NULL;

the context the wheel callback resizes the grid in

variable zoom_cell

static float zoom_cell = 50.0f;

variable wheels

static int wheels = 0;

what the wheel callback saw, and the cell size it keeps

variable wheel_mods

static int wheel_mods = 0;

variable presses

static int presses = 0;

variable pressed_y

static float pressed_y = 0.0f;

variable pressed_x

static float pressed_x = 0.0f;

variable pressed_index

static size_t pressed_index = 0;

variable pressed_button

static int pressed_button = 0;

variable log_level

static int log_level = 3;

variable last_seen

static size_t last_seen = 0;

variable first_seen

static size_t first_seen = 0;

variable failures

static int failures = 0;

variable cells_drawn

static int cells_drawn = 0;

what the callbacks saw

variable backgrounds

static int backgrounds = 0;

Macros Documentation

define ALLEGRO_UNSTABLE

#define ALLEGRO_UNSTABLE 1

Source code



#define ALLEGRO_UNSTABLE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <allegro5/allegro_primitives.h>

#include "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_gui.h"
#include "nilorea/n_gui_grid.h"

static int log_level = LOG_ERR;
static int failures = 0;

static int cells_drawn = 0, backgrounds = 0, presses = 0, pressed_button = 0;
static size_t first_seen = 0, last_seen = 0, pressed_index = 0;
static float pressed_x = 0.0f, pressed_y = 0.0f;
static int wheels = 0, wheel_mods = 0;
static float zoom_cell = 50.0f;
static N_GUI_CTX* zoom_gui = NULL;

static void expect_true(const char* what, int ok) {
    if (!ok) {
        n_log(LOG_ERR, "FAIL: %s", what);
        failures++;
    } else {
        n_log(LOG_INFO, "ok: %s", what);
    }
}

static int process_args(int argc, char** argv) {
    int opt = 0;

    while ((opt = getopt(argc, argv, "V:")) != -1) {
        if (opt == 'V') {
            log_level = atoi(optarg);
        } else {
            return -1;
        }
    }
    return 0;
}

static void draw_cell(int widget_id, size_t index, float x, float y, float w, float h, void* user_data) {
    (void)widget_id;
    (void)index;
    (void)user_data;
    cells_drawn++;
    al_draw_filled_rectangle(x, y, x + w, y + h, al_map_rgb(90, 90, 90));
}

static void draw_background(int widget_id, float x, float y, float w, float h, void* user_data) {
    (void)widget_id;
    (void)user_data;
    backgrounds++;
    al_draw_filled_rectangle(x, y, x + w, y + h, al_map_rgb(20, 20, 20));
}

static void visible(int widget_id, size_t first, size_t last, void* user_data) {
    (void)widget_id;
    (void)user_data;
    first_seen = first;
    last_seen = last;
}

static void press(int widget_id, size_t index, int button, float x, float y, void* user_data) {
    (void)widget_id;
    (void)user_data;
    presses++;
    pressed_index = index;
    pressed_button = button;
    pressed_x = x;
    pressed_y = y;
}

static int wheel(int widget_id, int dz, int modifiers, void* user_data) {
    (void)user_data;
    wheels++;
    wheel_mods = modifiers;
    if (!(modifiers & ALLEGRO_KEYMOD_CTRL)) return 0;
    zoom_cell += 10.0f * (float)dz;
    n_gui_grid_set_cell_size(zoom_gui, widget_id, zoom_cell, zoom_cell + 10.0f, 10.0f);
    return 1;
}

static void feed_key(N_GUI_CTX* gui, int type, int keycode) {
    ALLEGRO_EVENT ev;

    memset(&ev, 0, sizeof(ev));
    ev.type = (ALLEGRO_EVENT_TYPE)type;
    ev.keyboard.keycode = keycode;
    n_gui_process_event(gui, ev);
}

static void feed(N_GUI_CTX* gui, int type, float x, float y, int button, int dz) {
    ALLEGRO_EVENT ev;

    memset(&ev, 0, sizeof(ev));
    ev.type = (ALLEGRO_EVENT_TYPE)type;
    ev.mouse.x = (int)x;
    ev.mouse.y = (int)y;
    ev.mouse.dx = (type == ALLEGRO_EVENT_MOUSE_AXES && dz == 0) ? 1 : 0;
    ev.mouse.dz = dz;
    ev.mouse.button = (unsigned int)button;
    n_gui_process_event(gui, ev);
}

static void draw(N_GUI_CTX* gui) {
    ALLEGRO_BITMAP* target = NULL;
    int old = al_get_new_bitmap_flags();

    al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
    target = al_create_bitmap(400, 300);
    al_set_new_bitmap_flags(old);
    al_set_target_bitmap(target);
    al_clear_to_color(al_map_rgb(0, 0, 0));
    cells_drawn = 0;
    n_gui_draw(gui);
    al_set_target_bitmap(NULL);
    al_destroy_bitmap(target);
}

int main(int argc, char** argv) {
    N_GUI_GRID_CALLBACKS cb;
    size_t at = 0;
    float x = 0.0f, y = 0.0f, w = 0.0f, h = 0.0f;

    set_log_level(LOG_ERR);
    if (process_args(argc, argv) != 0) {
        fprintf(stderr, "Usage: %s [-V LOG_LEVEL]\n", argv[0]);
        return 2;
    }
    set_log_level(log_level);
    if (!al_init() || !al_init_font_addon() || !al_init_primitives_addon()) {
        n_log(LOG_ERR, "allegro init failed");
        return 1;
    }
    ALLEGRO_FONT* font = al_create_builtin_font();
    if (!font) return 1;
    N_GUI_CTX* gui = n_gui_new_ctx(font);
    if (!gui) {
        al_destroy_font(font);
        return 1;
    }
    n_gui_set_display_size(gui, 400.0f, 300.0f);
    int win = n_gui_add_window(gui, "W", 0.0f, 0.0f, 400.0f, 300.0f);
    n_gui_window_set_flags(gui, win, N_GUI_WIN_FRAMELESS);
    n_gui_open_window(gui, win);

    memset(&cb, 0, sizeof(cb));
    expect_true("a grid without draw_cell is refused", n_gui_grid_new(gui, win, &cb, NULL) < 0);
    cb.draw_cell = draw_cell;
    cb.draw_background = draw_background;
    cb.visible = visible;
    cb.press = press;
    int grid = n_gui_grid_new(gui, win, &cb, NULL);
    expect_true("a grid is made", grid >= 0);
    n_gui_grid_set_cell_size(gui, grid, 50.0f, 60.0f, 10.0f);
    n_gui_grid_set_rect(gui, grid, 10.0f, 10.0f, 300.0f, 200.0f, 12.0f);

    /* empty */
    draw(gui);
    expect_true("an empty grid draws its background and no cell", backgrounds == 1 && cells_drawn == 0);
    expect_true("and has no scrollbar", n_gui_grid_scrollbar_showing(gui, grid) == 0);
    expect_true("and no cell anywhere", n_gui_grid_index_at(gui, grid, 30.0f, 30.0f, &at) == FALSE);

    /* twenty cells: five across at 300 wide would be 290 tall, over 200, so
       the bar shows; 288 wide then holds four across */
    n_gui_grid_set_count(gui, grid, 20);
    expect_true("rows that overflow show the scrollbar", n_gui_grid_scrollbar_showing(gui, grid) == 1);
    expect_true("and the grid narrows for it", n_gui_grid_columns(gui, grid) == 4);
    draw(gui);
    expect_true("only the rows on screen are drawn", cells_drawn == 16);
    expect_true("and the range to prepare is one row further", first_seen == 0 && last_seen == 20);

    /* scrolling */
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, -1);
    expect_true("a wheel notch scrolls one row", n_gui_grid_get_scroll(gui, grid) == 70.0f);
    expect_true("and the bar follows", n_gui_scrollbar_get_pos(gui, n_gui_grid_scrollbar(gui, grid)) == 70.0);
    n_gui_grid_scroll_to(gui, grid, 0);
    expect_true("scrolling to the first cell goes back to the top", n_gui_grid_get_scroll(gui, grid) == 0.0f);
    n_gui_grid_scroll_to(gui, grid, 19);
    expect_true("scrolling to the last shows its whole row", n_gui_grid_get_scroll(gui, grid) == 160.0f);
    n_gui_grid_set_scroll(gui, grid, 1000.0f);
    expect_true("the view never goes past the end", n_gui_grid_get_scroll(gui, grid) == 160.0f);
    n_gui_grid_set_scroll(gui, grid, 0.0f);

    /* hit test: the grid is at 10,10, cells start one gap in */
    expect_true("a point on a cell is that cell", n_gui_grid_index_at(gui, grid, 25.0f, 25.0f, &at) && at == 0);
    expect_true("the gap after a cell is that cell", n_gui_grid_index_at(gui, grid, 75.0f, 25.0f, &at) && at == 0);
    expect_true("the gap below a row is that row", n_gui_grid_index_at(gui, grid, 25.0f, 85.0f, &at) && at == 0);
    expect_true("the right edge is the last column", n_gui_grid_index_at(gui, grid, 295.0f, 25.0f, &at) && at == 3);
    expect_true("outside the grid is no cell", n_gui_grid_index_at(gui, grid, 5.0f, 25.0f, &at) == FALSE);
    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);
    expect_true("a cell scrolled out of view says so", n_gui_grid_cell_rect(gui, grid, 19, NULL, NULL, NULL, NULL) == FALSE);

    /* presses */
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 105.0f, 120.0f, 0, 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 105.0f, 120.0f, 1, 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 105.0f, 120.0f, 1, 0);
    expect_true("a press on a cell is handed on with its index", presses == 1 && pressed_index == 5 && pressed_button == 1);
    expect_true("and where it was, on screen", pressed_x == 105.0f && pressed_y == 120.0f);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 105.0f, 120.0f, 2, 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 105.0f, 120.0f, 2, 0);
    expect_true("a right press too, as button 2", presses == 2 && pressed_button == 2);
    n_gui_grid_set_count(gui, grid, 18);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 245.0f, 190.0f, 0, 0);
    n_gui_grid_scroll_to(gui, grid, 17);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_DOWN, 245.0f, 170.0f, 1, 0);
    feed(gui, ALLEGRO_EVENT_MOUSE_BUTTON_UP, 245.0f, 170.0f, 1, 0);
    expect_true("a press past the last cell is not", presses == 2);

    /* fewer cells, then hidden */
    n_gui_grid_set_count(gui, grid, 3);
    expect_true("rows that fit hide the scrollbar, go back to the top and widen again",
                n_gui_grid_scrollbar_showing(gui, grid) == 0 && n_gui_grid_get_scroll(gui, grid) == 0.0f && n_gui_grid_columns(gui, grid) == 5);
    n_gui_grid_set_visible(gui, grid, 0);
    expect_true("a hidden grid has no cell anywhere", n_gui_grid_index_at(gui, grid, 25.0f, 25.0f, &at) == FALSE);
    expect_true("an id that is not a grid answers nothing", n_gui_grid_columns(gui, win + 1000) == 0);

    /* the wheel callback, and the modifiers the context follows */
    memset(&cb, 0, sizeof(cb));
    cb.draw_cell = draw_cell;
    cb.wheel = wheel;
    zoom_gui = gui;
    int zoomed = n_gui_grid_new(gui, win, &cb, NULL);
    expect_true("a grid with a wheel callback is made", zoomed >= 0);
    n_gui_grid_set_cell_size(gui, zoomed, zoom_cell, zoom_cell + 10.0f, 10.0f);
    n_gui_grid_set_rect(gui, zoomed, 10.0f, 10.0f, 300.0f, 200.0f, 12.0f);
    n_gui_grid_set_count(gui, zoomed, 40);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, -1);
    expect_true("the plain wheel is offered to the callback with no modifier", wheels == 1 && wheel_mods == 0);
    expect_true("and, refused, scrolls", n_gui_grid_get_scroll(gui, zoomed) == 70.0f);
    feed_key(gui, ALLEGRO_EVENT_KEY_DOWN, ALLEGRO_KEY_RCTRL);
    expect_true("a Ctrl pressed is held", n_gui_get_key_modifiers(gui) == ALLEGRO_KEYMOD_CTRL);
    feed(gui, ALLEGRO_EVENT_MOUSE_AXES, 50.0f, 50.0f, 0, 2);
    expect_true("Ctrl+wheel reaches the callback with Ctrl", wheels == 2 && (wheel_mods & ALLEGRO_KEYMOD_CTRL));
    expect_true("which takes it: the cells grow and nothing scrolls", zoom_cell == 70.0f && n_gui_grid_get_scroll(gui, zoomed) == 70.0f);
    feed_key(gui, ALLEGRO_EVENT_KEY_DOWN, ALLEGRO_KEY_LCTRL);
    feed_key(gui, ALLEGRO_EVENT_KEY_UP, ALLEGRO_KEY_RCTRL);
    expect_true("one Ctrl let go while the other is held keeps Ctrl", n_gui_get_key_modifiers(gui) == ALLEGRO_KEYMOD_CTRL);
    feed_key(gui, ALLEGRO_EVENT_DISPLAY_SWITCH_OUT, 0);
    expect_true("losing the focus forgets every key", n_gui_get_key_modifiers(gui) == 0);

    n_gui_destroy_ctx(&gui);
    al_destroy_font(font);
    if (failures) {
        fprintf(stderr, "ex_gui_grid: %d failure(s)\n", failures);
        return 1;
    }
    printf("ex_gui_grid: ok\n");
    return 0;
}