Tell a caller how big the window is
An :image can be bounded, but a caller wanting a row of tiles to divide the window between them cannot work out what to bound it by: how many there are, and how much gap goes between them, is arithmetic no single widget can do from the space it was handed. So vidya_screen_width and vidya_screen_height, in points rather than pixels, because whoever asks is laying out and layout is in the units the widgets use. Read off egui's last screen rect, so they answer between frames as well as during one and follow a window being dragged. Zero before the first frame, which is the honest answer to asking how big a window is before it has been painted. The width the window opened with was no answer: it is what was asked for once, and nothing updated it afterwards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
42dabb0 parent: c90f8af modified
crates/jolt-vidya/include/vidya_tree.h +17 -0 | @@ -132,6 +132,23 @@ VIDYA_API const char *vidya_tree_event_name(void); | ||
| 132 | 132 | VIDYA_API const char *vidya_tree_event_text(void); |
| 133 | 133 | VIDYA_API double vidya_tree_event_num(void); |
| 134 | 134 | |
| 135 | +/* The window | |
| 136 | + * | |
| 137 | + * Its size in points — not pixels — because whoever asks is about to lay | |
| 138 | + * something out, and layout is in the units the widgets use. 0 before the | |
| 139 | + * first frame. | |
| 140 | + * | |
| 141 | + * A caller wanting something to be a share of the window rather than a fixed | |
| 142 | + * size needs this: how many of them there are and how much gap goes between | |
| 143 | + * them is the caller's arithmetic, not something one widget can work out from | |
| 144 | + * the space it was handed. | |
| 145 | + * | |
| 146 | + * Reads what egui last saw, so it answers between frames as well as during | |
| 147 | + * one, and follows the window when it is dragged. | |
| 148 | + */ | |
| 149 | +VIDYA_API float vidya_screen_width(void); | |
| 150 | +VIDYA_API float vidya_screen_height(void); | |
| 151 | + | |
| 135 | 152 | /* Live frames |
| 136 | 153 | * |
| 137 | 154 | * Hand the tree a frame of raw pixels under `key`; any `:image` node whose |
| @@ -132,6 +132,23 @@ VIDYA_API const char *vidya_tree_event_name(void); | |||
| 132 | VIDYA_API const char *vidya_tree_event_text(void); | 132 | VIDYA_API const char *vidya_tree_event_text(void); |
| 133 | VIDYA_API double vidya_tree_event_num(void); | 133 | VIDYA_API double vidya_tree_event_num(void); |
| 134 | 134 | ||
| 135 | +/* The window | ||
| 136 | + * | ||
| 137 | + * Its size in points — not pixels — because whoever asks is about to lay | ||
| 138 | + * something out, and layout is in the units the widgets use. 0 before the | ||
| 139 | + * first frame. | ||
| 140 | + * | ||
| 141 | + * A caller wanting something to be a share of the window rather than a fixed | ||
| 142 | + * size needs this: how many of them there are and how much gap goes between | ||
| 143 | + * them is the caller's arithmetic, not something one widget can work out from | ||
| 144 | + * the space it was handed. | ||
| 145 | + * | ||
| 146 | + * Reads what egui last saw, so it answers between frames as well as during | ||
| 147 | + * one, and follows the window when it is dragged. | ||
| 148 | + */ | ||
| 149 | +VIDYA_API float vidya_screen_width(void); | ||
| 150 | +VIDYA_API float vidya_screen_height(void); | ||
| 151 | + | ||
| 135 | /* Live frames | 152 | /* Live frames |
| 136 | * | 153 | * |
| 137 | * Hand the tree a frame of raw pixels under `key`; any `:image` node whose | 154 | * Hand the tree a frame of raw pixels under `key`; any `:image` node whose |
modified
crates/jolt-vidya/src/app.rs +20 -0 | @@ -581,6 +581,26 @@ impl App { | ||
| 581 | 581 | /// |
| 582 | 582 | /// Returned together because widget calls need both, and they live in |
| 583 | 583 | /// different fields — splitting the borrow here keeps the call sites plain. |
| 584 | + /// The window's size in points, as egui last saw it. | |
| 585 | + /// | |
| 586 | + /// Points rather than pixels: a caller sizing something against this is | |
| 587 | + /// laying out, and layout is in the same units the widgets are. The | |
| 588 | + /// `width`/`height` the window was opened with are no answer — they are | |
| 589 | + /// what was asked for once, and say nothing about a window since dragged | |
| 590 | + /// wider. | |
| 591 | + /// | |
| 592 | + /// egui keeps the last screen rect on its context, so this answers between | |
| 593 | + /// frames as well as during one. Zero before the first frame, which is the | |
| 594 | + /// honest answer to asking how big a window is before it has been painted. | |
| 595 | + pub fn screen_size(&self) -> (f32, f32) { | |
| 596 | + let rect = self.handler.egui_ctx.screen_rect(); | |
| 597 | + if rect.width().is_finite() && rect.height().is_finite() { | |
| 598 | + (rect.width().max(0.0), rect.height().max(0.0)) | |
| 599 | + } else { | |
| 600 | + (0.0, 0.0) | |
| 601 | + } | |
| 602 | + } | |
| 603 | + | |
| 584 | 604 | pub fn ui(&mut self) -> Option<(&mut egui::Ui, &Theme)> { |
| 585 | 605 | let theme = &self.handler.theme; |
| 586 | 606 | self.stack.top().map(|ui| (ui, theme)) |
| @@ -581,6 +581,26 @@ impl App { | |||
| 581 | /// | 581 | /// |
| 582 | /// Returned together because widget calls need both, and they live in | 582 | /// Returned together because widget calls need both, and they live in |
| 583 | /// different fields — splitting the borrow here keeps the call sites plain. | 583 | /// different fields — splitting the borrow here keeps the call sites plain. |
| 584 | + /// The window's size in points, as egui last saw it. | ||
| 585 | + /// | ||
| 586 | + /// Points rather than pixels: a caller sizing something against this is | ||
| 587 | + /// laying out, and layout is in the same units the widgets are. The | ||
| 588 | + /// `width`/`height` the window was opened with are no answer — they are | ||
| 589 | + /// what was asked for once, and say nothing about a window since dragged | ||
| 590 | + /// wider. | ||
| 591 | + /// | ||
| 592 | + /// egui keeps the last screen rect on its context, so this answers between | ||
| 593 | + /// frames as well as during one. Zero before the first frame, which is the | ||
| 594 | + /// honest answer to asking how big a window is before it has been painted. | ||
| 595 | + pub fn screen_size(&self) -> (f32, f32) { | ||
| 596 | + let rect = self.handler.egui_ctx.screen_rect(); | ||
| 597 | + if rect.width().is_finite() && rect.height().is_finite() { | ||
| 598 | + (rect.width().max(0.0), rect.height().max(0.0)) | ||
| 599 | + } else { | ||
| 600 | + (0.0, 0.0) | ||
| 601 | + } | ||
| 602 | + } | ||
| 603 | + | ||
| 584 | pub fn ui(&mut self) -> Option<(&mut egui::Ui, &Theme)> { | 604 | pub fn ui(&mut self) -> Option<(&mut egui::Ui, &Theme)> { |
| 585 | let theme = &self.handler.theme; | 605 | let theme = &self.handler.theme; |
| 586 | self.stack.top().map(|ui| (ui, theme)) | 606 | self.stack.top().map(|ui| (ui, theme)) |
modified
crates/jolt-vidya/src/lib.rs +23 -0 | @@ -575,6 +575,29 @@ pub extern "C" fn vidya_tree_event_num() -> f64 { | ||
| 575 | 575 | with_tree(0.0, |tree| tree.current().map_or(0.0, |e| e.num)) |
| 576 | 576 | } |
| 577 | 577 | |
| 578 | +// ── The window ────────────────────────────────────────────────────────────── | |
| 579 | + | |
| 580 | +/// The window's width in points, or 0 before the first frame. | |
| 581 | +/// | |
| 582 | +/// Points, not pixels: whoever asks is about to lay something out, and layout | |
| 583 | +/// is in the units the widgets use. A caller that wants a tile to be a share | |
| 584 | +/// of the window rather than a fixed number of points needs this, because the | |
| 585 | +/// arithmetic — how many tiles, how much gap between them — is theirs and not | |
| 586 | +/// something a single widget can work out from the space it was handed. | |
| 587 | +/// | |
| 588 | +/// Reads what egui last saw, so it answers between frames as well as during | |
| 589 | +/// one, and follows the window when it is dragged. | |
| 590 | +#[no_mangle] | |
| 591 | +pub extern "C" fn vidya_screen_width() -> c_float { | |
| 592 | + with_app(0.0, |app| app.screen_size().0) | |
| 593 | +} | |
| 594 | + | |
| 595 | +/// The window's height in points, or 0 before the first frame. | |
| 596 | +#[no_mangle] | |
| 597 | +pub extern "C" fn vidya_screen_height() -> c_float { | |
| 598 | + with_app(0.0, |app| app.screen_size().1) | |
| 599 | +} | |
| 600 | + | |
| 578 | 601 | // ── Live frames ───────────────────────────────────────────────────────────── |
| 579 | 602 | |
| 580 | 603 | /// Hand the tree a frame of raw pixels under `key`, painted by any `:image` |
| @@ -575,6 +575,29 @@ pub extern "C" fn vidya_tree_event_num() -> f64 { | |||
| 575 | with_tree(0.0, |tree| tree.current().map_or(0.0, |e| e.num)) | 575 | with_tree(0.0, |tree| tree.current().map_or(0.0, |e| e.num)) |
| 576 | } | 576 | } |
| 577 | 577 | ||
| 578 | +// ── The window ────────────────────────────────────────────────────────────── | ||
| 579 | + | ||
| 580 | +/// The window's width in points, or 0 before the first frame. | ||
| 581 | +/// | ||
| 582 | +/// Points, not pixels: whoever asks is about to lay something out, and layout | ||
| 583 | +/// is in the units the widgets use. A caller that wants a tile to be a share | ||
| 584 | +/// of the window rather than a fixed number of points needs this, because the | ||
| 585 | +/// arithmetic — how many tiles, how much gap between them — is theirs and not | ||
| 586 | +/// something a single widget can work out from the space it was handed. | ||
| 587 | +/// | ||
| 588 | +/// Reads what egui last saw, so it answers between frames as well as during | ||
| 589 | +/// one, and follows the window when it is dragged. | ||
| 590 | +#[no_mangle] | ||
| 591 | +pub extern "C" fn vidya_screen_width() -> c_float { | ||
| 592 | + with_app(0.0, |app| app.screen_size().0) | ||
| 593 | +} | ||
| 594 | + | ||
| 595 | +/// The window's height in points, or 0 before the first frame. | ||
| 596 | +#[no_mangle] | ||
| 597 | +pub extern "C" fn vidya_screen_height() -> c_float { | ||
| 598 | + with_app(0.0, |app| app.screen_size().1) | ||
| 599 | +} | ||
| 600 | + | ||
| 578 | // ── Live frames ───────────────────────────────────────────────────────────── | 601 | // ── Live frames ───────────────────────────────────────────────────────────── |
| 579 | 602 | ||
| 580 | /// Hand the tree a frame of raw pixels under `key`, painted by any `:image` | 603 | /// Hand the tree a frame of raw pixels under `key`, painted by any `:image` |
modified
jolt/glimmer-vidya/src/glimmer_vidya/core.jolt +15 -0 | @@ -292,6 +292,21 @@ | ||
| 292 | 292 | [path] |
| 293 | 293 | (ffi/picked-image! path)) |
| 294 | 294 | |
| 295 | +(defn screen-size | |
| 296 | + "The window's size in points, as `[width height]`. `[0 0]` before the first | |
| 297 | + frame has been painted. | |
| 298 | + | |
| 299 | + For laying something out as a share of the window — a row of tiles that | |
| 300 | + should divide the width between them — where how many there are and how much | |
| 301 | + gap goes between them is the caller's arithmetic, and not something one | |
| 302 | + widget can work out from the space it was handed. | |
| 303 | + | |
| 304 | + Follows the window as it is dragged, so a component that reads it wants to be | |
| 305 | + re-rendered when it changes: keep it in a ratom rather than asking here at | |
| 306 | + render time, or the layout will be whatever it was on the first frame." | |
| 307 | + [] | |
| 308 | + [(ffi/screen-width) (ffi/screen-height)]) | |
| 309 | + | |
| 295 | 310 | (defn frame-rgba! |
| 296 | 311 | "Hand the backend a frame of live pixels under `key`. An `:image` node with |
| 297 | 312 | `:feed key` paints the latest one. |
| @@ -292,6 +292,21 @@ | |||
| 292 | [path] | 292 | [path] |
| 293 | (ffi/picked-image! path)) | 293 | (ffi/picked-image! path)) |
| 294 | 294 | ||
| 295 | +(defn screen-size | ||
| 296 | + "The window's size in points, as `[width height]`. `[0 0]` before the first | ||
| 297 | + frame has been painted. | ||
| 298 | + | ||
| 299 | + For laying something out as a share of the window — a row of tiles that | ||
| 300 | + should divide the width between them — where how many there are and how much | ||
| 301 | + gap goes between them is the caller's arithmetic, and not something one | ||
| 302 | + widget can work out from the space it was handed. | ||
| 303 | + | ||
| 304 | + Follows the window as it is dragged, so a component that reads it wants to be | ||
| 305 | + re-rendered when it changes: keep it in a ratom rather than asking here at | ||
| 306 | + render time, or the layout will be whatever it was on the first frame." | ||
| 307 | + [] | ||
| 308 | + [(ffi/screen-width) (ffi/screen-height)]) | ||
| 309 | + | ||
| 295 | (defn frame-rgba! | 310 | (defn frame-rgba! |
| 296 | "Hand the backend a frame of live pixels under `key`. An `:image` node with | 311 | "Hand the backend a frame of live pixels under `key`. An `:image` node with |
| 297 | `:feed key` paints the latest one. | 312 | `:feed key` paints the latest one. |
modified
jolt/glimmer-vidya/src/glimmer_vidya/ffi.jolt +7 -0 | @@ -60,6 +60,13 @@ | ||
| 60 | 60 | (ffi/defcfn event-text "vidya_tree_event_text" [] :string) |
| 61 | 61 | (ffi/defcfn event-num "vidya_tree_event_num" [] :double) |
| 62 | 62 | |
| 63 | + | |
| 64 | +;; --- the window -------------------------------------------------------------- | |
| 65 | +;; Its size in points, following the window as it is dragged. Points rather | |
| 66 | +;; than pixels because a caller asking is about to lay something out. | |
| 67 | +(ffi/defcfn screen-width "vidya_screen_width" [] :float) | |
| 68 | +(ffi/defcfn screen-height "vidya_screen_height" [] :float) | |
| 69 | + | |
| 63 | 70 | ;; --- live frames ------------------------------------------------------------- |
| 64 | 71 | ;; Pixels pushed in under a name, painted by an `:image` whose `:feed` names it. |
| 65 | 72 | ;; `rgba` is a foreign pointer to width*height*4 bytes, copied before the call |
| @@ -60,6 +60,13 @@ | |||
| 60 | (ffi/defcfn event-text "vidya_tree_event_text" [] :string) | 60 | (ffi/defcfn event-text "vidya_tree_event_text" [] :string) |
| 61 | (ffi/defcfn event-num "vidya_tree_event_num" [] :double) | 61 | (ffi/defcfn event-num "vidya_tree_event_num" [] :double) |
| 62 | 62 | ||
| 63 | + | ||
| 64 | +;; --- the window -------------------------------------------------------------- | ||
| 65 | +;; Its size in points, following the window as it is dragged. Points rather | ||
| 66 | +;; than pixels because a caller asking is about to lay something out. | ||
| 67 | +(ffi/defcfn screen-width "vidya_screen_width" [] :float) | ||
| 68 | +(ffi/defcfn screen-height "vidya_screen_height" [] :float) | ||
| 69 | + | ||
| 63 | ;; --- live frames ------------------------------------------------------------- | 70 | ;; --- live frames ------------------------------------------------------------- |
| 64 | ;; Pixels pushed in under a name, painted by an `:image` whose `:feed` names it. | 71 | ;; Pixels pushed in under a name, painted by an `:image` whose `:feed` names it. |
| 65 | ;; `rgba` is a foreign pointer to width*height*4 bytes, copied before the call | 72 | ;; `rgba` is a foreign pointer to width*height*4 bytes, copied before the call |