nandi/jolt-nativepublic Fork 0
5ae197f7be55bd41678978f11e41a4282ffcf4b7
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Register vidya_set_title, which the phone cannot call without it 726f7c6 · on 5ae197f7be55bd41678978f11e41a4282ffcf4b7 · nandi · 18d ago
vidya_tree.h · 236 lines · 10.0 KBC Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
/*
 * 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);

/* The window's title. Declared here because the ABI exports it and the
 * Android glue has to name every symbol it registers — on the phone the
 * loader answers for none of them, so a symbol this header omits is one Jolt
 * cannot call. */
VIDYA_API void vidya_set_title(const char *title);

#ifdef __cplusplus
}
#endif

#endif /* VIDYA_TREE_H */