Nilorea Library
C utilities for networking, threading, graphics
Loading...
Searching...
No Matches
ex_gui_tree_order.c

Tree view row-order regression test (headless): a child added after its parent's siblings is drawn under its parent, not at the bottom of the tree.

Tree view row-order regression test (headless): a child added after its parent's siblings is drawn under its parent, not at the bottom of the tree. This is what a tree filled on demand does, and the only order in which a file browser can be built.

Author
Castagnier Mickael
Version
1.0
/*
* 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_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);
}
}
/* 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);
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]);
}
}
}
int main(int argc, char** argv) {
(void)argc;
(void)argv;
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;
}
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);
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);
{
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);
{
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);
{
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. */
{
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. */
expect_true("selection kept", n_gui_tree_get_selection(tree) == root);
expect_true("selection is on the row it is drawn at",
root);
}
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;
}
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 N_GUI_CTX * gui
static void expect_order(N_GUI_TREE *tree, const char *const *want, int n)
static void visible_labels(N_GUI_TREE *tree, const char **out, int max, int *count)
FONT * font
the default 8x8 font, valid after allegro_init()
N_GUI_TREE_NODE nodes[2048]
node storage
Definition n_gui.h:3497
int listbox_id
listbox widget ID
Definition n_gui.h:3495
char label[128]
display text
Definition n_gui.h:3468
int visible_map[2048]
visible row to node index
Definition n_gui.h:3499
int nb_nodes
number of nodes
Definition n_gui.h:3498
int nb_visible
number of visible rows
Definition n_gui.h:3500
int depth
depth in tree (0 = root)
Definition n_gui.h:3471
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_listbox_get_selected(N_GUI_CTX *ctx, int widget_id)
get the index of the first selected item in a listbox
Definition n_gui.c:4455
void n_gui_tree_set_selection(N_GUI_TREE *tree, int node_index)
programmatically select a node; ancestors are expanded so the node is visible, and the listbox scroll...
Definition n_gui.c:17764
void n_gui_tree_free(N_GUI_TREE **tree)
free a tree view (does not destroy the N_GUI listbox)
Definition n_gui.c:17881
int n_gui_tree_get_selection(N_GUI_TREE *tree)
get the index of the currently selected node, or -1 when nothing is selected or the selection points ...
Definition n_gui.c:17757
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
N_GUI_TREE * n_gui_tree_create(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, void(*on_select)(int, void *), void *user_data)
create a tree view in an existing window
Definition n_gui.c:17598
void n_gui_tree_toggle_expand(N_GUI_TREE *tree, int node_index)
toggle expand/collapse of a node.
Definition n_gui.c:17819
void n_gui_destroy_ctx(N_GUI_CTX **ctx)
Destroy a GUI context and all its windows/widgets.
Definition n_gui.c:1209
int n_gui_tree_add_node(N_GUI_TREE *tree, const char *label, int parent_index, void *user_data)
add a node to the tree
Definition n_gui.c:17665
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
tree view built from an N_GUI listbox
Definition n_gui.h:3493
#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
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.