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

n_gui: a round GUI rounds the widgets that have no shape of their own

n_gui: a round GUI rounds the widgets that have no shape of their own

Author
Castagnier Mickael
Version
1.0
Date
05/10/2026

Buttons and comboboxes have a shape; text areas, checkboxes, a frameless window's body and the combobox dropdown did not, and stayed square under n_gui_set_shape_mode(N_GUI_SHAPE_ROUNDED): a round GUI still had square fields, square check boxes, and card windows whose square fill showed its corners outside their rounded outline. They now round when, and only when, the context asks for a round GUI.

Headless: the window is drawn into a memory bitmap and read back. Each shape is painted in a colour of its own; the check finds that colour's bounding box and looks at the box's own corner pixel, which is the colour when the shape is square and is not when it is rounded. Every case runs twice, in the default shape mode (where it must stay square: no existing program may change under it) and in N_GUI_SHAPE_ROUNDED.

#define ALLEGRO_UNSTABLE 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "nilorea/n_log.h"
#include "nilorea/n_gui.h"
#include <allegro5/allegro_primitives.h>
static int log_level = LOG_ERR;
static int failures = 0;
static void expect_true(const char* what, int ok) {
if (!ok) {
n_log(LOG_ERR, "FAIL: %s", what);
} 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 N_GUI_THEME flat(unsigned char r, unsigned char g, unsigned char b) {
ALLEGRO_COLOR c = al_map_rgb(r, g, b);
return n_gui_make_theme(c, c, c, c, c, c, al_map_rgb(0, 0, 0), al_map_rgb(0, 0, 0), al_map_rgb(0, 0, 0), 1.0f, 10.0f, 10.0f);
}
static int is_rgb(ALLEGRO_BITMAP* bmp, int x, int y, unsigned char r, unsigned char g, unsigned char b) {
unsigned char pr = 0, pg = 0, pb = 0;
al_unmap_rgb(al_get_pixel(bmp, x, y), &pr, &pg, &pb);
return pr == r && pg == g && pb == b;
}
static int corner_is_colour(ALLEGRO_BITMAP* bmp, int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b) {
int minx = x1, miny = y1, x, y;
for (y = y0; y < y1; y++)
for (x = x0; x < x1; x++)
if (is_rgb(bmp, x, y, r, g, b)) {
if (x < minx) minx = x;
if (y < miny) miny = y;
}
if (minx == x1 || miny == y1) return -1;
/* one pixel in from the box's corner: an outline drawn on the shape's
* edge reaches half a pixel past a square fill, so the exact corner can
* be neither; one pixel in is inside a square shape and, at the radius
* of 10 used here (12.7 px from the arc's centre), outside a rounded one */
return is_rgb(bmp, minx + 1, miny + 1, r, g, b);
}
static void run(ALLEGRO_FONT* font, int mode, const char* name) {
char what[128];
int rounded = (mode == N_GUI_SHAPE_ROUNDED);
ALLEGRO_BITMAP* target = NULL;
int old = al_get_new_bitmap_flags();
int win, ta, cb, r;
if (!gui) {
expect_true("context", 0);
return;
}
n_gui_set_display_size(gui, 400.0f, 300.0f);
n_gui_set_default_theme(gui, flat(0, 160, 0)); /* the window: green */
win = n_gui_add_window(gui, "", 50.0f, 50.0f, 300.0f, 200.0f);
ta = n_gui_add_textarea(gui, win, 20.0f, 20.0f, 200.0f, 40.0f, 0, 32, NULL, NULL);
n_gui_set_widget_theme(gui, ta, flat(200, 0, 0)); /* red */
cb = n_gui_add_checkbox(gui, win, "", 20.0f, 100.0f, 200.0f, 40.0f, 0, NULL, NULL);
n_gui_set_widget_theme(gui, cb, flat(220, 220, 0)); /* yellow */
al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);
target = al_create_bitmap(400, 300);
al_set_new_bitmap_flags(old);
if (!target) {
expect_true("memory bitmap", 0);
return;
}
al_set_target_bitmap(target);
al_clear_to_color(al_map_rgb(0, 0, 160)); /* the screen: blue */
/* the window body: its own top-left corner */
r = corner_is_colour(target, 40, 40, 360, 260, 0, 160, 0);
snprintf(what, sizeof(what), "%s: window body %s", name, rounded ? "rounded" : "square");
expect_true(what, r == (rounded ? 0 : 1));
/* the text area, inside the window */
r = corner_is_colour(target, 60, 60, 300, 120, 200, 0, 0);
snprintf(what, sizeof(what), "%s: text area %s", name, rounded ? "rounded" : "square");
expect_true(what, r == (rounded ? 0 : 1));
/* the checkbox's box */
r = corner_is_colour(target, 60, 140, 300, 200, 220, 220, 0);
snprintf(what, sizeof(what), "%s: checkbox %s", name, rounded ? "rounded" : "square");
expect_true(what, r == (rounded ? 0 : 1));
al_set_target_bitmap(NULL);
al_destroy_bitmap(target);
}
int main(int argc, char** argv) {
if (process_args(argc, argv) != 0) {
fprintf(stderr, "usage: %s [-V LOG_LEVEL]\n", argv[0]);
return 1;
}
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;
run(font, -1, "default mode");
run(font, N_GUI_SHAPE_RECT, "RECT mode");
run(font, N_GUI_SHAPE_ROUNDED, "ROUNDED mode");
al_destroy_font(font);
if (failures) {
fprintf(stderr, "ex_gui_round: %d failure(s)\n", failures);
return 1;
}
printf("ex_gui_round: all checks passed\n");
return 0;
}
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 float flat(const float *xs, const float *ys, int count, float x, void *user_data)
a caller's own curve: flat
static N_GUI_CTX * gui
static int mode
static int is_rgb(ALLEGRO_BITMAP *bmp, int x, int y, unsigned char r, unsigned char g, unsigned char b)
is pixel (x, y) exactly this colour?
static int corner_is_colour(ALLEGRO_BITMAP *bmp, int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b)
whether the top-left corner of a colour's bounding box (searched in a region) is that colour: 1 squar...
int run
Definition ex_kafka.c:56
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
void n_gui_set_widget_theme(N_GUI_CTX *ctx, int widget_id, N_GUI_THEME theme)
Override the theme of a specific widget.
Definition n_gui.c:3795
#define N_GUI_WIN_FIXED_POSITION
disable window dragging (default:enable)
Definition n_gui.h:274
#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_set_shape_mode(N_GUI_CTX *ctx, int shape_mode)
Set the global widget shape (round or square GUI).
Definition n_gui.c:12361
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_set_default_theme(N_GUI_CTX *ctx, N_GUI_THEME theme)
Set the default theme on a context and sync scrollbar style colors.
Definition n_gui.c:919
#define N_GUI_SHAPE_ROUNDED
rounded rectangle shape
Definition n_gui.h:172
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
int n_gui_add_textarea(N_GUI_CTX *ctx, int window_id, float x, float y, float w, float h, int multiline, size_t char_limit, void(*on_change)(int, const char *, void *), void *user_data)
Add a text area widget.
Definition n_gui.c:3162
int n_gui_add_checkbox(N_GUI_CTX *ctx, int window_id, const char *label, float x, float y, float w, float h, int initial_checked, void(*on_toggle)(int, int, void *), void *user_data)
Add a checkbox widget.
Definition n_gui.c:3207
#define N_GUI_SHAPE_RECT
rectangle shape (default)
Definition n_gui.h:170
N_GUI_THEME n_gui_make_theme(ALLEGRO_COLOR bg, ALLEGRO_COLOR bg_hover, ALLEGRO_COLOR bg_active, ALLEGRO_COLOR border, ALLEGRO_COLOR border_hover, ALLEGRO_COLOR border_active, ALLEGRO_COLOR text, ALLEGRO_COLOR text_hover, ALLEGRO_COLOR text_active, float border_thickness, float corner_rx, float corner_ry)
Create a custom theme with explicit colours.
Definition n_gui.c:825
The top-level GUI context that holds all windows.
Definition n_gui.h:1669
Color theme for a widget.
Definition n_gui.h:384
#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
Common headers and low-level functions & define.
GUI system: buttons, sliders, text areas, checkboxes, scrollbars, dropdown menus, windows.
Generic log system.