| Bring vidya in cfd3e36 nandi 19d ago | 1 | /* |
| 2 | * Vidya's retained-tree ABI — the reactive half. |
| 3 | * |
| 4 | * `raylib/include/vidya.h` is push/pop: the caller writes its UI out top to |
| 5 | * bottom every frame. That suits a program with a frame loop; it does not suit |
| 6 | * a reactive toolkit, which keeps a component tree, diffs it, and emits |
| 7 | * create/patch/append/remove against native widgets. |
| 8 | * |
| 9 | * This header is that widget layer. Nodes are integer handles, mutated by the |
| 10 | * calls below; nothing is drawn until `vidya_tree_frame`, which paints the |
| 11 | * whole tree at once. Interactions come back as a queue of events the caller |
| 12 | * drains and routes to its own handlers — a callback cannot cross this |
| 13 | * boundary, so identity does instead. |
| 14 | * |
| 15 | * The two halves share a window: open it with `vidya_open`, set the mode with |
| 16 | * `vidya_set_mode`, and drive `vidya_tree_frame` in place of |
| 17 | * `vidya_begin_frame` / `vidya_end_frame`. Do not mix them within one frame. |
| 18 | * |
| 19 | * Only the Rust/egui backend (`ffi/`) implements this header; the C/raylib |
| 20 | * backend implements `vidya.h` alone. Every call stays on the thread that |
| 21 | * called `vidya_open`, like the rest of the ABI. |
| 22 | */ |
| 23 | #ifndef VIDYA_TREE_H |
| 24 | #define VIDYA_TREE_H |
| 25 | |
| 26 | #include "vidya.h" |
| 27 | |
| 28 | #ifdef __cplusplus |
| 29 | extern "C" { |
| 30 | #endif |
| 31 | |
| 32 | /* |
| 33 | * Node handles are positive; 0 is "no node" — a failed allocation, and the |
| 34 | * `sibling` argument that means "first position". |
| 35 | * |
| 36 | * Tags are hiccup names without the colon: |
| 37 | * |
| 38 | * containers window box hbox vbox page card frame scroll |
| 39 | * widgets label title title-2 dim-label button checkbutton entry |
| 40 | * separator spacer progress spinner status |
| 41 | * |
| 42 | * An unrecognized tag is kept and paints as a vertical box, so a caller ahead |
| 43 | * of this backend still sees its children. |
| 44 | * |
| 45 | * Props are string-keyed, in the same names: |
| 46 | * |
| 47 | * every node sensitive (bool) |
| 48 | * box orientation ("horizontal"|"vertical"), spacing, margin |
| 49 | * page max-width |
| 50 | * scroll orientation ("vertical"|"horizontal"|"both") |
| 51 | * text widgets label, or text |
| 52 | * button label, kind ("default"|"primary"|"destructive") |
| 53 | * checkbutton label, active (bool) |
| 54 | * entry text, placeholder, multiline (bool), rows |
| 55 | * spacer size |
| 56 | * progress value (0..1), label |
| 57 | * status label, live (bool) |
| 58 | */ |
| 59 | |
| Fill the slot, and do not wedge on the way out e86c31c nandi 19d ago | 60 | /* An `:image` is bounded by `max-width` / `max-height` and, by default, never |
| 61 | * drawn larger than its own pixels — enlarging a screenshot to fill a column |
| 62 | * makes it worse. `upscale` says otherwise, for a picture whose size the |
| 63 | * layout decided rather than the file: a video tile is a slot, and a camera |
| 64 | * sending 480 wide into a 900-point slot should fill it. |
| 65 | */ |
| 66 | |
| Bring vidya in cfd3e36 nandi 19d ago | 67 | /* The window node, created on first use. Mount everything under it. */ |
| 68 | VIDYA_API int vidya_tree_root(void); |
| 69 | |
| 70 | VIDYA_API int vidya_node_new(const char *tag); |
| 71 | VIDYA_API void vidya_node_free(int node); |
| 72 | VIDYA_API int vidya_node_exists(int node); |
| 73 | |
| 74 | VIDYA_API void vidya_node_set_str(int node, const char *key, const char *value); |
| 75 | VIDYA_API void vidya_node_set_num(int node, const char *key, double value); |
| 76 | VIDYA_API void vidya_node_set_bool(int node, const char *key, int value); |
| 77 | VIDYA_API void vidya_node_clear_props(int node); |
| 78 | |
| 79 | /* |
| 80 | * Reads answer the empty string / 0 for a prop that is unset or of another |
| 81 | * type. The returned pointer belongs to the library and is valid only until |
| 82 | * the next string-returning call on this thread — copy it before the next one. |
| 83 | */ |
| 84 | VIDYA_API const char *vidya_node_get_str(int node, const char *key); |
| 85 | VIDYA_API double vidya_node_get_num(int node, const char *key); |
| 86 | VIDYA_API int vidya_node_get_bool(int node, const char *key); |
| 87 | |
| 88 | /* |
| 89 | * Reading the structure back. `vidya_node_tag` answers the canonical tag name |
| 90 | * ("box" for both hbox and vbox), under the same borrowed-pointer rule as the |
| 91 | * prop reads above; `vidya_node_child_at` answers 0 past the end. |
| 92 | */ |
| 93 | VIDYA_API const char *vidya_node_tag(int node); |
| 94 | VIDYA_API int vidya_node_child_count(int node); |
| 95 | VIDYA_API int vidya_node_child_at(int node, int index); |
| 96 | |
| 97 | /* |
| 98 | * The subtree at `node` as pretty-printed hiccup — `[:tag {props} children...]`, |
| 99 | * one node to a line — for logging, tests, and bug reports. `node` 0 means the |
| 100 | * root, so `vidya_tree_dump(0)` is the whole window. Borrowed pointer, under |
| 101 | * the same rule as the reads above. |
| 102 | * |
| 103 | * It answers what the tree *is*, read back from the arena, rather than what a |
| 104 | * caller meant to build. Two caveats: `hbox` and `vbox` both dump as `box`, |
| 105 | * with the orientation in the props, and handlers are absent because they |
| 106 | * never crossed this boundary. |
| 107 | */ |
| 108 | VIDYA_API const char *vidya_tree_dump(int node); |
| 109 | |
| 110 | VIDYA_API int vidya_node_append(int parent, int child); |
| 111 | /* Unparents AND frees `child` with everything under it. */ |
| 112 | VIDYA_API void vidya_node_remove(int parent, int child); |
| 113 | /* Moves `child` after `sibling`; `sibling` 0 means the first position. */ |
| 114 | VIDYA_API int vidya_node_insert_after(int parent, int child, int sibling); |
| 115 | /* Puts `new_child` where `old_child` was, and frees `old_child`. */ |
| 116 | VIDYA_API int vidya_node_replace(int parent, int old_child, int new_child); |
| 117 | |
| 118 | /* Paint the whole tree as one frame. Inert with no window open. */ |
| 119 | VIDYA_API void vidya_tree_frame(void); |
| 120 | |
| 121 | /* |
| 122 | * Drain interactions. `vidya_tree_poll_event` dequeues one and answers 1 while |
| 123 | * there was one; the accessors describe whichever was dequeued last. |
| 124 | * |
| 125 | * Event names are glimmer's handler props without the `on-`: |
| 126 | * |
| 127 | * click a button was pressed no payload |
| 128 | * toggled a checkbutton changed num is the new state |
| 129 | * change an entry's text changed text is the new text |
| 130 | * activate Enter was pressed in an entry no payload |
| 131 | * |
| 132 | * A widget does not own its value: `toggled` and `change` write the new state |
| 133 | * back into the node's props as well, so a caller that ignores the event still |
| 134 | * sees a working control, and the next prop write is what settles it. |
| 135 | */ |
| 136 | VIDYA_API int vidya_tree_poll_event(void); |
| 137 | VIDYA_API int vidya_tree_event_node(void); |
| 138 | VIDYA_API const char *vidya_tree_event_name(void); |
| 139 | VIDYA_API const char *vidya_tree_event_text(void); |
| 140 | VIDYA_API double vidya_tree_event_num(void); |
| 141 | |
| Tell a caller how big the window is 42dabb0 nandi 19d ago | 142 | /* The window |
| 143 | * |
| 144 | * Its size in points — not pixels — because whoever asks is about to lay |
| 145 | * something out, and layout is in the units the widgets use. 0 before the |
| 146 | * first frame. |
| 147 | * |
| 148 | * A caller wanting something to be a share of the window rather than a fixed |
| 149 | * size needs this: how many of them there are and how much gap goes between |
| 150 | * them is the caller's arithmetic, not something one widget can work out from |
| 151 | * the space it was handed. |
| 152 | * |
| 153 | * Reads what egui last saw, so it answers between frames as well as during |
| 154 | * one, and follows the window when it is dragged. |
| 155 | */ |
| 156 | VIDYA_API float vidya_screen_width(void); |
| 157 | VIDYA_API float vidya_screen_height(void); |
| 158 | |
| Bring vidya in cfd3e36 nandi 19d ago | 159 | /* Live frames |
| 160 | * |
| 161 | * Hand the tree a frame of raw pixels under `key`; any `:image` node whose |
| 162 | * `feed` prop names it paints the latest one. Returns 1 when accepted. |
| 163 | * |
| 164 | * This is what `src` cannot do: a `src` image decodes a file and caches the |
| 165 | * texture by its path for the life of the process, which is right for a |
| 166 | * picture in a message and useless for a source that makes a new one thirty |
| 167 | * times a second. A caller with its own pixels — a camera, a video decoder, a |
| 168 | * renderer — pushes them here instead. |
| 169 | * |
| 170 | * `rgba` is width * height * 4 bytes, row-major, 8 bits a channel, |
| 171 | * un-premultiplied. It is copied before the call returns; the caller may reuse |
| 172 | * the buffer immediately. A length disagreeing with the dimensions is refused. |
| 173 | * |
| 174 | * Frames coalesce rather than queue: one arriving before the last was painted |
| 175 | * replaces it, so a source faster than the window builds no backlog. |
| 176 | * |
| 177 | * Like the rest of this ABI, on the window's thread. `vidya_frame_drop` |
| 178 | * forgets a feed and releases its texture — without it the last frame of a |
| 179 | * source that has stopped keeps painting. |
| 180 | */ |
| 181 | VIDYA_API int vidya_frame_rgba(const char *key, int width, int height, |
| 182 | const unsigned char *rgba); |
| 183 | VIDYA_API int vidya_frame_drop(const char *key); |
| 184 | |
| 185 | /* Clipboard |
| 186 | * |
| 187 | * Write the picture on the system clipboard to `path` as a PNG; returns 1 when |
| 188 | * there was one and it was written, 0 otherwise (an empty clipboard, text on |
| 189 | * it, an unwritable path, or a platform with no image clipboard at all). |
| 190 | * |
| 191 | * A pasted image arrives through no event — egui carries only clipboard text |
| 192 | * into a frame — so this is asked rather than waited for: bind it to whatever |
| 193 | * gesture means paste, and read the file it names. Needs no window, and unlike |
| 194 | * the rest of this ABI is not tied to the window's thread. |
| 195 | */ |
| 196 | VIDYA_API int vidya_clipboard_image_png(const char *path); |
| 197 | |
| 198 | /* Open a URL |
| 199 | * |
| 200 | * Hand `url` to whatever shows web pages here — xdg-open/open on the desktop, |
| 201 | * an ACTION_VIEW intent on Android; returns 1 when something took it and 0 |
| 202 | * otherwise, in which case show the URL and let the reader carry it across. |
| 203 | * |
| 204 | * Like the clipboard call above it needs no window and no particular thread. |
| 205 | */ |
| 206 | VIDYA_API int vidya_open_url(const char *url); |
| 207 | |
| Catch up with vidya c90f8af nandi 19d ago | 208 | /* Choose a picture |
| 209 | * |
| 210 | * `vidya_pick_image` asks the platform for its own picture chooser and returns |
| 211 | * 1 when one opened. It does not answer with the picture: the reader is in |
| 212 | * another screen by then, and may be for a while. Poll `vidya_picked_image` |
| 213 | * with the path to put it at — 1 once there is one, and the answer is handed |
| 214 | * over only once. |
| 215 | * |
| 216 | * Android only, and only where the host activity offers the chooser: it is |
| 217 | * `void pickImage()` and `String takePickedImage()` on the activity named by |
| 218 | * the manifest, which a plain NativeActivity does not have. 0 everywhere else, |
| 219 | * which is a caller's cue to browse the filesystem itself. |
| 220 | * |
| 221 | * Both need no window and no particular thread. |
| 222 | */ |
| 223 | VIDYA_API int vidya_pick_image(void); |
| 224 | VIDYA_API int vidya_picked_image(const char *path); |
| 225 | |
| Bring vidya in cfd3e36 nandi 19d ago | 226 | #ifdef __cplusplus |
| 227 | } |
| 228 | #endif |
| 229 | |
| 230 | #endif /* VIDYA_TREE_H */ |