flake and design doc
This commit is contained in:
1 parent
8da6770ad6
commit
71ef8470f4
3 files changed
+233
-15
No files matched your search
+167
-15
@@ -1,20 +1,172 @@
|
||||
# Retained Mode GUI project
|
||||
# Regolith
|
||||
|
||||
## Hard requirements:
|
||||
- Must be written in C
|
||||
- Must be RETAINED MODE with a document structure that loads from disk
|
||||
- Must be versitile and independent so it can be used for Games, Desktop App GUI, etc.
|
||||
- Must be easy to initalize
|
||||
- Must have a friendly document language (My first thought is Lua as the documenting language)
|
||||
- Must have full Lua API for realtime changes and modability
|
||||
- Must be NATIVE and not use a webview or web backend at all
|
||||
- Used for NATIVE Desktop GUI applications as well as GUI for games
|
||||
- Must compile to native Linux, Windows, Mac, etc, as well as Web (WASM)
|
||||
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
|
||||
|
||||
## Medium requirements:
|
||||
- Use an existing project to calculate layouts and render commands
|
||||
- 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
|
||||
|
||||
## Soft requirements:
|
||||
- Bring your own rendering/platform backend (otherwise, just use OpenGL and SDL3)
|
||||
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
|
||||
|
||||
Rectangles, borders, and glyphs all reduce to quads. A backend only needs to
|
||||
manage textures, draw quad batches, and apply a scissor rect. Rendering
|
||||
backends are replaceable, and the SDL3 and OpenGL backend is the default.
|
||||
|
||||
## 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.
|
||||
|
||||
## 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
|
||||
|
||||
- Context and ownership of the `rg_context`.
|
||||
- Who owns the main loop.
|
||||
- The input event path and gamepad navigation.
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1793540411,
|
||||
"narHash": "sha256-fiPJ88izTVucjeo2EQ3jk4aWmLg03I1D4vxnX2QjEgE=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "0bb2eedc20e4739f29e8045034f2828ad7054f8f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
{
|
||||
description = "Regolith, a retained mode GUI library in C";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
systems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
forAllSystems = f: nixpkgs.lib.genAttrs systems (system:
|
||||
f nixpkgs.legacyPackages.${system});
|
||||
in {
|
||||
devShells = forAllSystems (pkgs: {
|
||||
default = pkgs.mkShell.override { stdenv = pkgs.clangStdenv; } {
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
clang-tools
|
||||
emscripten
|
||||
gdb
|
||||
valgrind
|
||||
lua5_4
|
||||
lua-language-server
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
sdl3
|
||||
libGL
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
export EM_CACHE="$PWD/.emscripten-cache"
|
||||
'';
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user