6 ex__gui__tree__lazy_8c
gullradriel edited this page 2026-10-09 00:55:18 +02:00

examples/ex_gui_tree_lazy.c

Functions

Name
const char * row(N_GUI_TREE * tree, int index)
the text of a visible row, "" past the end
int process_args(int argc, char ** argv)
parse -V LOG_LEVEL
void on_expand(int node, void * user_data)
the expand callback: a "folder" gets two subfolders, both themselves openable, except the one that stands for an empty folder
int main(int argc, char ** argv)
void expect_true(const char * what, int ok)
record one check

Attributes

Name
int log_level
int last_filled
the node the callback last filled
int fills
how many times the expand callback ran, and for which node last
int failures
int empty_node
nodes that stand for an empty folder: the callback adds nothing to them

Defines

Name
ALLEGRO_UNSTABLE

Functions Documentation

function row

static const char * row(
    N_GUI_TREE * tree,
    int index
)

the text of a visible row, "" past the end

function process_args

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

parse -V LOG_LEVEL

function on_expand

static void on_expand(
    int node,
    void * user_data
)

the expand callback: a "folder" gets two subfolders, both themselves openable, except the one that stands for an empty folder

function main

int main(
    int argc,
    char ** argv
)

function expect_true

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

record one check

Attributes Documentation

variable log_level

static int log_level = 3;

variable last_filled

static int last_filled = -1;

the node the callback last filled

variable fills

static int fills = 0;

how many times the expand callback ran, and for which node last

variable failures

static int failures = 0;

variable empty_node

static int empty_node = -1;

nodes that stand for an empty folder: the callback adds nothing to them

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 "nilorea/n_common.h"
#include "nilorea/n_log.h"
#include "nilorea/n_gui.h"

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

static int fills = 0;
static int last_filled = -1;
static int empty_node = -1;

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 on_expand(int node, void* user_data) {
    N_GUI_TREE* tree = (N_GUI_TREE*)user_data;
    int a = -1, b = -1;

    fills++;
    last_filled = node;
    if (node == empty_node) return;
    a = n_gui_tree_add_node(tree, "photos", node, NULL);
    b = n_gui_tree_add_node(tree, "empty", node, NULL);
    n_gui_tree_set_expandable(tree, a, 1);
    n_gui_tree_set_expandable(tree, b, 1);
    if (empty_node < 0) empty_node = b;
}

static const char* row(N_GUI_TREE* tree, int index) {
    return n_gui_listbox_get_item_text(tree->ctx, tree->listbox_id, index);
}

int main(int argc, char** argv) {
    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()) {
        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, "Folders", 0.0f, 0.0f, 300.0f, 200.0f);
    N_GUI_TREE* tree = n_gui_tree_create(gui, win, 0.0f, 0.0f, 200.0f, 160.0f, NULL, NULL);
    if (!tree) return 1;
    n_gui_tree_set_expand_callback(tree, on_expand, tree);

    int home = n_gui_tree_add_node(tree, "home", -1, NULL);
    expect_true("a node with no children has no expander", strncmp(row(tree, 0), "  ", 2) == 0);
    n_gui_tree_set_expandable(tree, home, 1);
    expect_true("an expandable node has one before it has children", strncmp(row(tree, 0), "> ", 2) == 0);
    expect_true("and nothing has been read yet", fills == 0 && tree->nb_nodes == 1);

    n_gui_tree_toggle_expand(tree, home);
    expect_true("opening it fills it, once", fills == 1 && last_filled == home && tree->nb_nodes == 3);
    expect_true("and shows what was added, in the same rebuild",
                tree->nb_visible == 3 && strncmp(row(tree, 0), "v ", 2) == 0);

    n_gui_tree_toggle_expand(tree, home);
    expect_true("closing it hides the children", tree->nb_visible == 1);
    n_gui_tree_toggle_expand(tree, home);
    expect_true("reopening it does not fill it again", fills == 1 && tree->nb_nodes == 3 && tree->nb_visible == 3);

    /* An empty folder: the callback runs and adds nothing. */
    n_gui_tree_expand(tree, empty_node);
    expect_true("opening an empty one asks once", fills == 2 && last_filled == empty_node);
    expect_true("and it loses its expander and stays shut",
                !tree->nodes[empty_node].expanded && !tree->nodes[empty_node].expandable);
    n_gui_tree_toggle_expand(tree, empty_node);
    expect_true("after which it cannot be opened again", fills == 2);

    /* Opening from code, as a caller revealing a path does. */
    int photos = 1;
    n_gui_tree_expand(tree, photos);
    expect_true("n_gui_tree_expand fills and opens", fills == 3 && tree->nodes[photos].expanded);
    n_gui_tree_expand(tree, photos);
    expect_true("and on an open node does nothing", fills == 3 && tree->nodes[photos].expanded);

    /* A refresh: the view saved, the tree rebuilt from nothing, the view put
     * back. Only the top node exists after the rebuild, so the state has to
     * fill each expanded level before the next one can be found. */
    N_GUI_TREE_STATE state;
    memset(&state, 0, sizeof(state));
    expect_true("the view is saved", n_gui_tree_state_save(tree, &state) == 0);
    n_gui_tree_clear(tree);
    home = n_gui_tree_add_node(tree, "home", -1, NULL);
    n_gui_tree_set_expandable(tree, home, 1);
    n_gui_tree_state_apply(tree, &state);
    n_gui_tree_state_free(&state);
    expect_true("both expanded levels come back, each filled once more",
                fills == 5 && tree->nodes[home].expanded && tree->nodes[photos].expanded &&
                    tree->nb_nodes == 5);

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