reset
This commit is contained in:
1 parent
5dad34f2e3
commit
da7445fbc1
87 files changed
+1
-42041
No files matched your search
-230
@@ -1,230 +0,0 @@
|
||||
# Regolith
|
||||
|
||||
Regolith is a retained mode GUI framework written in C99. It is built for
|
||||
native desktop applications and for game user interfaces, and it compiles to
|
||||
Linux, Windows, macOS, and WebAssembly. It uses no webview and no web backend.
|
||||
|
||||
## Goals
|
||||
|
||||
- A clean C API that other languages such as Odin and Zig can bind directly.
|
||||
- A retained document tree that loads from disk.
|
||||
- Lua as the document language and as a full runtime API, so an application
|
||||
can be written entirely in Lua after initialization.
|
||||
- Easy initialization, with a small number of calls to get a window and a
|
||||
document on screen.
|
||||
- Independence from any single platform or renderer.
|
||||
|
||||
## Inspiration
|
||||
|
||||
RmlUi is the main inspiration. Its document model, styling separation, data
|
||||
binding, animations, and event bubbling are the parts I want. Regolith aims
|
||||
for those ideas with a C API and a simpler event model. HTML and CSS parity
|
||||
is not a goal.
|
||||
|
||||
## Architecture
|
||||
|
||||
Regolith is split into layers. Each layer depends only on the ones below it.
|
||||
|
||||
- **core**: node tree, style resolution, events, data bindings, animation,
|
||||
and text. Pure C99. No Lua types and no Clay types appear in its public API.
|
||||
- **layout adapter**: the only code that touches Clay. It walks the retained
|
||||
tree each frame, emits Clay calls, and reads back computed layout.
|
||||
- **lua module**: the document loader and the Lua API. It is a thin layer
|
||||
over the C API.
|
||||
- **backend interface**: a struct of function pointers defined in the main
|
||||
header. It creates and updates textures, draws batches of colored and
|
||||
textured quads, and sets the scissor rect.
|
||||
- **default backend**: SDL3 and OpenGL.
|
||||
- **null backend**: records the quad list instead of drawing, used for
|
||||
headless tests.
|
||||
|
||||
The C API is the source of truth. Every capability exists in C first, and the
|
||||
Lua binding exposes it without adding behavior of its own.
|
||||
|
||||
## Dependencies
|
||||
|
||||
All dependencies are vendored in `third_party/` and built by Regolith's own
|
||||
CMake, so native and WASM builds use identical versions.
|
||||
|
||||
- Lua 5.4 for documents and scripting
|
||||
- Clay for layout and render commands
|
||||
- stb_truetype and stb_rect_pack for fonts and atlas packing
|
||||
|
||||
SDL3 is the only external dependency, and only the default backend uses it.
|
||||
|
||||
## Layout
|
||||
|
||||
Clay provides layout. Regolith exposes a Clay-flavored property vocabulary
|
||||
(`sizing`, `padding`, `gap`, `align`, `direction`). Every node is a box with a
|
||||
direction. Node types use Godot-style names such as `Panel`, `Label`,
|
||||
`Button`, `HBox`, `VBox`, and `ScrollContainer`, where `HBox` and `VBox` are
|
||||
shorthand for a box with a fixed direction.
|
||||
|
||||
## Text
|
||||
|
||||
The core owns all text handling. It loads fonts, caches glyph metrics, packs
|
||||
glyphs into a single-channel atlas, and measures strings for Clay. The
|
||||
backend receives the atlas as a texture and draws glyphs as textured quads.
|
||||
Rasterization happens at logical size multiplied by display scale, so the font
|
||||
API carries a scale factor from the start. The text module sits behind a small
|
||||
interface so a shaper can replace it later. Shaping, bidirectional text, and
|
||||
color emoji are out of scope for the first versions.
|
||||
|
||||
## Rendering
|
||||
|
||||
The core never calls a backend. Each frame `rg_render` returns a draw list
|
||||
made of texture operations (create, update region, destroy) followed by
|
||||
batches of colored and textured quads with scissor rects. A backend is any
|
||||
code that consumes that list. The SDL3 and OpenGL backend is the default, and
|
||||
the null backend records the list for headless tests. A WASM host can consume
|
||||
the list from JavaScript. The shipped backends share a small convenience
|
||||
interface, and the core does not depend on it.
|
||||
|
||||
## Viewport and scale
|
||||
|
||||
All API coordinates are logical pixels. The host calls
|
||||
`rg_set_viewport(ctx, w, h, scale)` when the window resizes or the display
|
||||
scale changes. A scale change invalidates the glyph atlas and glyphs are
|
||||
rasterized again at the new size.
|
||||
|
||||
## Documents
|
||||
|
||||
Documents are Lua files. Node constructors such as `VBox`, `Label`, and
|
||||
`Button` return plain descriptor tables containing a type, properties, and
|
||||
children. A separate step realizes the descriptor tree into live nodes
|
||||
through the C API. Descriptors are pure data, which keeps them easy to print
|
||||
and debug and allows hot reload to reconcile a new tree against the live one
|
||||
by `id` or key.
|
||||
|
||||
Each document runs in its own `_ENV` that provides the node constructors and
|
||||
helpers. A document is free to contain arbitrary Lua logic, including
|
||||
frontend-only behavior and calls to the host application. The same `_ENV`
|
||||
mechanism provides sandboxing, which is an option at initialization. A
|
||||
sandboxed state omits `io`, `os`, `debug`, and arbitrary `load`, and installs
|
||||
an instruction-count hook to stop runaway scripts.
|
||||
|
||||
Both authoring surfaces exist. The call style
|
||||
(`VBox { Label { text = "Hi" } }`) is the primary form. The builder style
|
||||
(create a node, set properties, append it to a parent) is the same C API
|
||||
exposed to Lua and is used for dynamic content.
|
||||
|
||||
## Styling
|
||||
|
||||
Styles are Lua tables with a small selector engine. Supported selectors are
|
||||
type, class, id, the state pseudo-classes `:hover`, `:focus`, `:active`, and
|
||||
`:disabled`, and a child combinator. The cascade orders by specificity and
|
||||
then declaration order. There is no `!important`. Components are Lua
|
||||
functions that return descriptor tables, and shared appearance comes from
|
||||
classes. Style resolution runs only when classes, state, or tree structure
|
||||
change.
|
||||
|
||||
## Data binding
|
||||
|
||||
A binding is a callback with userdata attached to a node property. The data
|
||||
model is a keyed store with version counters, and a binding re-evaluates only
|
||||
when a key it depends on has a newer version than its last evaluation. In Lua
|
||||
the model is a proxy table whose `__newindex` bumps the version. Dependencies
|
||||
are declared as explicit keys. Automatic dependency tracking is a later
|
||||
improvement.
|
||||
|
||||
## Animation
|
||||
|
||||
Property changes can transition over time, similar to CSS transitions. The
|
||||
retained tree stores the previous value, and the interpolated value is fed
|
||||
into layout each frame. Keyframes and coroutine-driven sequences come later.
|
||||
|
||||
## Events
|
||||
|
||||
Events bubble and have no capture phase. The fixed set is click, hover enter,
|
||||
hover leave, focus, blur, change, key, and custom named events. A handler is
|
||||
either a Lua function or a string name. A string name pushes an event into a
|
||||
polled queue, so C, Zig, and Odin users consume events without callbacks
|
||||
across the FFI boundary.
|
||||
|
||||
## API conventions
|
||||
|
||||
- One public header, `regolith.h`, with the implementation in ordinary `.c`
|
||||
files compiled into a static or shared library.
|
||||
- `regolith_lua.h` and `regolith_sdl3.h` are separate optional headers, so
|
||||
`lua_State` and SDL types never appear in the main header.
|
||||
- Fixed-width integer types, plain structs, no bitfields, no variadics, and
|
||||
no `static inline` in the public header.
|
||||
- Every public function is marked `RG_API` and prefixed with `rg_`.
|
||||
- Enums are an `int` typedef with named constants.
|
||||
- Nodes are referenced by opaque handles made of an index and a generation,
|
||||
never by raw pointers.
|
||||
- The user can supply an allocator, and internal allocation uses arenas.
|
||||
|
||||
## Context and ownership
|
||||
|
||||
All state lives in an `rg_context`, which is created with `rg_create` and
|
||||
passed explicitly to every call. There are no hidden globals. A context owns
|
||||
its node tree, style data, data model, event queue, text atlas, layout state,
|
||||
and, when the Lua module is attached, its own `lua_State`. This allows
|
||||
headless tests, multiple windows, and multiple Lua states in one process.
|
||||
The user-supplied allocator is set in the config struct at creation and is
|
||||
used for all internal allocation, including Lua and Clay. A context is not
|
||||
thread safe. It is used from one thread at a time, and separate contexts are
|
||||
fully independent.
|
||||
|
||||
## Main loop
|
||||
|
||||
The host owns the main loop. Regolith never blocks and never creates a
|
||||
window loop of its own. Each frame the host pushes input events, calls
|
||||
`rg_update` with a delta time, then calls `rg_render` to get a draw list
|
||||
that it hands to a backend. This is the same shape on native and on WASM,
|
||||
where the browser calls the host back once per frame.
|
||||
|
||||
## Input
|
||||
|
||||
The core accepts platform-neutral input events and never polls a platform
|
||||
API. The event kinds are pointer move, pointer button, scroll, key, and text.
|
||||
Key codes are Regolith's own enum and the SDL3 backend translates into them.
|
||||
Gamepad navigation will be added as a semantic layer (up, down, left, right,
|
||||
accept, cancel) that drives focus movement, so a game can map any device to
|
||||
it.
|
||||
|
||||
## Errors and logging
|
||||
|
||||
Functions that can fail return an `int` status where zero is success and
|
||||
negative values are errors. `rg_last_error(ctx)` returns the message for the
|
||||
most recent failure on that context. The config carries a log callback, a log
|
||||
level, and a userdata pointer. The default callback writes to stderr and the
|
||||
default level is trace, so logging is verbose until I lower it. Lua errors
|
||||
such as syntax errors and handler failures are logged and never replace the
|
||||
live tree, so a broken save during hot reload leaves the last good tree on
|
||||
screen.
|
||||
|
||||
## Testing
|
||||
|
||||
`ctest` runs the smoke tests. The core smoke test installs a counting
|
||||
allocator and fails if any bytes are live after `rg_destroy`. Every
|
||||
allocation, including Lua's, goes through the context allocator.
|
||||
|
||||
### Build and environment
|
||||
|
||||
The build system is CMake with Ninja. Development happens on NixOS using
|
||||
direnv and flakes, and the devShell provides CMake, Ninja, pkg-config, clang,
|
||||
clang-tools, SDL3, Emscripten, and lua-language-server. Emscripten's cache
|
||||
directory is set to `.emscripten-cache` inside the project. WASM builds go
|
||||
through `emcmake` into `build-wasm/`. Sources live in a flat `src/` with no
|
||||
generated files, so Zig and Odin users can compile them with their own
|
||||
toolchain.
|
||||
|
||||
### Milestone 1
|
||||
|
||||
A `main.lua` builds a `VBox` of `Label` and `Button` nodes. A stylesheet with
|
||||
classes and a `:hover` state styles them. Layout runs through Clay and draws
|
||||
through the SDL3 and OpenGL backend with stb_truetype text. A button click
|
||||
fires both a Lua handler and a named event in the polled queue. One
|
||||
transition eases a button background on hover. A data-bound label shows a
|
||||
counter that the button increments.
|
||||
|
||||
Text inputs and scrolling follow in milestone 2, along with focus handling.
|
||||
|
||||
|
||||
|
||||
### Open questions
|
||||
|
||||
- Node handle format.
|
||||
- Property system: how properties are named, typed, and stored.
|
||||
Reference in new issue
Block a user