Browse Source

version 1.0.0, I guess

master 1.0.0
Lephenixnoir 3 months ago
Signed by: Lephenixnoir GPG Key ID: 1BBA026E13FC0495
38 changed files with 4181 additions and 0 deletions
  1. +10
  2. +51
  3. +24
  4. +10
  5. +8
  6. BIN
  7. BIN
  8. +19
  9. +33
  10. +87
  11. +3
  12. +163
  13. +25
  14. +10
  15. +62
  16. +50
  17. +104
  18. +91
  19. +134
  20. +118
  21. +65
  22. +105
  23. +158
  24. +370
  25. +49
  26. +179
  27. +351
  28. +360
  29. +349
  30. +17
  31. +31
  32. +66
  33. +55
  34. +203
  35. +677
  36. +56
  37. +23
  38. +65

+ 10
- 0
.gitignore View File

@ -0,0 +1,10 @@
# Build files
# GiteaPC support
# Developer's files

+ 51
- 0
CMakeLists.txt View File

@ -0,0 +1,51 @@
# Just UI
cmake_minimum_required(VERSION 3.16)
find_package(Gint 2.1 REQUIRED)
configure_file(include/justui/ include/justui/config.h)
fxconv_declare_assets(${ASSETS_fx} ${ASSETS_cg})
set(NAME "justui-${FXSDK_PLATFORM}")
add_library(${NAME} STATIC
target_compile_options(${NAME} PUBLIC
-Wall -Wextra -std=c11 -Os)
target_include_directories(${NAME} PUBLIC
Gint::Gint -lopenlibm)
install(TARGETS ${NAME}
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/justui/config.h"
install(FILES cmake/FindJustUI.cmake

+ 24
- 0 View File

@ -0,0 +1,24 @@
# Just User Interfaces
JustUI is a "small" library to make GUIs on fx-9860G and fx-CG 50 with gint
(and I mean small by GUI library standards). I've built it to improve user
interfaces in [gintctl](,
but it's still documented enough that you can try it if you need GUIs.
Features include:
* Widget trees with parent/children ownership
* Basic `jwidget` and derived types (`jlabel`, `jinput`, etc.)
* A good layout system to determine the size and position of widgets
* Low-effort custom widgets (`jpainted`) and completely custom widget types
* Decent keyboard focus and event system
Screenshots: TODO
* [Widget types](doc/
* [Widget hierarchy](doc/
* [Space distribution and layout](doc/
* [Scenes and events](doc/ (TODO)
* Everything else is explained in the headers.

+ 10
- 0
TODO View File

@ -0,0 +1,10 @@
* Implement grid layout
* Other useful widgets
* Event subscriptions
* Simplify jlabel code using dfont_default()
Box layout:
* align-items (don't always center along the cross axis)
* justify-content (distribute empty space along the main axis)

+ 8
- 0
assets/fxconv-metadata.txt View File

@ -0,0 +1,8 @@
name: j_img_input_modes
type: bopti-image
name: j_img_input_modes
type: bopti-image
profile: p4

assets/input-modes-cg.png View File

Before After
Width: 77  |  Height: 10  |  Size: 325 B

assets/input-modes-fx.png View File

Before After
Width: 59  |  Height: 8  |  Size: 194 B

+ 19
- 0
cmake/FindJustUI.cmake View File

@ -0,0 +1,19 @@
find_package(Gint 2.1 REQUIRED)
find_simple_library("libjustui-${FXSDK_PLATFORM}.a" include/justui/config.h
add_library(JustUI::JustUI UNKNOWN IMPORTED)
set_target_properties(JustUI::JustUI PROPERTIES

+ 33
- 0
doc/ View File

@ -0,0 +1,33 @@
# JustUI: Widget hierarchy
## The tree hierarchy
Every widget (either of type `jwidget` or of a structure type that begins with
a `jwidget`) has a parent. This relationship forms a tree. The widgets that
have a common parent *w* are called the children of *w*.
The motivation for the widget hierarchy is to have groups of widgets behave as
one. A complex UI element can have many widgets to implement its complex
interface, but group them together as children of a parent and expose only the
parent. This is an essential tool to build complex interfaces out of smaller
When a widget is created, for instance with `jwidget_create()` or
`jlabel_create()`, its parent is specified as the last parameter.
In the tree one of the widgets is the ancestor of all the others, and is called
the *root* of the scene. In JustUI the root of the tree is normally a `jscene`,
because that is how event handling and keyboard input are managed.
## Managing ownership
Every widget *owns* its children with regards to memory allocation. This means
that when a widget is destroyed, its children are destroyed along with it.
Thus, even though the user program performs a function call for each widget to
allocate in a scene, a single call freeing the root will destroy all of them.
Whenever children are moved around, they change ownership. This is important
because we always want the parent to outlive the children. Specifically, if a
source widget holds a pointer of reference to a target widget, then the source
should make sure that either the target is one of its parents, or it is
informed when the target is destroyed.

+ 87
- 0
doc/ View File

@ -0,0 +1,87 @@
# JustUI: Space distribution and layout
Layout is the process through which space is allocated to widgets, which are
then sized and positioned.
## Content widgets and containers
Widgets in a scene usually fulfill one of two roles:
* **Content widgets** such as labels, input fields, or menu browsers, display
the interface and receive events. They often have no children, and do their
own rendering.
* **Containers** such as rows and columns, stacks, or grids, organize other
widgets so they align nicely and don't overlap. The widgets they organize are
their children; they themselves often perform no rendering.
JustUI does not enforce this separation, and a single widget can both handle
contents and organize children. But the features are usually designed with one
of the two roles in mind.
## Layouts
Layouts are parameters that can be attached to widgets to automatically size
and position their widgets in a useful fashion. This is mostly designed for
containers. There are currently 4 types of layouts:
* **Horizontal boxes** and **vertical boxes** arrange children in a row or a
column, respectively. Each widget gets its desired size; if there is space
left, widgets can expand according to stretch parameters (more on that
* **Stacks** arrange all the widgets on top of each other. Only one widget is
visible at a time. This is useful for tabbed interfaces.
* **Grids** arrange all widgets in a grid. (TODO: WIP)
A widget that does not have a layout needs to manually determine its own
desired size as well as the position its children.
## The layout process
The layout process has two phases.
1. **Size estimation.** In the first phase, each widget declares a desired
size. This size often depends on the size of the children, so this phase
proceeds bottom-up: first the children declare their desired sizes, then the
parents deduce their own desired sizes, and so on.
2. **Space distribution.** In the second phase, space is distributed by the
parents to the children. If the parents have more available space than the
children request, extra space is distributed as well. This phase is
top-down: first the root distributes the available space to its children,
then these children split their shares between their own children, etc.
All of this proceeds automatically unless some widgets cannot provide a natural
size (or only a useless one), in which case the user should give a hint.
## Internals
During the first phase, the content size of each widget is evaluated by either
the layout's `csize()` function, or the polymorphic widget's `csize()`
override. Then `jwidget_msize()` adds in the geometry and stores the margin-box
size in the `w` and `h` attributes.
During the second phase, `jwidget_layout_apply()` distributes the space by
dispatching to the layout's `apply()` function, or the polymorphic widget's
`apply()` override. It proceeds recursively in a depth-first, prefix order.
## Layout control
The widget type provides a natural content size, but the user has the final say
in the size of any widget. Any widget can have a minimum and maximum size
specified, and every layout guarantees that the allocated size falls in this
range (note that limits are examined during the second phase, and the natural
content size does not need to be in the acceptable range).
Additionally, the user can provide stretch rates indicating whether the widget
can use more space horizontally and vertically. When there is space to
distribute and several widgets are competing to use it, space is allocated in
proportion to stretch rates. For instance, a widget with double the stretch
rate of its competitors will get twice as much space as them.
In certain occasions, one might want to disregard the natural content size
entirely and distribute space based only on stretch rates, for instance to
split a screen in evenly-sized columns even when the contents of the columns
have different natural sizes. In this case, one can set the columns to a fixed
width of 0 while enabling stretching-beyond-limits. Stretching beyond limits
will allow the widgets to grow despite being of fixed size, and because they
all start with the same width of 0, they will all end up with the same size.

+ 3
- 0
doc/ View File

@ -0,0 +1,3 @@
# JustUI: Scenes and events

+ 163
- 0
doc/ View File

@ -0,0 +1,163 @@
# JustUI: Widget types
## General widgets and void types
JustUI defines a base structure `jwidget` useful for containers, as well as a
number of derived structures for content widgets. Because C does not support
any type of polymorphism, JustUI uses `void *` pointers when referring to "any
widget", which accepts pointers to any base or derived structure.
This relaxed typing scheme mostly applies to the `jwidget_*()` functions that
operate on the widget level. Widget-specific functions such as `jinput_value()`
request exactly their type of widget as input (in this case, `jinput`).
Essentially, this means that `jwidget` can be inherited from transparently,
but the other types cannot (a structure attribute must be used). This is a
compromise to work with polymorphic types in the C type system.
## Widget collection
**`jwidget`** is the base type. It provides children/parent management (see
[Widget hierarchy](, built-in layouts (see [Space distribution
and layout]( and size constraints, geometry (margin, border,
padding, background), and automatic repaint/relayout mechanisms.
**`jlabel`** is a content widget that shows a single-line or multi-line piece
of text. It supports various text alignment options (left/right/center), line
wrapping (letter and word level), and a couple of graphical options.
**`jscene`** is a special widget designed to be at the root of the widget tree.
It provides event dispatching, automatic repaint and layout, keyboard input,
and a generic input loop that is suitable for most GUI programs. See [Scenes
and events](
**`jinput`** is a one-line input field. It supports direct key input, delayed
and instant SHIFT and ALPHA modifiers, as well as modifier locking, with visual
**`jpainted`** is a very simple wigdet that paints itself with a user-provided
function. It is intended to make custom widgets with very little effort. A
`jpainted` can be positioned, sized and managed by the widget tree while the
user only provides a drawing function.
**`jfkeys`** represents a function key bar, normally at the bottom of the
screen. On fx-9860G, it uses an image to show keys; on fx-CG 50, it supports a
string specification that looks like `"@JUMP;;#ROM;#RAM;#ILRAM;#ADDIN"`. It can
change options dynamically.
## Custom widgets and polymorphic operations
For custom widgets that just have custom rendering a no event management, one
can simply use a `jpainted` instance with well-chosen options. However, for
reusable widgets that have internal state or event handling, a new widget type
should be created.
A custom widget must be a structure that starts with a `jwidget`. The type
itself should be register with `j_register_widget()`, and provide a couple of
polymorphic functions. Here is an example with a very trivial widget that holds
an integer counter.
typedef struct {
jwidget widget;
int counter;
} jcounter;
To be registered in JustUI, a custom widget type must provide a couple of
functions, in the form of a `jwidget_poly` structure. All of the following
functions are detailed in `<justui/widget-api.h>`, but here is a brief review
of the requirements (the `type_poly_` prefix is customary). All of the
functions receive pointers to `jcounter` structures, but the type is `void *`
because of the limitations mentioned earlier.
/* Receives a (jcounter *). Should set its (w) and (h) attributes to the
natural dimensions of the widget. Cannot be NULL. */
void jcounter_poly_csize(void *counter);
/* Paint a jcounter at (x,y). */
void jcounter_poly_render(void *counter, int x, int y);
/* Destroy the resources of a jcounter. (If there are not malloc'd pointers in
the structure, this is generally not needed.) */
void jcounter_poly_destroy(void *counter);
The following functions should be implemented if the custom widget needs to
receive events (such as keyboard input). Events are defined in
/* Handle event (e) sent to a jcounter. Should return true if the event was
accepted/used, false if it was ignored/unused. */
bool jcounter_poly_event(void *counter, jevent e);
The following function is used if (1) an instance of the custom widget has
children, and (2) this instance does not have a layout. This is rarely needed.
See [Space distribution and layout](
/* Receives a (jcounter *) that has its full size set in the (w) and (h)
attributes. Should determine the position and size of the children. */
void jcounter_poly_layout(void *counter);
In general, most of the work consists in specifying the `csize()` and
`render()` functions. `destroy()` just has to release the resources allocated
during widget creation, `event()` is straightforward, and `layout()` is very
rarely needed at all.
Once the required functions are implemented, a polymorphic widget structure can
be defined and registered as a new type. A good way to do this is to register
the widget in a constructor located in the same file as the widget creation
function, so that it runs automatically if the widget is used.
static jwidget_poly type_jcounter = {
.name = "jcounter",
.csize = jcounter_poly_csize,
.layout = NULL,
.render = jcounter_poly_render,
.event = NULL,
.destroy = NULL,
static int jcounter_type_id;
static void j_register_jcounter(void)
jcounter_type_id = j_register_widget(&type_jcounter, "jwidget");
The second parameter to `j_register_widget()` specifies inheritance. `jcounter`
inherits from `jwidget`, which means that the unspecified polymorphic functions
(`layout`, `event` and `destroy`) will use the default behavior of `jwidget`.
This is mostly useful if you don't specify `csize` (the default behavior is to
select the smallest size where all children fit) or `render` (the default
behavior is to render all visible children).
The type ID returned by `j_register_widget()` is how JustUI differentiates
labels from input fields from custom counters. When creating the widget, you
should initialize the `jwidget` field with `jwidget_init()` and specify the
type ID. Note how the parent is also passed at creation time so that the new
widget can automatically be attached to the tree.
jcounter *jcounter_create(int initial_value, void *parent)
if(jcounter_type_id < 0) return NULL;
jcounter *c = malloc(sizeof *c);
jwidget_init(&c->widget, jcounter_type_id, parent);
c->counter = initial_value;
return c;
That's pretty much it. There is a fair amount of boilerplate, but this part
is the same for every widget. See `jpainted` for a simple example and `jinput`
for a full-featured one.

+ 25
- 0
giteapc.make View File

@ -0,0 +1,25 @@
# giteapc: version=1 depends=Lephenixnoir/gint
-include giteapc-config.make
@ fxsdk build-fx -c $(JUSTUI_CMAKEOPTS_FX)
@ fxsdk build-cg -c $(JUSTUI_CMAKEOPTS_CG)
@ fxsdk build-fx
@ fxsdk build-cg
@ fxsdk build-fx install
@ fxsdk build-cg install
@ if [ -e build-fx/install_manifest.txt ]; then \
xargs rm -f < build-fx/install_manifest.txt; \
@ if [ -e build-cg/install_manifest.txt ]; then \
xargs rm -f < build-cg/install_manifest.txt; \
.PHONY: configure build install uninstall

+ 10
- 0
include/justui/ View File

@ -0,0 +1,10 @@
// Just UI: Compile-time configuration
#define J_VERSION "@JustUI_VERSION@"
#endif /* _JUSTUI_CONFIG */

+ 62
- 0
include/justui/defs.h View File

@ -0,0 +1,62 @@
// JustUI.defs: Type and utility definitions
#ifndef _J_DEFS
#define _J_DEFS
#include <gint/defs/types.h>
#include <gint/defs/util.h>
#include <gint/defs/attributes.h>
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
/* j_dirs_t: Quadruplet with four directions */
typedef struct {
uint8_t top;
uint8_t right;
uint8_t bottom;
uint8_t left;
} jdirs;
/* j_align_t: Alignment options with both horizontal and vertical names */
typedef enum {
/* Horizontal names */
/* Vertical names */
} __attribute__((packed)) jalign;
/* j_arg_t: Standard 4-byte argument types for callbacks */
typedef union {
/* Pointers of different qualifiers */
void *p;
void const *cp;
volatile void *vp;
volatile void const *vcp;
/* Integers */
int32_t i32;
uint32_t u32;
/* Vararg macro to cast (void *) parameters to (jwidget *), useful in generic
widget functions. J_CAST(x, y, ...) will expand to
jwidget *x = x0;
jwidget *y = y0;
and accepts from 1 to 4 parameters. */
#define J_CAST0(x) _Pragma("GCC error \"J_CAST takes only up to 4 arguments\"")
#define J_CAST1(x, ...) jwidget *x = x ## 0; __VA_OPT__(J_CAST0(__VA_ARGS__))
#define J_CAST2(x, ...) jwidget *x = x ## 0; __VA_OPT__(J_CAST1(__VA_ARGS__))
#define J_CAST3(x, ...) jwidget *x = x ## 0; __VA_OPT__(J_CAST2(__VA_ARGS__))
#define J_CAST(x, ...) jwidget *x = x ## 0; __VA_OPT__(J_CAST3(__VA_ARGS__))
#endif /* _J_DEFS */

+ 50
- 0
include/justui/jevent.h View File

@ -0,0 +1,50 @@
// JustUI.jevent: GUI union event
#ifndef _J_EVENT
#define _J_EVENT
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <gint/keyboard.h>
/* jevent: GUI event
This type is mostly a union type that provides details on every event that
occurs in the GUI. These are mostly widget signaling state changes,
validations, and other GUI specifics that might require attention. Events
can either be reported to the user by the scene (upwards event) or notify
widgets of something occuring to them (downwards event).
JustUI tries hard to not invert flow control and leave the user to decide
when to produce downwards events. In a normal situation, events from
getkey() are passed to the scene using jscene_process_key() while reading
GUI events moving upwards with jscene_pollevent(). This way, the user can
decide to filter their key events or even craft some.
For the sake of convenience, a single function jscene_run() is provided that
implements a common form of main loop, which forwards keyboard events to the
scene and reports upwards GUI events and ignored key events.
Event IDs can be registered with j_register_event() and (usually) exposed as
global variables. Extensions are meaningful for custom widget types that
need their own upwards events. */
typedef struct {
/* Widget that emitted the event (if upwards), NULL otherwise */
void *source;
/* Type of event */
uint16_t type;
/* Reserved for future use */
uint16_t _;
/* Event details or data */
union {
/* Downward JWIDGET_KEY event or upwards JSCENE_KEY */
key_event_t key;
} jevent;
#endif /* _J_EVENT */

+ 104
- 0
include/justui/jfkeys.h View File

@ -0,0 +1,104 @@
// JustUI.jfkeys: Row of function keys
#ifndef _J_JFKEYS
#define _J_JFKEYS
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <gint/display.h>
/* jfkeys: Functions keys indicating functions for the F1..F6 keys
This widget is the typical function key bar with a slightly different
design. There are four types of keys, with conventional guidelines:
* MENU KEYS are used for functions that open menus or navigate between tabs
on a same application. The name comes from their primary usage in the
system apps. Navigation functions should be easilty reversible and fairly
failproof. Menu keys are black rectangular keys with a chipped corner.
* ENTRY KEYS are used for catalog entries such as the leaves of PRGM's many
nested input menus. They represent entries to be chosen from. Entry keys
are black rectangular keys.
* ACTION KEYS are used for generic safe and unsafe actions. Action keys are
black round keys.
* SPECIAL KEYS are used for special functions, such as scrolling to the next
set of functions keys when there are several pages, important functions
that should catch attention, or particulary unsafe actions. They are round
white keys.
On fx-CG 50, the keys are drawn dynamically using gint's default font, and
specified using 6 strings that give the type and name of the keys:
* "/NAME" for a menu key;
* ".NAME" for an entry key;
* "@NAME" for an action key;
" "#NAME" for a special key.
The names are separated by semicolons, eg "/F1;;/F3;.F4;@F5;#F6". Several
sets of function keys can be defined if separated by a '|' character. For
instance, "/F1;#F2|/F1" represents a function bar where the F2
function can be hidden by swithing from level 0 to level 1.
On fx-9860G, there is not enough space to generate keys on-the-fly, so the
full specification is just an image. The convention for the image is to be
128x8 pixels, with each function key (i) positioned at (x = 21*i + 2) of
width 19. Several levels can be stacked up (n levels in an image of height
9n-1) and selected independently. */
typedef struct {
jwidget widget;
int8_t level;
#ifdef FX9860G
bopti_image_t const *img;
#ifdef FXCG50
char const *labels;
} jfkeys;
/* jfkeys_create(): Create a set of function keysed
The arguments are obviously different on fx-9860G and fx-CG 50. If your
application supports both, you might want to specify arguments for both
platforms in a single call with jfkeys_create2() which will filter them out
for you. Referencing an image unavailable on fx-CG 50 in jfkeys_create2() is
safe since the preprocessor will remove that text. */
#ifdef FX9860G
jfkeys *jfkeys_create(bopti_image_t const *img, void *parent);
#define jfkeys_create2(img, labels, parent) jfkeys_create(img, parent)
#ifdef FXCG50
jfkeys *jfkeys_create(char const *labels, void *parent);
#define jfkeys_create2(img, labels, parent) jfkeys_create(labels, parent)
/* jfkeys_set(): Replace functions
This will also reset the level to 0. */
#ifdef FX9860G
void jfkeys_set(jfkeys *keys, bopti_image_t const *img);
#define jfkeys_set2(keys, img, labels) jfkeys_set(keys, img)
#ifdef FXCG50
void jfkeys_set(jfkeys *keys, char const *labels);
#define jfkeys_set2(keys, img, labels) jfkeys_set(keys, labels)
/* jfkeys_level(): Return the current function key level */
int jfkeys_level(jfkeys *keys);
/* jfkeys_set_level(): Set the function key level */
void jfkeys_set_level(jfkeys *keys, int level);
#endif /* _J_JFKEYS */

+ 91
- 0
include/justui/jinput.h View File

@ -0,0 +1,91 @@
// JustUI.jinput: One-line input field
#ifndef _J_JINPUT
#define _J_JINPUT
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <justui/p/vec.h>
#include <gint/display.h>
/* jinput: One-line input field
This widget is used to read input from the user. It has a single line of
text which can be edited when focused, and an optional prompt. On the right,
an indicator displays the status of modifier keys.
The edition rules support both the OS' native one-key-at-time input system,
and the usual computer modifer-keys-held method.
* The normal insertion mode is used by default.
* When pressing SHIFT or ALPHA in combination with a key (without releasing
SHIFT or ALPHA, as on a computer), the secondary or alphabetic function of
the key is used.
* Pressing then releasing SHIFT or ALPHA activates the secondary or
alphabetic function for the next key press.
* Double-tapping SHIFT or ALPHA locks the corresponding mode on until the
locked mode is disabled by another press-release of the same modifier key.
TODO: jinput: Selection with SHIFT
TODO: jscene: Clipboard support
A timer is used to make the cursor blink, sending JSCENE_REPAINT events
every second or so. The timer is held only during editing and freed when
input stops. If no timer is available the cursor is simply not animated for
the corresponding input sequence.
* JINPUT_VALIDATED when EXE is pressed during edition
* JINPUT_CANCELED when EXIT is pressed during edition */
typedef struct {
jwidget widget;
/* Color and font of text */
int color;
font_t const *font;
/* Optional prompt */
char const *prompt;
/* Text being input */
char *text;
/* Current size (including NUL) and maximum size */
uint16_t size;
uint16_t max;
/* Current cursor position */
int16_t cursor;
/* Current input mode */
uint8_t mode;
/* Timer ID for the cursor state */
int8_t timer;
} jinput;
/* Type IDs */
extern uint16_t JINPUT_VALIDATED;
extern uint16_t JINPUT_CANCELED;
/* jinput_create(): Create an input field
The input field is disabled until it receives focus from its scene. The
edited text is initially empty and is allocated when needed. The length
specifies the maximum amount of bytes in the input. */
jinput *jinput_create(char const *prompt, size_t length, void *parent);
/* Trivial properties */
void jinput_set_text_color(jinput *input, int color);
void jinput_set_font(jinput *input, font_t const *font);
void jinput_set_prompt(jinput *input, char const *prompt);
/* Current value visible in the widget, normally useful upon receiving the
JINPUT_VALIDATED event, not guaranteed otherwise */
char const *jinput_value(jinput *input);
/* jinput_clear(): Clear current text */
void jinput_clear(jinput *input);
#endif /* _J_JINPUT */

+ 134
- 0
include/justui/jlabel.h View File

@ -0,0 +1,134 @@
// JustUI.jlabel: Simple one-line or multi-line text label without formatting
#ifndef _J_JLABEL
#define _J_JLABEL
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <justui/p/vec.h>
#include <gint/display.h>
/* jlabel_wrapmode: Text wrapping options */
typedef enum jwrapmode {
/* Wrap only at \n characters */
/* Break only at spaces; if a word is longer than a full line, breaking in
that word is allowed */
/* Break only at spaces and tabs, even if a word is longer than a line */
/* Break at any letter */
} __attribute__((packed)) jwrapmode;
/* jlabel: One-line or multi-line piece of text, without formatting.
This widget is used for mundane text printing. All the text in the label is
printed with a single font. The text can be aligned horizontally and
vertically within the widget ("block alignment"), and the lines can also be
aligned horizontally within the text block ("text alignment").
| label | Text is centered within the widget (block centered).
| 1 | Lines are aligned by their center (text centered).
| label | Text is centered within the widget (block centered).
| 2 | Lines are aligned by their left side (text left).
|label | Text is aligned left within the widget (block left).
| 3 | Lines are aligned by their center (text centered).
Lines are broken at '\n' characters. Depending on the word wrap settings,
lines can also be broken whenever the edge of the widget is reached. Extra
line spacing (even negative) can be specified.
The natural size of the widget is always computed based on newline
characters, since there is no way to wrap until a width has been assigned.
If you want predictable results, use size constraints.
The text color and font can also be set, using the same color values and
fonts as <gint/display.h>. If the font is set to NULL, gint's default font
is used. The background color for the whole widget is handled by jwidget. */
typedef struct {
jwidget widget;
/* Horizontal block alignment */
jalign block_halign;
/* Vertical block alignment */
jalign block_valign;
/* Text alignment */
jalign text_align;
/* Pixels of spacing between each line, in addition to font->height */
int8_t line_spacing;
/* Text to display */
char const *text;
/* List of line break offsets; indexes 2n and 2n+1 give the start and end
of line number n */
DECLARE_VEC(uint16_t, breaks);
/* Text wrapping mode */
enum jwrapmode wrap_mode;
/* Whether the text has been allocated by the label or supplied by user */
int8_t owns_text;
/* Block width (maximum length of a rendered line) */
uint16_t block_width;
/* Color and font of text; if NULL, gint's default font is used */
int color;
font_t const *font;
} jlabel;
/* jlabel_create(): Create a label with a fixed text
Initially the label has the supplied string as text (which must outlive the
widget). If you want dynamic text, you can provide an empty string and use
jlabel_printf() later. */
jlabel *jlabel_create(char const *text, void *parent);
/* jlabel_set_text(): Set a fixed string
This function sets the label text to a fixed string. This is the same as in
jlabel_create(). The string is not copied, it must outlive the label. This
is the most useful when the provided string is a literal. */
void jlabel_set_text(jlabel *l, char const *text);
/* jlabel_asprintf(): Generate and set a formatted string
This function generates the label string with printf-formatting. The
resulting string is created with malloc() and owned by the label; it is
destroyed when the text is replaced of the label is destroyed. Because of
how asprintf() works, the string is generated twice. Returns the number of
characters printed. */
int jlabel_asprintf(jlabel *l, char const *format, ...);
/* jlabel_snprintf(): Generate and set a formatted string
Similar to jlabel_asprintf(), but an upper bound on the length of the result
string has to be provided. This avoids generating the string twice. Return
the number of characters printed. */
int jlabel_snprintf(jlabel *l, size_t size, char const *format, ...);
/* jlabel_text(): Get the current string */
char const *jlabel_text(jlabel *l);
/* Set block and text alignment, individually */
void jlabel_set_block_alignment(jlabel *l, jalign horz, jalign vert);
void jlabel_set_text_alignment(jlabel *l, jalign align);
/* Set both horizontal alignments at the same time (most natural) */
void jlabel_set_alignment(jlabel *l, jalign horizontal_align);
/* Trivial properties */
void jlabel_set_line_spacing(jlabel *l, int line_spacing);
void jlabel_set_wrap_mode(jlabel *l, jwrapmode mode);
void jlabel_set_text_color(jlabel *l, int color);
void jlabel_set_font(jlabel *l, font_t const *font);
#endif /* _J_JLABEL */

+ 118
- 0
include/justui/jlayout.h View File

@ -0,0 +1,118 @@
// JustUI.jlayout: Widget positioning mechanisms
#ifndef _J_JLAYOUT
#define _J_JLAYOUT
#include <justui/defs.h>
/* jlayout_type: Built-in layout mechanisms */
typedef enum {
/* User or sub-class places children manually */
/* Children are laid out in a vertical box; spacing applies. Extra
space is redistributed proportionally to the stretch attribute. */
/* Same as J_LAYOUT_VBOX, but horizontally. */
/* Children are stacked, only one is visible at a time. */
/* Children are laid out in a grid; spacing applies. */
} jlayout_type;
// Manual layout
/* jlayout_set_manual(): Remove a layout from a widget
This function removes the current layout of the widget. */
void jlayout_set_manual(void *w);
// Box layout
/* jlayout_box: Parameters for VBOX and HBOX layouts */
typedef struct {
/* Spacing between elements */
uint8_t spacing;
uint :24;
} jlayout_box;
/* jlayout_get_hbox(), jlayout_get_vbox(): Get configuration for box layouts
These functions return the jlayout_box parameters for widgets that have a
box layout, and NULL otherwise. */
jlayout_box *jlayout_get_hbox(void *w);
jlayout_box *jlayout_get_vbox(void *w);
/* jlayout_set_hbox(), jlayout_set_vbox(): Create box layouts
These functions configure the specified widget to have a box layout, and
return the jlayout_box parameters to configure that layout. The parameters
are initialized even if the widget previously had a box layout. */
jlayout_box *jlayout_set_hbox(void *w);
jlayout_box *jlayout_set_vbox(void *w);
// Stack layouts
/* jlayout_stack: Parameters for STACK layouts */
typedef struct {
/* Index of currently-visible child */
int8_t active;
uint :24;
} jlayout_stack;
/* jlayout_get_stack(): Get configuration for stack layouts
For widgets that have a stack layout, returns a pointer to the parameters.
Otherwise, returns NULL. */
jlayout_stack *jlayout_get_stack(void *w);
/* jlayout_set_stack(): Create stack layouts
Configure the specified widget to have a stack layout and return the new
parameters. The new layout is cleared even if the widget previously had a
stack layout. */
jlayout_stack *jlayout_set_stack(void *w);
// Grid layouts
typedef enum {
/* Rows from top to bottom */
/* Rows from bottom to top */
/* Columns from left to right */
/* Columns from right to left */
} jlayout_grid_order;
typedef struct {
/* Spacing between rows and between columns */
uint8_t row_spacing;
uint8_t col_spacing;
/* Child order. The major order specifies whether the grid is filled by
rows or by columns. The minor order specifies how these rows or
columns are filled themselves. There must be exactly one vertical
setting and one horizontal setting. */
uint major_order :2;
uint minor_order :2;
/* Number of rows, and number of columns; default to -1, in which case
the minor size is determined based on the first minor group and the
major size is calculated greedily. */
uint rows :6;
uint cols :6;
} jlayout_grid;
/* TODO: Functions for grid layouts */
#endif /* _J_JLAYOUT */

+ 65
- 0
include/justui/jpainted.h View File

@ -0,0 +1,65 @@
// JustUI.jpainted: Widget defined by just a size and painting function
#ifndef _J_JPAINTED
#define _J_JPAINTED
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <justui/jwidget-api.h>
/* jpainted: Simple widget designed to integrate low-effort rendering
This widget is simply a rectangle on which a custom function does the
rendering. It allows the GUI to have custom rendering with very little code,
as the widget only requires a natural size and a renderer.
The natural size doesn't need to be exactly the size of the widget; ideally
it should be larger than what the rendered needs. In any case the natural
size is not the final one, as stretch can be used in layouts.
This widget will typically be used like this:
void draw_something(int x, int y) {
dimage(x, y, ...);
dtext(x+2, y+10, ...);
jpainted *widget = jpainted_create(draw_something, NULL, 10, 10, parent);
The rendering function can optionally take an argument; this argument can be
an integer or a pointer (essentially).
void draw_value(int x, int y, uint32_t *value) {
dprint(x, y, "%08X", *value);
uint32_t my_value;
jpainted *widget = jpainted_create(draw_value, &my_value, 30, 8, parent);
The type of the argument is defined as j_arg_t but you don't need to create
a j_arg_t object, just pass the argument directly. If you don't have an
argument, pass NULL.
Note that in this example you will need to set (widget->update = 1) whenever
you change (my_value), in order for the scene to produce a PAINT event.
Otherwise, a change in (my_value) will not be seen on-screen until the next
time the scene is painted.
The layout and rendering will be performed automatically, allowing the GUI
to benefit from automated positioning with virtually no overhead. */
typedef struct {
jwidget widget;
/* Renderer function and its argument */
void (*paint)(int x, int y, j_arg_t arg);
j_arg_t arg;
/* Natural size */
int16_t natural_w;
int16_t natural_h;
} jpainted;
/* jpainted_create(): Create a simple painted widget */
jpainted *jpainted_create(void *function, j_arg_t arg, int natural_w,
int natural_h, void *parent);
#endif /* _J_JPAINTED */

+ 105
- 0
include/justui/jscene.h View File

@ -0,0 +1,105 @@
// JustUI.jscene: Root object that provides keyboard focus and event handling
#ifndef _J_JSCENE
#define _J_JSCENE
#include <justui/defs.h>
#include <justui/jwidget.h>
#include <justui/jevent.h>
/* jscene: A widget scene with keyboard focus and event handling
This widget is designed to be the root of a widget tree. It keeps track of
widgets with keyboard focus, feeds them keyboard events, and catches other
useful events to store them in an event queue. */
typedef struct {
jwidget widget;
/* Location on screen */
int16_t x, y;
/* Widget with focus */
jwidget *focus;
/* Circular event queue */
jevent queue[JSCENE_QUEUE_SIZE];
uint8_t queue_first;
uint8_t queue_next;
/* Number of events lots */
uint16_t lost_events;
} jscene;
/* Events */
extern uint16_t JSCENE_NONE;
extern uint16_t JSCENE_PAINT;
extern uint16_t JSCENE_KEY;
/* jscene_create(): Create a new scene at that specified screen position */
jscene *jscene_create(int x, int y, int w, int h, void *parent);
/* jscene_create_fullscreen(): Create a fullscreen scene
The position is (0,0) and the size is (DWIDTH,DHEIGHT). */
jscene *jscene_create_fullscreen(void *parent);
/* jscene_layout(): Layout a scene
This is automatically called by jscene_render(), but may be useful if you
need to know the size of your widgets before rendering. The layout is
recomputed only if something in the scene has changed. */
#define jscene_layout jwidget_layout
/* jscene_render(): Layout and render a scene
Layout is lazy and performed only if needed. The scene is rendered at its
(x,y) point. */
void jscene_render(jscene *scene);
// Events sent from the scene to the user
/* jscene_read_event(): Get the next upwards event from the queue
If there is no event, returns an event of type JSCENE_NONE. */
jevent jscene_read_event(jscene *scene);
/* jscene_queue_event(): Queue an upwards event to be later read by the user
This function records an event in the scene's queue, which will later be
returned by jscene_pollevent(). This is mostly used by widget code to signal
stuff that warrants attention. */
void jscene_queue_event(jscene *scene, jevent event);
// Keyboard focus and keyboard events
/* jscene_focused_widget(): Query the widget that currently has focus */
void *jscene_focused_widget(jscene *scene);
/* jscene_set_focused_widget(): Move the focus to a widget
The selected widget, obviously, must be a descendant of the scene. */
void jscene_set_focused_widget(jscene *scene, void *widget);
/* jscene_process_key(): Send a downwards key event to the focused widget
Returns true if the event was accepted, false if it was ignored. */
bool jscene_process_key(jscene *scene, key_event_t event);
/* jscene_process_event(): Send a downards GUI event to the focused widget */
// bool jscene_process_event(jscene *scene, jevent event);
/* jscene_run(): Run a scene's main loop
This function implements a main control loop that sleeps when there is
nothing to do, forwards all key events to the scene, and returns only to
notify GUI events or hand over key events that have been ignored by the
If a scene event occurs, returns it. If a key event occurs, an event of type
JSCENE_KEY is return and its .key attribute contains the details of the
forwarded keyboard event. */
jevent jscene_run(jscene *scene);
#endif /* _J_JSCENE */

+ 158
- 0
include/justui/jwidget-api.h View File

@ -0,0 +1,158 @@
// JustUI.jwidget-API: API for subclassed widget types
#ifndef _J_JWIDGET_API
#define _J_JWIDGET_API
#include <justui/defs.h>
#include <justui/jevent.h>
#include <gint/keyboard.h>
// Polymorphic operations on widgets
// The following operations are widget-type-dependent. Derived widget types
// must provide their implementation through a jwidget_poly structure. The
// supplied functions will then be called at different points during the
// lifetime of the derived widgets.
/* jwidget_poly_csize_t: Determine the natural content size of a widget
This function is called during layout. It should set the natural size of the
content box of the widget in (w->w) and (w->h). If the widget has a layout,
this function will not be called; instead, the layout will determine the
natural content size. Thus, this function is mostly useful for content
widgets and not useful for containers.
Implementations of this function should use jwidget_msize() on the children
to position their margin box within the widget's content box. The size set
by this function needs not be in the minimum/maximum range of the widget. */
typedef void jwidget_poly_csize_t(void *w);
/* jwidget_poly_layout_t: Layout a widget after its size has been set
This function is called during the second phase of the layout process, if
the widget has no layout. The margin-box size allocated to the widget has
been set in (w->w) and (w->h); the widget must now position its contents and
children. If the widget has a layout, the layout's specialized function is
called instead of this one. */
typedef void jwidget_poly_layout_t(void *w);
/* jwidget_poly_render_t: Render a widget
This function is called during rendering after the widget's geometry is
drawn. (x,y) are the coordinates of the content box. This function must
render widget-specific visuals; there is no clipping, so the widget should
honor its width and height.
This function should call jwidget_render() for all children that need to be
rendered. jwidget_render() handles the geometry and takes as parameters the
coordinates of the margin box, so it can be called as:
jwidget_render(child, x + child->x, y + child->y).
It will draw the geometry and call the polymorphic renderer of the child at
its content-box coordinates. Normally you can ignore geometry altogether. */
typedef void jwidget_poly_render_t(void *w, int x, int y);
/* jwidget_poly_event_t: Handle an event
This function is called when an event is targeted at a widget, including for
key events. This function is somewhat of a catch-all function for dynamic
occurrences. The widget should either accept the event, do something with
it, and return true, or refuse the event, do nothing and return false. This
is influences events that propagate, such as key events. */
typedef bool jwidget_poly_event_t(void *w, jevent e);
/* jwidget_poly_destroy_t: Destroy a widget's specific resources
This function must destroy the widget-specific resources. It is called by
jwidget_destroy(), which follows it by freeing the widget's standard data
and destroying the children. This function can be NULL if there are no
widget-specific resources to free. */
typedef void jwidget_poly_destroy_t(void *w);
/* jwidget_poly: Polymorphic interface for a widget type */
typedef struct {
/* Type name, used for display and inheritance */
char const *name;
/* Polymorphic functions */
jwidget_poly_csize_t *csize;
jwidget_poly_layout_t *layout;
jwidget_poly_render_t *render;
jwidget_poly_event_t *event;
jwidget_poly_destroy_t *destroy;
} jwidget_poly;
// Widget registration
/* j_register_widget(): Register a new widget type
This function returns a new widget type ID to pass to jwidget_init() when
creating widgets of the custom type. Returns -1 if registration fails. The
polymorphic structure must outlive all widgets of the custom type.
If (inherits) is non-NULL, the new type will inherit from the widget type
with the provided name. All NULL functions in (poly) will be replaced by the
parent type's functions. This mechanism only works if widgets are declared
in inheritance order, which is normally enforced by constructor priority. */
int j_register_widget(jwidget_poly *poly, char const *inherits);
/* j_register_event(): Register a new event type
This function returns a new ID to set in the (type) field of jevent objects.
The ID is unique, and can be trusted to be always valid (unless you register
more than 64k events in which case you asked for the trouble). */
int j_register_event(void);
// Helpers to implement custom widget types
/* jwidget_init(): Initialize a widget
This function should be called in the constructor for the subclassed widget
type, preferably as soon as possible. It initializes common widget
attributes, sets the widget type, and declares the parent.
A subclassed widget type must have a jwidget as a first member (so that the
address of any instance of the subclassed widget is a valid pointer to
jwidget), and that jwidget must be initialized with jwidget_init().
@w Widget to initialize
@type Type ID, as returned by j_register_widget()
@parent Parent, same as in jwidget_create() */
void jwidget_init(jwidget *w, int type, void *parent);
/* jwidget_msize(): Compute and apply the natural size of a widget's margin-box
This function computes the widget's natural margin-box size. It determines
the natural content size with the csize() function of either the layout or
the widget type, then adds the geometry.
The margin-box size is stored in the (w) and (h) attributes of the widget.
This function should only be called during the first phase of the layout,
to implement subclassed csize() functions. Usually, the parent will
implement a customn csize() function by combining the position and msize()
of its children. */
void jwidget_msize(void *w);
/* jwidget_emit(): Emit an upwards event from this widget
This function walks up the tree until it finds a jscene that can store the
event. If there is no jscene in the tree, the event is ignored. The (source)
field can be omitted and will be set to the widget's address by default. */
void jwidget_emit(void *w, jevent e);
/* jwidget_event(): Send a downwards event to a widget
This function calls the polymorphic event function of the targeted widget to
notify it of the specified event. */
bool jwidget_event(void *w, jevent e);
#endif /* _J_JWIDGET_API */

+ 370
- 0
include/justui/jwidget.h View File

@ -0,0 +1,370 @@
// JustUI.jwidget: Base object for all widgets
#ifndef _J_JWIDGET
#define _J_JWIDGET
#include <justui/defs.h>
#include <justui/jlayout.h>
/* jwidget: Base object for all widgets.
Functions that ought to work on any widget take void * parameters; this
includes jwidget and derived types whose first attribute is a jwidget. A
void * parameter named "w" is implicitly a widget.
Widgets for a scene are arranged in a tree hierarchy; each object has a
unique parent and a number of distinct children, which they own. Widgets
destroy their children when they are destroyed. The parent of all the
widgets in the scene is called the root, it's the only one without a parent.
Every widget has an HTML-like box model described by its geometry object.
See the jwidget_geometry type below for details. By default widgets have no
geometry to save memory, it is only created when actually used.
The size of each widget can be controlled in two ways. Initially, the
widget's contents suggest a "natural size". The user can then restrict
acceptable sizes by specifying a size hint along with a policy. See the
jwidget_size_policy type below for details. Additionally, the user can set
stretch parameters to allow the widget to grow and occupy available space.
Widgets usually have one of two roles; either they are "containers" for
other widgets, or they are "content widgets" that render text and images, or
take input from the user. In order to make containers easy to design, each
widget can be equipped with a "layout" that automatically arranges children
in useful ways, such as vertical lists or grids. Content widgets usually
don't have layouts.
Polymorphic operations are defined for each widget type and accessed through
the type attribute. See the widget extension API at the end of this header
for details on polymorphic operations.
The following attributes can be accessed by the user:
.parent (read-only)
.children[] (read-only)
.child_count (read-only) */
typedef struct jwidget {
/* Parent and children in the widget tree */
struct jwidget *parent;
struct jwidget **children;
/* Location within the content-box of the parent (after layout) */
int16_t x, y;
/* Margin-box size in pixels (after layout) */
int16_t w, h;
/* Size hints: these are user-provided sizes, which are combined with the
size policy to determine acceptable widget dimensions */
int16_t min_w, min_h;
int16_t max_w, max_h;
/* Widget geometry, defaults to a fixed geometry object */
struct jwidget_geometry *geometry;
/* Layout data, access with the jlayout_{get,set}_*() functions) */
union {
jlayout_box layout_box;
jlayout_stack layout_stack;
jlayout_grid layout_grid;
/* Widget type, used to find polymorphic operations */
uint8_t type;
/* Number of children */
uint8_t child_count;
/* Number of pointers allocated in the children array */
uint8_t child_alloc;
/* Horizontal and vertical stretch rates */
uint stretch_x :4;
uint stretch_y :4;
/* Type of layout (see the jlayout_type enum) */
uint layout :4;
/* Whether stretch can go beyond the maximum size */
uint stretch_force :1;
/* Whether the layout needs to be recomputed */
uint dirty :1;
/* Whether the widget needs to be redrawn */
uint update :1;
/* Whether widget is visible inside its parent */
uint visible :1;
uint :24;
} jwidget;
/* jwidget_border_style: Styles of widget borders */
typedef enum {
/* No border */
/* Border is a solid color */
/* TODO: More border styles (especially on fx-CG 50) */
} jwidget_border_style;
/* jwidget_geometry: Built-in positioning and border geometry
Every widget has a "geometry", which consists of a border and two layers of
spacing around the widget:
* The "padding" is the spacing between the widget's contents and its border
* The "border" is a visible decoration around the widget's contents
* The "margin" is the spacing between the border and the widget's edge
For users familiar with the CSS box model, this is it. Note, however, that
unlike the common CSS property (box-sizing: border-box), JustUI counts the
margin as widget-owned space and measures widgets using the margin box. */
typedef struct jwidget_geometry {
/* Padding (in pixels) on all four sides; access either using,
.right, .bottom and .left, or using paddings[0] through paddings[3] */
union {
uint8_t paddings[4];
jdirs padding;
/* Width of the border on all four sides */
union {
uint8_t borders[4];
jdirs border;
/* Size of the margin (in pixel) on all four sides */
union {
uint8_t margins[4];
jdirs margin;
/* Border color (as in <gint/display.h>) */
int border_color;
/* Border style */
jwidget_border_style border_style;
/* Background color */
int background_color;
} jwidget_geometry;
/* Downwards key event: widget is notified of a key press that ocurred while it
had active focus.
-> .data.key: Key event */
extern uint16_t JWIDGET_KEY;
/* Downwards focus-in event: the widget has just received focus */
extern uint16_t JWIDGET_FOCUS_IN;
/* Downwards focus-out event: the widget has just lost focus */
extern uint16_t JWIDGET_FOCUS_OUT;
// Creation and destruction
/* jwidget_create(): Create a widget
This function creates a type-less widget. If you want to create labels,
buttons, input fields... you need to use the specific creation functions
such as jlabel_create(). This function only creates empty widgets, which are
primarily useful as containers.
If a non-NULL parent is specified, then the new widget becomes a child of
that parent. The parent will then handle the positioning and sizing of the
new widget, and destroy it automatically.
After creating a container with jwidget_create(), it is common to give it a
layout and add children to it, either with jwidget_add_child() or through
the children's constructors.
@parent This widget's parent.
-> Returns the new widget (NULL on error). */
jwidget *jwidget_create(void *parent);
/* jwidget_destroy(): Destroy a widget
This function destroys the specified widget and its children. If the
destroyed widget has a parent, the parent is notified, so the widget tree
cannot become invalid. However, the layout process should be re-run to
layout the remaining scene elements. */
void jwidget_destroy(void *w);
// Widget tree manipulation
/* jwidget_set_parent(): Change a widget's parent
Moves the widget from its current parent to another parent. If the widget
already had a parent, it is notified. If the new parent is NULL, the widget
is left without a parent. */
void jwidget_set_parent(void *w, void *parent);
/* jwidget_add_child(): Add a child at the end of a widget's child list
If the widget already had a parent, that parent is notified. */
void jwidget_add_child(void *w, void *child);
/* jwidget_insert_child(): Insert a child in the widget's child list
Similar to jwidget_add_child(), but the child is added at the requestd
position in the parent's child list. The position must be in the range
[0 ... w->child_count]. */
void jwidget_insert_child(void *w, void *child, int position);
/* jwidget_remove_child(): Remove a child from a widget
(w) must be the parent of (child). The child is left without a parent. */
void jwidget_remove_child(void *w, void *child);
// Sizing and stretching
/* Functions to set the minimum width, minimum height, or both. The minimum
size can be cleared by specifying 0. */
void jwidget_set_minimum_width(void *w, int min_width);
void jwidget_set_minimum_height(void *w, int min_height);
void jwidget_set_minimum_size(void *w, int min_width, int min_height);
/* Functions to set the maximum width, maximum height, or both. The maximum
size can be cleared by specifying -1. */
void jwidget_set_maximum_width(void *w, int max_width);
void jwidget_set_maximum_height(void *w, int max_height);
void jwidget_set_maximum_size(void *w, int max_width, int max_height);
/* Functions to set both the minimum and maximum size at the same time. */
void jwidget_set_fixed_width(void *w, int width);
void jwidget_set_fixed_height(void *w, int height);
void jwidget_set_fixed_size(void *w, int width, int height);
/* jwidget_set_stretch(): Set the stretch factors for a widget
Stretch factors indicate how much a widget wants to grow. Stretch is used in
all size policies except the fixed one. Due to storage limits, the stretch
factors should be in the range [0 ... 15].
The last parameter indicates whether to allow stretching beyond the maximum
size of the widget. In almost all situations this should be false. However,
in some cases you might want to completely ignore natural size and allocate
space based uniquely on stretch factors. In this case, you can set a fixed
size of 0 and enable stretching beyond limits. */
void jwidget_set_stretch(void *w, int stretch_x, int stretch_y,
bool stretch_beyond_limits);
// Geometry
/* jwidget_geometry_r(): Get read-only access to a widget's geometry
This function returns a read-only pointer to the specified widget's
geometry. Because most widgets don't have customized geometry, JustUI
doesn't store any data until the geometry is modified, to save memory. This
is why it makes sense to separate read-only and read-write accesses.
For widgets without customized geometry, this functions returns a pointer to
a fixed constant geometry with zero padding, border and margin. */
jwidget_geometry const *jwidget_geometry_r(void *w);
/* jwidget_geometry_rw(): Get read-write access to a widget's geometry
This function returns a read-write pointer to the specified widget's
geometry. For widgets that don't have customized geometry yet, this will
duplicate the default settings. This avoids memory consumption on widgets
that don't need custom geometry.
Returns NULL if duplication fails because of memory exhaustion. */
jwidget_geometry *jwidget_geometry_rw(void *w);