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

examples/ex_gui_tree_order.c

Functions

Name
void visible_labels(N_GUI_TREE * tree, const char ** out, int max, int * count)
int main(int argc, char ** argv)
void expect_true(const char * label, int cond)
void expect_order(N_GUI_TREE * tree, const char *const * want, int n)

Attributes

Name
int failures

Defines

Name
ALLEGRO_UNSTABLE

Functions Documentation

function visible_labels

static void visible_labels(
    N_GUI_TREE * tree,
    const char ** out,
    int max,
    int * count
)

function main

int main(
    int argc,
    char ** argv
)

function expect_true

static void expect_true(
    const char * label,
    int cond
)

function expect_order

static void expect_order(
    N_GUI_TREE * tree,
    const char *const * want,
    int n
)

Attributes Documentation

variable failures

static int failures = 0;

Macros Documentation

define ALLEGRO_UNSTABLE

#define ALLEGRO_UNSTABLE 1

Source code

/*
 * Nilorea Library
 * Copyright (C) 2005-2026 Castagnier Mickael
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.
 *
 * SPDX-License-Identifier: Apache-2.0
 */


#define ALLEGRO_UNSTABLE 1

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

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

static int failures = 0;

static void expect_true(const char* label, int cond) {
    if (!cond) {
        n_log(LOG_ERR, "%s: condition false", label);
        failures++;
    }
}

/* What the tree is showing, top to bottom, as labels. The listbox holds the
   drawn text with its indent and its marker, so the node behind each row is
   read through visible_map instead. */
static void visible_labels(N_GUI_TREE* tree, const char** out, int max, int* count) {
    int i = 0;
    *count = 0;
    for (i = 0; i < tree->nb_visible && i < max; i++)
        out[(*count)++] = tree->nodes[tree->visible_map[i]].label;
}

static void expect_order(N_GUI_TREE* tree, const char* const* want, int n) {
    const char* got[32];
    int count = 0, i = 0;

    visible_labels(tree, got, 32, &count);
    if (count != n) {
        n_log(LOG_ERR, "expected %d rows, got %d", n, count);
        failures++;
        for (i = 0; i < count; i++)
            n_log(LOG_ERR, "  row %d: %s", i, got[i]);
        return;
    }
    for (i = 0; i < n; i++) {
        if (strcmp(got[i], want[i]) != 0) {
            n_log(LOG_ERR, "row %d: expected %s, got %s", i, want[i], got[i]);
            failures++;
        }
    }
}

int main(int argc, char** argv) {
    (void)argc;
    (void)argv;
    set_log_level(LOG_ERR);

    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) {
        n_log(LOG_ERR, "al_create_builtin_font failed");
        return 1;
    }
    N_GUI_CTX* gui = n_gui_new_ctx(font);
    if (!gui) {
        n_log(LOG_ERR, "n_gui_new_ctx failed");
        al_destroy_font(font);
        return 1;
    }
    n_gui_set_display_size(gui, 400.0f, 300.0f);

    {
        int win = n_gui_add_window(gui, "Panel", 0.0f, 0.0f, 300.0f, 260.0f);
        N_GUI_TREE* tree = n_gui_tree_create(gui, win, 0.0f, 0.0f, 200.0f, 240.0f, NULL, NULL);
        int home = -1, data = -1, music = -1, prog = -1, root = -1;

        expect_true("tree created", tree != NULL);
        if (!tree)
            return 1;

        /* Two roots and their children, added breadth first, which is what a
           directory listing hands you: one readdir gives every child of one
           folder and nothing below them. */
        home = n_gui_tree_add_node(tree, "home", -1, NULL);
        data = n_gui_tree_add_node(tree, "data", home, NULL);
        music = n_gui_tree_add_node(tree, "music", home, NULL);
        root = n_gui_tree_add_node(tree, "tmp", -1, NULL);
        expect_true("nodes added", tree->nb_nodes == 4 && root == 3);

        n_gui_tree_toggle_expand(tree, home);
        expect_true("a child knows its depth", tree->nodes[music].depth == 1);
        {
            static const char* const want[] = {"home", "data", "music", "tmp"};
            expect_order(tree, want, 4);
        }

        /* And now the case this file exists for: the folder is opened, so its
           own children are read and added. They go on the end of the node
           array, a long way after their parent, and they still have to be
           drawn directly under it. */
        prog = n_gui_tree_add_node(tree, "prog", data, NULL);
        n_gui_tree_add_node(tree, "photos", data, NULL);
        n_gui_tree_toggle_expand(tree, data);
        {
            static const char* const want[] = {"home", "data", "prog", "photos", "music",
                                               "tmp"};
            expect_order(tree, want, 6);
        }

        /* Two levels of it, because the bug this catches put every lazily
           read level at the bottom in the order it was read, which looks
           almost right until a second level is opened. */
        n_gui_tree_add_node(tree, "afterapro", prog, NULL);
        n_gui_tree_toggle_expand(tree, prog);
        {
            static const char* const want[] = {"home", "data", "prog", "afterapro",
                                               "photos", "music", "tmp"};
            expect_order(tree, want, 7);
        }

        /* A child of the second root, added last of all, belongs to it and
           not to the first one. */
        n_gui_tree_add_node(tree, "cache", root, NULL);
        n_gui_tree_toggle_expand(tree, root);
        {
            static const char* const want[] = {"home", "data", "prog", "afterapro",
                                               "photos", "music", "tmp", "cache"};
            expect_order(tree, want, 8);
        }

        /* Folding a branch hides everything under it, however far down the
           node array those children happen to live. */
        n_gui_tree_toggle_expand(tree, data);
        {
            static const char* const want[] = {"home", "data", "music", "tmp", "cache"};
            expect_order(tree, want, 5);
        }

        /* And the row the selection is on follows the node, not its index:
           selecting the second root and asking which row it is on has to
           answer the row it is drawn at. */
        n_gui_tree_set_selection(tree, root);
        expect_true("selection kept", n_gui_tree_get_selection(tree) == root);
        expect_true("selection is on the row it is drawn at",
                    tree->visible_map[n_gui_listbox_get_selected(gui, tree->listbox_id)] ==
                        root);

        n_gui_tree_free(&tree);
    }

    n_gui_destroy_ctx(&gui);
    al_destroy_font(font);

    if (failures)
        printf("ex_gui_tree_order: FAILED, %d check(s)\n", failures);
    else
        printf("ex_gui_tree_order: ok\n");
    return failures ? 1 : 0;
}