Add the UI demo: title menu, raw-CLAY options screen, HUD and dialog

examples/uidemo is the program the UI chapter quotes. Three screens, three
ways of building an interface: the title screen is one akgl_UiMenu plus a
heading label; the options screen is written in raw CLAY() declarations --
hover highlighting inside the declaration, clicks paired with the
application's own press edge -- because the widgets are a convenience, not a
boundary; and the play screen is two HUD labels and the one-call dialog over
a checkerboard standing in for a game world. No tilemap, no actors, no
physics: everything left on screen is the subject.

Its route_event() is the documented call-order contract in the flesh -- UI
first, consumed events stop there, then the current screen's keys -- and the
screen routing is the focus model, stated rather than invented.

The headless smoke run (example_uidemo, --frames 240 --demo) tours every
path through the real event chain: a mouse click on the centred menu (the
consumed path, landing on the middle row by symmetry), keyboard into and out
of the options screen, the dialog opened and dismissed, and Quit confirmed
from the menu. The run prints its score and settings so a pass can be read,
not just counted. docs_game_figures gains a uidemo frame -- the play screen
with the dialog up -- for the chapter to come.

Co-Authored-By: Claude Code (Claude Fable 5, claude-fable-5) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzBDV2fqgnUAcqCKqKvc71
This commit is contained in:
2026-08-02 11:24:58 -04:00
parent badaf570ab
commit 4ddd8d1ad3
5 changed files with 737 additions and 3 deletions

58
examples/uidemo/uidemo.h Normal file
View File

@@ -0,0 +1,58 @@
/**
* @file uidemo.h
* @brief Shared constants and types for the UI demo.
*
* There is deliberately little here: the demo is one translation unit, and
* this header exists so the chapter can quote the constants and the script
* type without quoting the code around them.
*/
#ifndef _UIDEMO_H_
#define _UIDEMO_H_
#include <SDL3/SDL.h>
/** @brief Window and layout width, in pixels. */
#define UIDEMO_SCREEN_WIDTH "640"
/** @brief Window and layout height, in pixels. */
#define UIDEMO_SCREEN_HEIGHT "480"
/** @brief The same width as a number, for akgl_ui_init. */
#define UIDEMO_WIDTH 640
/** @brief The same height as a number. */
#define UIDEMO_HEIGHT 480
/** @brief Registry name the demo's one font is loaded under. */
#define UIDEMO_FONT_NAME "uidemo"
/** @brief Point size the font is loaded at. The size is baked into the handle. */
#define UIDEMO_FONT_SIZE 16
/**
* @brief Which screen the demo is on.
*
* Three states, three ways of building an interface: the title screen is the
* menu *widget*, the options screen is raw `CLAY()` layout, and the play
* screen is the label and dialog widgets over a moving world stand-in.
*/
typedef enum {
UIDEMO_STATE_TITLE, /**< The akgl_UiMenu title menu. */
UIDEMO_STATE_OPTIONS, /**< The hand-written CLAY() options panel. */
UIDEMO_STATE_PLAY /**< HUD labels and a dialog over the "game". */
} uidemo_State;
/**
* @brief One scripted input in a `--demo` run.
*
* Key steps carry a keycode; mouse steps carry a position. Every step is
* synthesized as a real SDL_Event and pushed through the same routing the
* interactive loop uses, so the demo exercises the event chain rather than
* poking state behind its back.
*/
typedef struct {
long frame; /**< The frame this step fires on. */
uint32_t type; /**< SDL_EVENT_KEY_DOWN, _MOUSE_MOTION, _MOUSE_BUTTON_DOWN or _MOUSE_BUTTON_UP. */
SDL_Keycode key; /**< For key steps. */
float x; /**< For mouse steps. */
float y; /**< For mouse steps. */
} uidemo_ScriptStep;
#endif // _UIDEMO_H_