/* * Vidya's retained-tree ABI — the reactive half. * * `raylib/include/vidya.h` is push/pop: the caller writes its UI out top to * bottom every frame. That suits a program with a frame loop; it does not suit * a reactive toolkit, which keeps a component tree, diffs it, and emits * create/patch/append/remove against native widgets. * * This header is that widget layer. Nodes are integer handles, mutated by the * calls below; nothing is drawn until `vidya_tree_frame`, which paints the * whole tree at once. Interactions come back as a queue of events the caller * drains and routes to its own handlers — a callback cannot cross this * boundary, so identity does instead. * * The two halves share a window: open it with `vidya_open`, set the mode with * `vidya_set_mode`, and drive `vidya_tree_frame` in place of * `vidya_begin_frame` / `vidya_end_frame`. Do not mix them within one frame. * * Only the Rust/egui backend (`ffi/`) implements this header; the C/raylib * backend implements `vidya.h` alone. Every call stays on the thread that * called `vidya_open`, like the rest of the ABI. */ #ifndef VIDYA_TREE_H #define VIDYA_TREE_H #include "vidya.h" #ifdef __cplusplus extern "C" { #endif /* * Node handles are positive; 0 is "no node" — a failed allocation, and the * `sibling` argument that means "first position". * * Tags are hiccup names without the colon: * * containers window box hbox vbox page card frame scroll * widgets label title title-2 dim-label button checkbutton entry * separator spacer progress spinner status * * An unrecognized tag is kept and paints as a vertical box, so a caller ahead * of this backend still sees its children. * * Props are string-keyed, in the same names: * * every node sensitive (bool) * box orientation ("horizontal"|"vertical"), spacing, margin * page max-width * scroll orientation ("vertical"|"horizontal"|"both") * text widgets label, or text * button label, kind ("default"|"primary"|"destructive") * checkbutton label, active (bool) * entry text, placeholder, multiline (bool), rows * spacer size * progress value (0..1), label * status label, live (bool) */ /* An `:image` is bounded by `max-width` / `max-height` and, by default, never * drawn larger than its own pixels — enlarging a screenshot to fill a column * makes it worse. `upscale` says otherwise, for a picture whose size the * layout decided rather than the file: a video tile is a slot, and a camera * sending 480 wide into a 900-point slot should fill it. */ /* The window node, created on first use. Mount everything under it. */ VIDYA_API int vidya_tree_root(void); VIDYA_API int vidya_node_new(const char *tag); VIDYA_API void vidya_node_free(int node); VIDYA_API int vidya_node_exists(int node); VIDYA_API void vidya_node_set_str(int node, const char *key, const char *value); VIDYA_API void vidya_node_set_num(int node, const char *key, double value); VIDYA_API void vidya_node_set_bool(int node, const char *key, int value); VIDYA_API void vidya_node_clear_props(int node); /* * Reads answer the empty string / 0 for a prop that is unset or of another * type. The returned pointer belongs to the library and is valid only until * the next string-returning call on this thread — copy it before the next one. */ VIDYA_API const char *vidya_node_get_str(int node, const char *key); VIDYA_API double vidya_node_get_num(int node, const char *key); VIDYA_API int vidya_node_get_bool(int node, const char *key); /* * Reading the structure back. `vidya_node_tag` answers the canonical tag name * ("box" for both hbox and vbox), under the same borrowed-pointer rule as the * prop reads above; `vidya_node_child_at` answers 0 past the end. */ VIDYA_API const char *vidya_node_tag(int node); VIDYA_API int vidya_node_child_count(int node); VIDYA_API int vidya_node_child_at(int node, int index); /* * The subtree at `node` as pretty-printed hiccup — `[:tag {props} children...]`, * one node to a line — for logging, tests, and bug reports. `node` 0 means the * root, so `vidya_tree_dump(0)` is the whole window. Borrowed pointer, under * the same rule as the reads above. * * It answers what the tree *is*, read back from the arena, rather than what a * caller meant to build. Two caveats: `hbox` and `vbox` both dump as `box`, * with the orientation in the props, and handlers are absent because they * never crossed this boundary. */ VIDYA_API const char *vidya_tree_dump(int node); VIDYA_API int vidya_node_append(int parent, int child); /* Unparents AND frees `child` with everything under it. */ VIDYA_API void vidya_node_remove(int parent, int child); /* Moves `child` after `sibling`; `sibling` 0 means the first position. */ VIDYA_API int vidya_node_insert_after(int parent, int child, int sibling); /* Puts `new_child` where `old_child` was, and frees `old_child`. */ VIDYA_API int vidya_node_replace(int parent, int old_child, int new_child); /* Paint the whole tree as one frame. Inert with no window open. */ VIDYA_API void vidya_tree_frame(void); /* * Drain interactions. `vidya_tree_poll_event` dequeues one and answers 1 while * there was one; the accessors describe whichever was dequeued last. * * Event names are glimmer's handler props without the `on-`: * * click a button was pressed no payload * toggled a checkbutton changed num is the new state * change an entry's text changed text is the new text * activate Enter was pressed in an entry no payload * * A widget does not own its value: `toggled` and `change` write the new state * back into the node's props as well, so a caller that ignores the event still * sees a working control, and the next prop write is what settles it. */ VIDYA_API int vidya_tree_poll_event(void); VIDYA_API int vidya_tree_event_node(void); VIDYA_API const char *vidya_tree_event_name(void); VIDYA_API const char *vidya_tree_event_text(void); VIDYA_API double vidya_tree_event_num(void); /* The window * * Its size in points — not pixels — because whoever asks is about to lay * something out, and layout is in the units the widgets use. 0 before the * first frame. * * A caller wanting something to be a share of the window rather than a fixed * size needs this: how many of them there are and how much gap goes between * them is the caller's arithmetic, not something one widget can work out from * the space it was handed. * * Reads what egui last saw, so it answers between frames as well as during * one, and follows the window when it is dragged. */ VIDYA_API float vidya_screen_width(void); VIDYA_API float vidya_screen_height(void); /* Live frames * * Hand the tree a frame of raw pixels under `key`; any `:image` node whose * `feed` prop names it paints the latest one. Returns 1 when accepted. * * This is what `src` cannot do: a `src` image decodes a file and caches the * texture by its path for the life of the process, which is right for a * picture in a message and useless for a source that makes a new one thirty * times a second. A caller with its own pixels — a camera, a video decoder, a * renderer — pushes them here instead. * * `rgba` is width * height * 4 bytes, row-major, 8 bits a channel, * un-premultiplied. It is copied before the call returns; the caller may reuse * the buffer immediately. A length disagreeing with the dimensions is refused. * * Frames coalesce rather than queue: one arriving before the last was painted * replaces it, so a source faster than the window builds no backlog. * * Like the rest of this ABI, on the window's thread. `vidya_frame_drop` * forgets a feed and releases its texture — without it the last frame of a * source that has stopped keeps painting. */ VIDYA_API int vidya_frame_rgba(const char *key, int width, int height, const unsigned char *rgba); VIDYA_API int vidya_frame_drop(const char *key); /* Clipboard * * Write the picture on the system clipboard to `path` as a PNG; returns 1 when * there was one and it was written, 0 otherwise (an empty clipboard, text on * it, an unwritable path, or a platform with no image clipboard at all). * * A pasted image arrives through no event — egui carries only clipboard text * into a frame — so this is asked rather than waited for: bind it to whatever * gesture means paste, and read the file it names. Needs no window, and unlike * the rest of this ABI is not tied to the window's thread. */ VIDYA_API int vidya_clipboard_image_png(const char *path); /* Open a URL * * Hand `url` to whatever shows web pages here — xdg-open/open on the desktop, * an ACTION_VIEW intent on Android; returns 1 when something took it and 0 * otherwise, in which case show the URL and let the reader carry it across. * * Like the clipboard call above it needs no window and no particular thread. */ VIDYA_API int vidya_open_url(const char *url); /* Choose a picture * * `vidya_pick_image` asks the platform for its own picture chooser and returns * 1 when one opened. It does not answer with the picture: the reader is in * another screen by then, and may be for a while. Poll `vidya_picked_image` * with the path to put it at — 1 once there is one, and the answer is handed * over only once. * * Android only, and only where the host activity offers the chooser: it is * `void pickImage()` and `String takePickedImage()` on the activity named by * the manifest, which a plain NativeActivity does not have. 0 everywhere else, * which is a caller's cue to browse the filesystem itself. * * Both need no window and no particular thread. */ VIDYA_API int vidya_pick_image(void); VIDYA_API int vidya_picked_image(const char *path); #ifdef __cplusplus } #endif #endif /* VIDYA_TREE_H */