kui_core/ui.rs
1//! [`Ui`]: the frame builder a Rust view declares its tree through.
2//!
3//! A `Ui` borrows a [`Core`] for the length of one frame. It is a thin,
4//! safe facade over the core's flat builder methods (which are also the FFI
5//! surface): every call opens, fills or closes a node, or reads a fact the
6//! last frame established. It never captures app state, so `view(&state)`
7//! and `update(&mut state)` cannot conflict.
8//!
9//! The shape of a view: containers open with [`Ui::with`] (scoped) or
10//! [`Ui::open`]/[`Ui::close`], leaves with [`Ui::leaf`] and [`Ui::text`],
11//! and each node's look and behaviour is a [`NodeSpec`]. Nodes are keyed by
12//! position unless a `_keyed` or `_indexed` form gives them a stable
13//! identity, which anything retained across frames (focus, scrolling, an
14//! edit buffer, a transition) needs.
15//!
16//! ```rust
17//! use kui_core::{Color, Core, NodeSpec, Size, Sizing, TextStyle, widgets};
18//!
19//! let mut core = Core::new();
20//! let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
21//! let theme = ui.theme();
22//! ui.configure_root(NodeSpec::column().fill().pad(12.0).gap(8.0));
23//!
24//! // A toolbar: a row of buttons, keyed by their labels.
25//! ui.with(NodeSpec::row().gap(6.0), |ui| {
26//! widgets::button(ui, "Open", "open");
27//! widgets::button(ui, "Save", "save");
28//! });
29//!
30//! // A panel that grows to fill the rest, with a label in it.
31//! let panel = ui.with_keyed(
32//! "panel",
33//! NodeSpec::column().fill().pad(8.0).bg(theme.surface).radius(6.0),
34//! |ui| {
35//! ui.text("Ready.", TextStyle::new(14.0).color(theme.muted));
36//! ui.leaf(NodeSpec::row().size(Sizing::GROW, 1.0).bg(Color::hex(0x80808080)));
37//! },
38//! );
39//! assert!(!ui.is_hovered(panel)); // nothing has moved the pointer yet
40//! ui.finish();
41//! ```
42//!
43//! [`Ui::finish`] is the one way out of a frame: it runs layout and
44//! emission and leaves the results in [`Core::output`].
45
46use crate::edit::EditOptions;
47use crate::env::Env;
48use crate::geom::{Rect, Size, Vec2};
49use crate::key::Key;
50use crate::line::Stroke;
51use crate::runtime::Core;
52use crate::slot::{Fill, Slot};
53use crate::spec::{NodeSpec, TextStyle};
54use crate::text::Span;
55use crate::tree::OriginId;
56use crate::value::Value;
57use crate::window::{WindowCommand, WindowConfig, WindowId};
58
59pub struct Ui<'a> {
60 core: &'a mut Core,
61 /// What fills the slots this frame declares (`Core::frame_with`);
62 /// `None` for a frame begun without one, and inside a fill — an
63 /// extension's `slot` declares and fills nothing.
64 filler: Option<&'a mut dyn Fill>,
65}
66
67impl<'a> Ui<'a> {
68 pub(crate) fn new(core: &'a mut Core) -> Self {
69 Self { core, filler: None }
70 }
71
72 /// [`Self::wrap`] with something to fill the slots the frame declares
73 /// — for a frontend that drives `Core` directly and keeps an
74 /// extension list of its own (the C context, a headless Node one), so
75 /// its `slot` and `finish` are this type's rather than a copy of them.
76 pub fn with_filler(core: &'a mut Core, filler: &'a mut dyn Fill) -> Self {
77 Self {
78 core,
79 filler: Some(filler),
80 }
81 }
82
83 /// Escape hatch to the underlying core (e.g. for FFI view callbacks).
84 pub fn core(&mut self) -> &mut Core {
85 self.core
86 }
87
88 /// The inverse escape hatch: wraps a borrowed core mid-frame so foreign
89 /// frontends that drive `Core` directly (FFI, Node) can call `widgets::*`.
90 pub fn wrap(core: &'a mut Core) -> Self {
91 Self { core, filler: None }
92 }
93
94 /// Declares a slot here, with no parameters: whatever fills it draws
95 /// now, as children of the node this view is inside, at this
96 /// position among its siblings. `name` is the full name,
97 /// `namespace/slot` — the namespace the host gave the extension when
98 /// it loaded it, and the slot in the extension's own vocabulary
99 /// (`"fs/panel"`). `"ns/root"` is the fill that follows the host's
100 /// view for an extension listing no slots, and declaring it moves
101 /// that fill here.
102 pub fn slot(&mut self, name: &str) {
103 self.slot_with(name, &crate::slot::NULL_PARAMS);
104 }
105
106 /// `slot` with parameters the extension reads this frame
107 /// (`Slot::params`); a `Value` because it is the type that already
108 /// crosses to an extension. Nothing is retained — pass what is true
109 /// this frame, every frame.
110 ///
111 /// Whether the slot was declared: false for a name this frame already
112 /// declared (which warns, `duplicate-slot`) or outside a frame. True
113 /// whether or not anything filled it — a host with nothing loaded
114 /// still gets a placed, empty node to lay out around.
115 pub fn slot_with(&mut self, name: &str, params: &Value) -> bool {
116 let Some(key) = self.core.begin_slot(name) else {
117 return false;
118 };
119 if let Some(filler) = self.filler.as_deref_mut() {
120 filler.fill(name, key, params, &mut Ui::new(self.core));
121 }
122 true
123 }
124
125 /// Whether the full name `name` was declared this frame so far.
126 pub fn slot_declared(&self, name: &str) -> bool {
127 self.core.slot_declared(name)
128 }
129
130 /// Declares a devtools tab an extension fills: `name` is the tab's
131 /// identity, `label` what the strip shows,
132 /// `slot` the full slot name (`"ts/panel"`) the extension names.
133 /// While the tab is the one on show, the panel declares the slot in
134 /// the tab's body and the fill is drawn there; otherwise the slot is
135 /// not declared and the extension is not asked, though its naming
136 /// the slot raises no `unknown-slot`. Made every frame, panel on or
137 /// off. A name declared twice in a frame warns `duplicate-tab`.
138 pub fn devtools_tab(&mut self, name: &str, label: &str, slot: &str) {
139 self.core.devtools_tab_declare(name, label, Some(slot));
140 }
141
142 /// Declares a devtools tab the host draws itself, and draws it
143 /// **only when it is shown**: `f` runs
144 /// when the panel is on, docked in this window, and `name` is the
145 /// tab on show — otherwise this declares and returns, and the tab
146 /// costs nothing. What `f` builds is the host's: its keys, labels
147 /// and origin, its events reaching the host untouched — laid out
148 /// and painted as a layer over the panel's tab body, clipped to it,
149 /// in the dock's focus region. The panel's facts are read through
150 /// `devtools_selected` and its siblings.
151 pub fn devtools_tab_with(&mut self, name: &str, label: &str, f: impl FnOnce(&mut Ui<'_>)) {
152 if !self.core.devtools_tab_declare(name, label, None) {
153 return;
154 }
155 if !self.core.devtools_tab_shown(name) {
156 return;
157 }
158 self.core.devtools_tab_open(name);
159 f(self);
160 self.core.close();
161 }
162
163 /// Whether the host form of tab `name` is shown this frame — what
164 /// `devtools_tab_with` asks before running its closure, for a caller
165 /// that builds the content some other way.
166 pub fn devtools_tab_shown(&self, name: &str) -> bool {
167 self.core.devtools_tab_shown(name)
168 }
169
170 /// The bare declaration either form makes: `slot` for the extension
171 /// form, `None` for a host form whose
172 /// content is built some other way or not at all this frame. Whether
173 /// the declaration stood — false for a name already declared this
174 /// frame (`duplicate-tab`) or outside a frame.
175 pub fn devtools_tab_declare(&mut self, name: &str, label: &str, slot: Option<&str>) -> bool {
176 self.core.devtools_tab_declare(name, label, slot)
177 }
178
179 /// The host form for a binding that decided the laziness on its own
180 /// side (a Node encoder or a Lua converter that already read which
181 /// tab is on show): declares the tab and builds
182 /// `f` as its content **whether or not** the tab is shown here — a
183 /// content whose body the panel did not build anchors to nothing
184 /// and paints nothing, and a name already declared this frame
185 /// (`duplicate-tab`) builds into a node of no size, so the caller's
186 /// stream stays in step either way. `devtools_tab_with` is the door
187 /// for a caller that can skip the work.
188 pub fn devtools_tab_declared(&mut self, name: &str, label: &str, f: impl FnOnce(&mut Ui<'_>)) {
189 if self.core.devtools_tab_declare(name, label, None) {
190 self.core.devtools_tab_open(name);
191 } else {
192 self.core.open(
193 NodeSpec::column()
194 .width(crate::spec::Sizing::Fixed(0.0))
195 .height(crate::spec::Sizing::Fixed(0.0))
196 .clip(),
197 );
198 }
199 f(self);
200 self.core.close();
201 }
202
203 /// The panel's selected node (`Core::devtools_selected`).
204 pub fn devtools_selected(&self) -> Option<Key> {
205 self.core.devtools_selected()
206 }
207
208 /// The panel's hovered tree row (`Core::devtools_hovered`).
209 pub fn devtools_hovered(&self) -> Option<Key> {
210 self.core.devtools_hovered()
211 }
212
213 /// The node the picker is over (`Core::devtools_picked`).
214 pub fn devtools_picked(&self) -> Option<Key> {
215 self.core.devtools_picked()
216 }
217
218 /// Whether the panel's picker is up (`Core::devtools_picking`).
219 pub fn devtools_picking(&self) -> bool {
220 self.core.devtools_picking()
221 }
222
223 /// The tab the panel is on, by name (`Core::devtools_current_tab`).
224 pub fn devtools_current_tab(&self) -> String {
225 self.core.devtools_current_tab()
226 }
227
228 /// Loads `ext` under `namespace` into the list filling this frame's
229 /// slots, and answers with the origin it got. This is how an
230 /// extension hosts an extension of its own: the guest asks mid-frame,
231 /// when it knows what it wants, and the plugin lands in the same list
232 /// as the host's own — one namespace map, one origin per extension,
233 /// however deep the loading went (`crate::slot`).
234 ///
235 /// Fails when the namespace is taken, or empty with an extension that
236 /// names itself nothing, exactly as
237 /// `Extensions::push_as` does, and when this frame was begun without
238 /// a filler (`Core::frame`) or with one that is not a list.
239 pub fn add_extension(
240 &mut self,
241 namespace: &str,
242 ext: Box<dyn crate::runtime::Extension>,
243 ) -> Result<OriginId, String> {
244 match self.filler.as_deref_mut() {
245 Some(filler) => filler.add(namespace, ext),
246 None => Err(format!(
247 "cannot load `{namespace}`: this frame declares no slots to fill"
248 )),
249 }
250 }
251
252 /// Runs `f` as the fill of `slot` under `origin`: nodes it opens are
253 /// tagged with the origin, keyed under the slot's key, and closed
254 /// for it if it leaves any open. What a `Fill` implementation calls
255 /// per extension; see `Core::fill`.
256 pub fn fill(&mut self, origin: OriginId, slot: &Slot<'_>, f: impl FnOnce(&mut Ui<'_>)) {
257 self.core.fill(slot, origin, f);
258 }
259
260 /// `fill`, with `filler` answering the slots the fill declares — an
261 /// extension hosting extensions of its own. `Extensions::fill_one`
262 /// passes itself, which is what makes one namespace map do for every
263 /// level; see `crate::slot`.
264 pub fn fill_within(
265 &mut self,
266 origin: OriginId,
267 slot: &Slot<'_>,
268 filler: &mut dyn Fill,
269 f: impl FnOnce(&mut Ui<'_>),
270 ) {
271 self.core.fill_within(slot, origin, Some(filler), f);
272 }
273
274 /// The origin the nodes opened right now are tagged with:
275 /// `OriginId::HOST` in the host's own view, the filling extension's
276 /// inside a fill. What records who declared a slot, and so where the
277 /// replies of whatever fills it go.
278 pub fn origin(&self) -> OriginId {
279 self.core.origin()
280 }
281
282 /// The viewport this frame lays out into: the window, less the
283 /// devtools' dock while the panel is docked (`Core::viewport`).
284 pub fn viewport(&self) -> Size {
285 self.core.viewport()
286 }
287
288 /// The env reading's inputs, the frame's facts included
289 /// (`Core::env_facts`): what a binding's `env` table is filled from.
290 pub fn env_facts(&self) -> crate::schema::EnvFacts {
291 self.core.env_facts()
292 }
293
294 /// Host facts pushed by the frame driver (refresh rate, focus).
295 pub fn env(&self) -> Env {
296 self.core.env
297 }
298
299 /// This frame's palette: the named colours the stock widgets paint
300 /// with, derived from `env.system` unless the app pinned something
301 /// else (see [`crate::theme`]).
302 ///
303 /// By value, because it is [`Copy`] and a view that took a reference
304 /// could not then touch `ui`. `let t = ui.theme();` at the top of a
305 /// widget is the idiom.
306 pub fn theme(&self) -> crate::theme::Theme {
307 *self.core.theme()
308 }
309
310 /// Whether anyone chose the theme's accent, or it is kui's fallback
311 /// blue — see [`Core::has_accent`](crate::runtime::Core::has_accent).
312 pub fn has_accent(&self) -> bool {
313 self.core.has_accent()
314 }
315
316 /// The sizes the stock widgets are built from
317 /// ([`Metrics`](crate::metrics::Metrics)), by value like the theme and
318 /// for the same reason. What a view reads to make its own controls
319 /// agree with the stock ones on a radius and a padding.
320 pub fn metrics(&self) -> crate::metrics::Metrics {
321 *self.core.metrics()
322 }
323
324 /// Declares the tokens this origin references by name (see
325 /// [`crate::tokens`] and
326 /// [`Core::set_tokens`](crate::runtime::Core::set_tokens)). Inside a
327 /// fill the table is the extension's own.
328 pub fn set_tokens(&mut self, tokens: crate::tokens::Tokens) {
329 self.core.set_tokens(tokens);
330 }
331
332 /// What a `$name` resolves to this frame — the running origin's
333 /// table over the host's, the roles in front of both — for a view
334 /// that reads a token by name rather than holding its id.
335 pub fn tokens(&self) -> crate::tokens::TokenLookup<'_> {
336 self.core.token_lookup()
337 }
338
339 /// A colour token by name, this frame's half. A name that resolves
340 /// to nothing, or to a length, raises `unknown-token` and answers
341 /// `None`, so the view leaves the slot at its default
342 /// (`.bg(ui.token_color("peach").unwrap_or(t.surface))`) rather than
343 /// painting a transparent that would hide the node a typo was on.
344 pub fn token_color(&mut self, name: &str) -> Option<crate::color::Color> {
345 match self.core.token_lookup().color(name) {
346 Ok(c) => Some(c),
347 Err(e) => {
348 self.core.warn_unknown_token(&e);
349 None
350 }
351 }
352 }
353
354 /// A length token by name; unknown warns and answers `None`, as above.
355 pub fn token_length(&mut self, name: &str) -> Option<f32> {
356 match self.core.token_lookup().length(name) {
357 Ok(v) => Some(v),
358 Err(e) => {
359 self.core.warn_unknown_token(&e);
360 None
361 }
362 }
363 }
364
365 /// Declares this frame's window title (declare every frame you care;
366 /// the driver diffs and applies changes).
367 pub fn window_title(&mut self, title: &str) {
368 self.core.set_window_title(title);
369 }
370
371 /// Declares that this frame wants the window kept above every other
372 /// app's; see [`crate::Core::set_always_on_top`]. Declare it every
373 /// frame you want it — a frame that does not lowers the window again,
374 /// which is what makes a pin button a toggle — and read whether the
375 /// platform agreed from `env().window.always_on_top`.
376 pub fn always_on_top(&mut self, on_top: bool) {
377 self.core.set_always_on_top(on_top);
378 }
379
380 /// Declares that this frame wants secure keyboard entry while the
381 /// window has the keyboard (a password prompt); see
382 /// [`crate::Core::set_secure_input`]. Declare it every frame the
383 /// prompt is up: a frame that does not turns it off.
384 pub fn secure_input(&mut self, on: bool) {
385 self.core.set_secure_input(on);
386 }
387
388 /// Declares which Option keys act as Alt in this window on macOS, so
389 /// Option-u arrives as the chord `A-u` rather than composing an
390 /// accent; see [`crate::Core::set_option_as_alt`]. Declare it every
391 /// frame: a frame that does not gives the Option keys back to the
392 /// layout.
393 pub fn option_as_alt(&mut self, option_as_alt: crate::OptionAsAlt) {
394 self.core.set_option_as_alt(option_as_alt);
395 }
396
397 /// Declares that this window takes the keyboard as keys, with the
398 /// platform's input method off — no composition, and on a Mac no dead
399 /// keys and no press-and-hold, so a held letter repeats; see
400 /// [`crate::Core::set_ime_off`]. A modal editor's normal mode. Declare
401 /// it every frame: a frame that does not gives the IME back.
402 pub fn ime_off(&mut self, off: bool) {
403 self.core.set_ime_off(off);
404 }
405
406 /// Declares that a window named `name` exists this frame; see
407 /// `Core::declare_window`. It opens on the first frame that declares
408 /// it (`config` is read then and never again), stays open while any
409 /// window's frame keeps declaring it, and closes when none does.
410 /// `view` is then called for it too, with [`Ui::window_name`] saying
411 /// which window is being drawn.
412 pub fn window(&mut self, name: &str, config: WindowConfig) {
413 self.core.declare_window(name, config);
414 }
415
416 /// The name of the window this frame is drawing: `"main"` for the one
417 /// the launcher opened, else the name the declaration that opened it
418 /// used. `env().window.id` is the same window as a number.
419 pub fn window_name(&self) -> std::rc::Rc<str> {
420 self.core.window_name()
421 }
422
423 /// Sets the origin the nodes opened from here on are tagged with; see
424 /// [`Ui::origin`].
425 pub fn set_origin(&mut self, origin: OriginId) {
426 self.core.set_origin(origin);
427 }
428
429 /// The root node's spec for this frame: its direction, padding, gap
430 /// and background. Call it first; the default root is a fit column.
431 pub fn configure_root(&mut self, spec: NodeSpec) {
432 self.core.configure_root(spec);
433 }
434
435 /// The key a child opened under `label` would get here, without
436 /// opening it: read it before the node exists to ask `is_hovered` or
437 /// `is_focused` while building it.
438 pub fn child_key(&self, label: &str) -> Key {
439 self.core.child_key(label)
440 }
441
442 /// The key the `i`th child gets from auto-keying; see `open_indexed`.
443 pub fn child_key_indexed(&self, i: u64) -> Key {
444 self.core.child_key_indexed(i)
445 }
446
447 /// Whether the pointer is over `key`, as of the last input. Only a
448 /// node that tracks hover answers true (one with a click, a drag, a
449 /// hover background or `hoverable`).
450 pub fn is_hovered(&self, key: Key) -> bool {
451 self.core.is_hovered(key)
452 }
453
454 /// Whether the primary button is held on `key`.
455 pub fn is_pressed(&self, key: Key) -> bool {
456 self.core.is_pressed(key)
457 }
458
459 /// Whether files dragged in from the OS are over `key`, for
460 /// drop-dependent *layout*; a colour swap is `drop_bg`.
461 pub fn is_drop_target(&self, key: Key) -> bool {
462 self.core.is_drop_target(key)
463 }
464
465 /// The zone the dragged files are over, if any.
466 pub fn drop_target(&self) -> Option<Key> {
467 self.core.drop_target()
468 }
469
470 /// Whether any member of a hover group (`NodeSpec::hover_group`) is
471 /// hovered; the id comes from `NodeSpec::hover_group_id`.
472 pub fn is_group_hovered(&self, group: u64) -> bool {
473 self.core.is_group_hovered(group)
474 }
475
476 /// Whether any member of a hover group is pressed.
477 pub fn is_group_pressed(&self, group: u64) -> bool {
478 self.core.is_group_pressed(group)
479 }
480
481 /// Physical modifier state (the host also receives it as a
482 /// `{kind="modifiers"}` event whenever it changes).
483 pub fn modifiers(&self) -> crate::input::KeyMods {
484 self.core.modifiers()
485 }
486
487 /// The exit `key` leaves by if this frame stops declaring it (backlog
488 /// F136); see `Core::set_exit`. The view that removes a card thrown
489 /// by a button names the throw in the same frame, with no frame drawn
490 /// first to aim it.
491 pub fn exit_with(&mut self, key: Key, exit: crate::enter::Enter) {
492 self.core.set_exit(key, exit);
493 }
494
495 /// Asks for a frame at `at` on the frame clock (backlog F135); see
496 /// `Core::request_frame_at`. `ui.request_frame_at(ui.now() + 3.0)` is
497 /// a toast's expiry, with no thread and nothing owed in between.
498 pub fn request_frame_at(&mut self, at: f64) {
499 self.core.request_frame_at(at);
500 }
501
502 /// The frame clock in the driver's seconds (backlog F134); see
503 /// `Core::now`. A view's deadlines read from it — a toast's expiry, a
504 /// sequence's beats — so a test's `advance` moves them with the
505 /// core's own easing.
506 pub fn now(&self) -> f64 {
507 self.core.now()
508 }
509
510 /// The caret's blink phase — `true` draws it; see
511 /// `Core::caret_visible`. A custom editor draws its caret node on the
512 /// on phase and skips it on the off, keeping the `caret` row on its
513 /// `line` either way (that row is what the clock is armed on).
514 pub fn caret_visible(&self) -> bool {
515 self.core.caret_visible()
516 }
517
518 /// Asks for one more frame after this one; see `Core::request_frame`.
519 #[track_caller]
520 pub fn request_frame(&mut self) {
521 self.core.request_frame();
522 }
523
524 /// Asks for one more frame on kui's own behalf (a stock widget's, not
525 /// the app's), named `why` in a trace.
526 #[track_caller]
527 pub(crate) fn owe_frame(&mut self, why: &'static str) {
528 self.core.owe_frame(why);
529 }
530
531 /// Measures text the way layout would, without adding a node; see
532 /// `Core::measure_text`. Sizing a column to its widest label, or
533 /// choosing a tier that fits, is arithmetic on these numbers instead
534 /// of hand-tuned constants. The metrics do not scale linearly:
535 /// `measured × zoom` is not `measure(size × zoom)`, because shaping
536 /// rounds per size, so anything that zooms measures at the size it
537 /// draws.
538 pub fn measure_text(
539 &mut self,
540 content: &str,
541 style: &TextStyle,
542 max_w: Option<f32>,
543 ) -> crate::text::TextMetrics {
544 self.core.measure_text(content, style, max_w)
545 }
546
547 /// Where a point lands in the text node `key` drew, as a byte offset
548 /// and a visual line; see `Core::text_hit`. During a build it answers
549 /// from the last frame, which is the layout a click was made against.
550 pub fn text_hit(&self, key: Key, point: Vec2) -> Option<crate::text::TextHit> {
551 self.core.text_hit(key, point)
552 }
553
554 /// The window's selected text — a `selectable` scope's, or the
555 /// focused editor's; see `Core::copy_selection`.
556 pub fn selection_text(&self) -> Option<String> {
557 self.core.copy_selection()
558 }
559
560 /// The text selection's two ends as the drag made them — anchor, then
561 /// focus — each a virtualised row's data index (or none) and a byte;
562 /// see `Core::selection_ends`.
563 pub fn selection_ends(&self) -> Option<(crate::select::RangeEnd, crate::select::RangeEnd)> {
564 self.core.selection_ends()
565 }
566
567 /// The window's selection when it lives in a `cells` grid — its ends
568 /// as absolute lines and columns; see `Core::cell_selection`.
569 ///
570 /// Offered where the text selection offers only [`Self::selection_text`]
571 /// because a grid's ends mean something to the app: they are the
572 /// session's own line numbers, not byte offsets into runs the app never
573 /// laid out.
574 pub fn cell_selection(&self) -> Option<crate::select::CellSelection> {
575 self.core.cell_selection()
576 }
577
578 /// Opens a context menu; see `Core::open_menu`.
579 pub fn open_menu(&mut self, menu: crate::menu::Menu) {
580 self.core.open_menu(menu);
581 }
582
583 /// Closes whatever menu is open; see `Core::close_menu`.
584 pub fn close_menu(&mut self) -> bool {
585 self.core.close_menu()
586 }
587
588 /// Asks for the selection as text; see `Core::request_copy`.
589 pub fn request_copy(&mut self) -> crate::select::CopyRequest {
590 self.core.request_copy()
591 }
592
593 /// Answers a `selectionrange` ask; see `Core::answer_selection_range`.
594 pub fn answer_selection_range(&mut self, text: &str) -> bool {
595 self.core.answer_selection_range(text)
596 }
597
598 /// The selection as HTML; see `Core::selection_html`.
599 pub fn selection_html(&self) -> Option<String> {
600 self.core.selection_html()
601 }
602
603 /// Puts text on the system clipboard; see `Core::set_clipboard`.
604 pub fn set_clipboard(&mut self, text: impl Into<String>, html: Option<String>) {
605 self.core.set_clipboard(text, html);
606 }
607
608 /// Puts a secret on the system clipboard marked concealed and
609 /// transient, the way a password manager does; see
610 /// `Core::set_clipboard_secret`.
611 pub fn set_clipboard_secret(&mut self, text: impl Into<String>) {
612 self.core.set_clipboard_secret(text);
613 }
614
615 /// Asks for the clipboard's text, delivered as a `text` event on the
616 /// focused sink or as typing into the focused editor; see
617 /// `Core::request_paste`.
618 pub fn request_paste(&mut self) {
619 self.core.request_paste();
620 }
621
622 /// Whether a paste asked for is still unanswered; see
623 /// `Core::awaiting_paste`.
624 pub fn awaiting_paste(&self) -> bool {
625 self.core.awaiting_paste()
626 }
627
628 /// Asks the host for a file dialog; the answer is a `files` event to
629 /// whoever's view asked. False when one is already outstanding. See
630 /// `Core::request_files`.
631 #[track_caller]
632 pub fn request_files(&mut self, dialog: crate::dialog::FileDialog) -> bool {
633 self.core.request_files(dialog)
634 }
635
636 /// `Core::awaiting_files`.
637 pub fn awaiting_files(&self) -> bool {
638 self.core.awaiting_files()
639 }
640
641 /// Selects everything in the scope `key` declared; see
642 /// `Core::select_all_in`.
643 pub fn select_all_in(&mut self, key: Key) -> bool {
644 self.core.select_all_in(key)
645 }
646
647 /// Drops the window's selection; see `Core::clear_selection`.
648 pub fn clear_selection(&mut self) -> bool {
649 self.core.clear_selection()
650 }
651
652 /// The caret rect for a byte offset in the text node `key` drew; see
653 /// `Core::caret_rect`.
654 pub fn caret_rect(&self, key: Key, byte: usize) -> Option<Rect> {
655 self.core.caret_rect(key, byte)
656 }
657
658 /// `measure_text` for a rich-text paragraph.
659 pub fn measure_rich_text(
660 &mut self,
661 spans: &[Span<'_>],
662 base: &TextStyle,
663 max_w: Option<f32>,
664 ) -> crate::text::TextMetrics {
665 self.core.measure_rich_text(spans, base, max_w)
666 }
667
668 /// Opens a node under the next auto key; its children follow until
669 /// [`Self::close`]. [`Self::with`] is the scoped form, and the one to
670 /// prefer: an `open` without its `close` is a `node-left-open`
671 /// warning.
672 #[inline]
673 pub fn open(&mut self, spec: NodeSpec) -> Key {
674 self.core.open(spec)
675 }
676
677 /// [`Self::open`] under a label key: stable across frames whatever
678 /// the siblings before it, and what `key_of(label)` finds.
679 #[inline]
680 pub fn open_keyed(&mut self, label: &str, spec: NodeSpec) -> Key {
681 self.core.open_keyed(label, spec)
682 }
683
684 /// Opens a node under a key the caller built rather than a label:
685 /// `ui.child_key("gap").index(id)` is a label and an index with no
686 /// string formatted and no clash with the sibling-index keys
687 /// `open_indexed` gives, and a key kept from an earlier `child_key` —
688 /// read for `is_hovered` first — is opened as it is instead of spelled
689 /// twice. Build it from this parent's `child_key`: two nodes under one
690 /// key in a frame are a `duplicate-key` warning, as two labels are.
691 /// It has no label, so `key_of` does not find it.
692 #[inline]
693 pub fn open_key(&mut self, key: Key, spec: NodeSpec) -> Key {
694 self.core.open_key(key, spec)
695 }
696
697 /// Scoped [`Self::open_key`].
698 pub fn with_key(&mut self, key: Key, spec: NodeSpec, f: impl FnOnce(&mut Ui<'_>)) -> Key {
699 self.open_key(key, spec);
700 f(self);
701 self.close();
702 key
703 }
704
705 /// [`Self::leaf`] under a key the caller built; see [`Self::open_key`].
706 #[inline]
707 pub fn leaf_key(&mut self, key: Key, spec: NodeSpec) -> Key {
708 self.open_key(key, spec);
709 self.close();
710 key
711 }
712
713 /// `open` under a key the caller chose — the panel's own nodes, whose
714 /// keys another node anchors to by name before they exist.
715 #[inline]
716 pub(crate) fn open_with_key(&mut self, key: Key, label: &str, spec: NodeSpec) {
717 self.core.open_with_key_named(key, label, spec)
718 }
719
720 /// `open_keyed` by sibling index: the key auto-keying would have given
721 /// the `i`th child. A virtualizing list opens each row with its data
722 /// index, so a row keeps its identity when the built range slides past
723 /// it. See `Core::open_indexed`.
724 #[inline]
725 pub fn open_indexed(&mut self, i: u64, spec: NodeSpec) -> Key {
726 self.core.open_indexed(i, spec)
727 }
728
729 /// Closes the node the last `open*` opened.
730 #[inline]
731 pub fn close(&mut self) {
732 self.core.close();
733 }
734
735 /// Opens a node, runs `f` for its children and closes it. The usual
736 /// way to declare a container:
737 ///
738 /// ```rust
739 /// # use kui_core::{Core, NodeSpec, Size, TextStyle};
740 /// # let mut core = Core::new();
741 /// # let mut ui = core.frame(Size::new(200.0, 100.0), 1.0);
742 /// ui.with(NodeSpec::row().gap(8.0).pad(4.0), |ui| {
743 /// ui.text("Name", TextStyle::new(14.0));
744 /// ui.text("Ada", TextStyle::new(14.0));
745 /// });
746 /// # ui.finish();
747 /// ```
748 pub fn with(&mut self, spec: NodeSpec, f: impl FnOnce(&mut Ui<'_>)) -> Key {
749 let key = self.open(spec);
750 f(self);
751 self.close();
752 key
753 }
754
755 /// Scoped [`Self::open_keyed`].
756 pub fn with_keyed(&mut self, label: &str, spec: NodeSpec, f: impl FnOnce(&mut Ui<'_>)) -> Key {
757 let key = self.open_keyed(label, spec);
758 f(self);
759 self.close();
760 key
761 }
762
763 /// A node with no children: a spacer, a rule, a swatch, a hit area —
764 /// `with(spec, |_| {})` without the empty closure.
765 #[inline]
766 pub fn leaf(&mut self, spec: NodeSpec) -> Key {
767 let key = self.open(spec);
768 self.close();
769 key
770 }
771
772 /// [`Self::leaf`] under a label key.
773 #[inline]
774 pub fn leaf_keyed(&mut self, label: &str, spec: NodeSpec) -> Key {
775 let key = self.open_keyed(label, spec);
776 self.close();
777 key
778 }
779
780 /// [`Self::leaf`] under a data index; see [`Self::open_indexed`].
781 #[inline]
782 pub fn leaf_indexed(&mut self, i: u64, spec: NodeSpec) -> Key {
783 let key = self.open_indexed(i, spec);
784 self.close();
785 key
786 }
787
788 /// Declares how many indexed rows the open node's virtual list has,
789 /// built or not; see [`crate::Core::row_count`].
790 pub fn row_count(&mut self, n: u64) {
791 self.core.row_count(n);
792 }
793
794 /// Scoped `open_indexed`: the `i`th child's auto-key, given to a node
795 /// that is not in the `i`th slot.
796 pub fn with_indexed(&mut self, i: u64, spec: NodeSpec, f: impl FnOnce(&mut Ui<'_>)) -> Key {
797 let key = self.open_indexed(i, spec);
798 f(self);
799 self.close();
800 key
801 }
802
803 /// A paragraph of plain text in one style, wrapped at the width its
804 /// parent gives it. A text has no box of its own (no padding,
805 /// background, key or click); [`Self::text_in`] puts it in one.
806 ///
807 /// ```rust
808 /// # use kui_core::{Color, Core, NodeSpec, Size, TextStyle};
809 /// # let mut core = Core::new();
810 /// # let mut ui = core.frame(Size::new(200.0, 100.0), 1.0);
811 /// ui.text("Plain, in the theme's foreground", TextStyle::new(14.0));
812 /// ui.text("Bold-ish, mono, red", TextStyle::new(13.0).mono().color(Color::hex(0xd43b3bff)));
813 /// let badge = ui.text_in(NodeSpec::row().pad_xy(6.0, 2.0).radius(4.0), "3", TextStyle::new(12.0));
814 /// # let _ = badge;
815 /// # ui.finish();
816 /// ```
817 pub fn text(&mut self, content: &str, style: TextStyle) {
818 self.core.text_node(content, style);
819 }
820
821 /// A box of `spec` holding one text: `with(spec, |ui| ui.text(…))` for
822 /// the label, the cell, the badge that needs a width, a background, a
823 /// click or a role. Returns the box's key.
824 #[inline]
825 pub fn text_in(&mut self, spec: NodeSpec, content: &str, style: TextStyle) -> Key {
826 let key = self.open(spec);
827 self.text(content, style);
828 self.close();
829 key
830 }
831
832 /// [`Self::text_in`] under a label key.
833 #[inline]
834 pub fn text_in_keyed(
835 &mut self,
836 label: &str,
837 spec: NodeSpec,
838 content: &str,
839 style: TextStyle,
840 ) -> Key {
841 let key = self.open_keyed(label, spec);
842 self.text(content, style);
843 self.close();
844 key
845 }
846
847 /// [`Self::text_in`] under a data index; see [`Self::open_indexed`].
848 #[inline]
849 pub fn text_in_indexed(
850 &mut self,
851 i: u64,
852 spec: NodeSpec,
853 content: &str,
854 style: TextStyle,
855 ) -> Key {
856 let key = self.open_indexed(i, spec);
857 self.text(content, style);
858 self.close();
859 key
860 }
861
862 /// A cell grid (a terminal's screen) as one node; see
863 /// [`crate::cells`]. The spec is the node's own.
864 pub fn cells(&mut self, grid: &crate::cells::CellGrid<'_>, spec: NodeSpec) {
865 self.core.cells(grid, spec);
866 }
867
868 /// [`Self::cells`] under a data index; see [`Self::open_indexed`].
869 pub fn cells_indexed(&mut self, i: u64, grid: &crate::cells::CellGrid<'_>, spec: NodeSpec) {
870 self.core.cells_indexed(i, grid, spec);
871 }
872
873 /// [`Self::cells`] under a declared key.
874 pub fn cells_keyed(&mut self, label: &str, grid: &crate::cells::CellGrid<'_>, spec: NodeSpec) {
875 self.core.cells_keyed(label, grid, spec);
876 }
877
878 /// A paragraph of styled spans, shaped and wrapped as one flow.
879 pub fn rich_text(&mut self, spans: &[Span<'_>], base: TextStyle) {
880 self.core.rich_text_node(spans, base);
881 }
882
883 /// A registered image; see `Core::image_node` for sizing semantics.
884 pub fn image(&mut self, id: crate::resources::ImageId, spec: NodeSpec) {
885 self.core.image_node(id, spec);
886 }
887
888 /// [`Self::image`] with its `sampling` and `fit` rows; see
889 /// `Core::image_node_with`.
890 pub fn image_with(
891 &mut self,
892 id: crate::resources::ImageId,
893 opts: crate::resources::ImageOpts,
894 spec: NodeSpec,
895 ) {
896 self.core.image_node_with(id, opts, spec);
897 }
898
899 /// A box a registered WGSL function paints; see `Core::fragment_node`
900 /// for what it is, and `Core::add_fragment` for where the handle comes
901 /// from. It has no intrinsic size, so give it one.
902 ///
903 /// `frag` is the handle, or `handle.with_image(img)` for a function
904 /// that reads a registered image through `kui_sample(uv)`: a
905 /// waveform, a heatmap, an image effect.
906 pub fn fragment(
907 &mut self,
908 frag: impl Into<crate::fragment::FragmentRef>,
909 params: &[f32],
910 spec: NodeSpec,
911 ) -> Key {
912 self.core.fragment_node(frag, params, spec)
913 }
914
915 /// A fragment holding children, which paint over it: a gradient card
916 /// with a title and buttons on top of it.
917 pub fn fragment_with(
918 &mut self,
919 frag: impl Into<crate::fragment::FragmentRef>,
920 params: &[f32],
921 spec: NodeSpec,
922 f: impl FnOnce(&mut Ui<'_>),
923 ) -> Key {
924 let key = self.core.open_fragment(frag, params, spec);
925 f(self);
926 self.core.close();
927 key
928 }
929
930 /// [`Self::fragment`] under a label key, for one that transitions or
931 /// exits and needs a stable identity across frames.
932 pub fn fragment_keyed(
933 &mut self,
934 label: &str,
935 frag: impl Into<crate::fragment::FragmentRef>,
936 params: &[f32],
937 spec: NodeSpec,
938 ) -> Key {
939 self.core.fragment_node_keyed(label, frag, params, spec)
940 }
941
942 /// [`Self::fragment_with`] under a label key.
943 pub fn fragment_with_keyed(
944 &mut self,
945 label: &str,
946 frag: impl Into<crate::fragment::FragmentRef>,
947 params: &[f32],
948 spec: NodeSpec,
949 f: impl FnOnce(&mut Ui<'_>),
950 ) -> Key {
951 let key = self.core.open_fragment_keyed(label, frag, params, spec);
952 f(self);
953 self.core.close();
954 key
955 }
956
957 /// [`Self::fragment`] under a data index; see [`Self::open_indexed`].
958 pub fn fragment_indexed(
959 &mut self,
960 i: u64,
961 frag: impl Into<crate::fragment::FragmentRef>,
962 params: &[f32],
963 spec: NodeSpec,
964 ) -> Key {
965 let key = self.core.open_fragment_indexed(i, frag, params, spec);
966 self.core.close();
967 key
968 }
969
970 /// [`Self::fragment_with`] under a data index.
971 pub fn fragment_with_indexed(
972 &mut self,
973 i: u64,
974 frag: impl Into<crate::fragment::FragmentRef>,
975 params: &[f32],
976 spec: NodeSpec,
977 f: impl FnOnce(&mut Ui<'_>),
978 ) -> Key {
979 let key = self.core.open_fragment_indexed(i, frag, params, spec);
980 f(self);
981 self.core.close();
982 key
983 }
984
985 /// A round-capped segment from `from` to `to`, in the parent's box
986 /// space; see `Core::line_node` for what it is and is not.
987 #[inline]
988 pub fn line(&mut self, from: Vec2, to: Vec2, stroke: Stroke, spec: NodeSpec) {
989 self.core.line_node(&[from, to], stroke, spec);
990 }
991
992 /// [`Self::line`] under a label key.
993 pub fn line_keyed(
994 &mut self,
995 label: &str,
996 from: Vec2,
997 to: Vec2,
998 stroke: Stroke,
999 spec: NodeSpec,
1000 ) {
1001 self.core.line_node_keyed(label, &[from, to], stroke, spec);
1002 }
1003
1004 /// [`Self::line`] under a data index; see [`Self::open_indexed`].
1005 pub fn line_indexed(&mut self, i: u64, from: Vec2, to: Vec2, stroke: Stroke, spec: NodeSpec) {
1006 self.core.line_node_indexed(i, &[from, to], stroke, spec);
1007 }
1008
1009 /// A stroke through `points`: a polyline, or a smooth curve through
1010 /// them with [`Stroke::curve`]; see `Core::line_node`.
1011 #[inline]
1012 pub fn polyline(&mut self, points: &[Vec2], stroke: Stroke, spec: NodeSpec) {
1013 self.core.line_node(points, stroke, spec);
1014 }
1015
1016 /// [`Self::polyline`] under a data index; see [`Self::open_indexed`].
1017 pub fn polyline_indexed(&mut self, i: u64, points: &[Vec2], stroke: Stroke, spec: NodeSpec) {
1018 self.core.line_node_indexed(i, points, stroke, spec);
1019 }
1020
1021 /// [`Self::polyline`] under a label key.
1022 pub fn polyline_keyed(&mut self, label: &str, points: &[Vec2], stroke: Stroke, spec: NodeSpec) {
1023 self.core.line_node_keyed(label, points, stroke, spec);
1024 }
1025
1026 /// A filled polygon through `points` in the parent's box space, the
1027 /// fill in `spec`'s `bg`; see `Core::polygon_node` for what it is and
1028 /// is not.
1029 pub fn polygon(&mut self, points: &[Vec2], spec: NodeSpec) {
1030 self.core.polygon_node(points, spec);
1031 }
1032
1033 /// [`Self::polygon`] under a label key.
1034 pub fn polygon_keyed(&mut self, label: &str, points: &[Vec2], spec: NodeSpec) {
1035 self.core.polygon_node_keyed(label, points, spec);
1036 }
1037
1038 /// [`Self::polygon`] under a data index; see [`Self::open_indexed`].
1039 pub fn polygon_indexed(&mut self, i: u64, points: &[Vec2], spec: NodeSpec) {
1040 self.core.polygon_node_indexed(i, points, spec);
1041 }
1042
1043 /// A path — any outline — filled with `spec`'s `bg` and stroked by
1044 /// the path's own stroke; see `Core::path_node` for what it is and
1045 /// is not.
1046 pub fn path(&mut self, path: &crate::path::Path, spec: NodeSpec) {
1047 self.core.path_node(path, spec);
1048 }
1049
1050 /// [`Self::path`] under a label key.
1051 pub fn path_keyed(&mut self, label: &str, path: &crate::path::Path, spec: NodeSpec) {
1052 self.core.path_node_keyed(label, path, spec);
1053 }
1054
1055 /// [`Self::path`] under a data index; see [`Self::open_indexed`].
1056 pub fn path_indexed(&mut self, i: u64, path: &crate::path::Path, spec: NodeSpec) {
1057 self.core.path_node_indexed(i, path, spec);
1058 }
1059
1060 /// [`Self::path`] from SVG path data; data that does not parse raises
1061 /// `path-malformed` and draws nothing. See `Core::path_d_node`.
1062 pub fn path_d(
1063 &mut self,
1064 d: &str,
1065 rule: crate::path::FillRule,
1066 stroke: Option<Stroke>,
1067 turn: Option<crate::path::Turn>,
1068 spec: NodeSpec,
1069 ) {
1070 self.core.path_d_node(d, rule, stroke, turn, spec);
1071 }
1072
1073 /// [`Self::path_d`] under a label key.
1074 pub fn path_d_keyed(
1075 &mut self,
1076 label: &str,
1077 d: &str,
1078 rule: crate::path::FillRule,
1079 stroke: Option<Stroke>,
1080 turn: Option<crate::path::Turn>,
1081 spec: NodeSpec,
1082 ) {
1083 self.core
1084 .path_d_node_keyed(label, d, rule, stroke, turn, spec);
1085 }
1086
1087 /// An `audio` node: a playback retained for as long as the view keeps
1088 /// declaring it; see `Core::audio_node`.
1089 pub fn audio(&mut self, spec: crate::audio::AudioSpec) -> Key {
1090 self.core.audio_node(spec)
1091 }
1092
1093 /// `audio` with a label-derived key; see `Core::audio_node_keyed`.
1094 pub fn audio_keyed(&mut self, label: &str, spec: crate::audio::AudioSpec) -> Key {
1095 self.core.audio_node_keyed(label, spec)
1096 }
1097
1098 /// Starts a playback from a view; see `Core::play`. Views run every
1099 /// frame, so gate it on state that changes once (or use `audio`).
1100 pub fn play(
1101 &mut self,
1102 sound: crate::resources::SoundId,
1103 opts: crate::audio::PlayOptions,
1104 ) -> crate::audio::PlaybackId {
1105 self.core.play(sound, opts)
1106 }
1107
1108 /// An editable text node; state retained by key. See `Core::text_edit`.
1109 pub fn text_edit(
1110 &mut self,
1111 label: &str,
1112 initial: &str,
1113 opts: &EditOptions,
1114 spec: NodeSpec,
1115 ) -> Key {
1116 self.core.text_edit(label, initial, opts, spec)
1117 }
1118
1119 /// Whether `key` holds keyboard focus — any node: an editor, a key
1120 /// sink, a button Tab landed on (see `Core::focus`).
1121 pub fn is_focused(&self, key: Key) -> bool {
1122 self.core.is_focused(key)
1123 }
1124
1125 /// The node holding keyboard focus, if any.
1126 pub fn focused(&self) -> Option<Key> {
1127 self.core.focus()
1128 }
1129
1130 /// Whether focus got where it is by keyboard or assistive technology
1131 /// (a Tab press, a reader's request) rather than a click — when a
1132 /// view that styles its own focus should show it.
1133 pub fn focus_visible(&self) -> bool {
1134 self.core.focus_visible()
1135 }
1136
1137 /// The key of the node opened under the key label `label` — in this
1138 /// frame so far, then in the last finished one. The key label is the
1139 /// name the view opened the node under (`with_keyed`, a `key` prop),
1140 /// not the accessible name its `label` row gives a reader, which
1141 /// `Core::key_named` looks up. For a caller that holds only the label
1142 /// and cannot spell the path (`child_key` is the same question asked
1143 /// from the parent); see `Core::key_of`.
1144 pub fn key_of(&mut self, label: &str) -> Option<Key> {
1145 self.core.key_of(label)
1146 }
1147
1148 /// Moves keyboard focus to `key` now (an editor, an `on_key` sink, a
1149 /// control, a `focusable` node); see `Core::set_focus`.
1150 pub fn focus(&mut self, key: Key) {
1151 self.core.set_focus(Some(key));
1152 }
1153
1154 /// Drops keyboard focus.
1155 pub fn blur(&mut self) {
1156 self.core.set_focus(None);
1157 }
1158
1159 /// Moves focus to the next focusable node in tree order, wrapping —
1160 /// what Tab does. A key sink that binds Tab itself calls this to hand
1161 /// the keyboard on.
1162 ///
1163 /// Deferred, unlike the rest of this handle: a `Ui` only exists while a
1164 /// frame is being built, and `begin_frame` cleared the tree the Tab
1165 /// ring is made of, so stepping now would walk an empty ring. The step
1166 /// is applied at `finish`, against the frame this call is part of — so
1167 /// a view that declares three rows and asks to step lands on one of
1168 /// them, without waiting a frame for them to exist. Outside a frame
1169 /// (a driver handling a key press) `Core::focus_next` steps at once.
1170 #[track_caller]
1171 pub fn focus_next(&mut self) {
1172 self.core.request_focus_step(true);
1173 }
1174
1175 /// Shift-Tab: the previous focusable node. Deferred to `finish` for the
1176 /// reason [`Ui::focus_next`] gives.
1177 #[track_caller]
1178 pub fn focus_prev(&mut self) {
1179 self.core.request_focus_step(false);
1180 }
1181
1182 /// Enters a focus region (the node `key` names, declared
1183 /// `focus_region`), or the main ring for `None`: focus lands on what
1184 /// that ring
1185 /// last held if the node is still there, else its `initial_focus`,
1186 /// else its first stop, and shows. Deferred to `finish` like
1187 /// [`Ui::focus_next`], so a view may name the region it is declaring
1188 /// right now — the dock this frame toggles on. A key the frame does
1189 /// not declare as a region raises `focus-region-without-node`.
1190 #[track_caller]
1191 pub fn focus_region(&mut self, key: Option<Key>) {
1192 self.core.focus_region(key);
1193 }
1194
1195 /// The focus region in effect — the node whose ring Tab walks — or
1196 /// `None` for the main ring. What a chord that toggles between a dock
1197 /// and the app reads to know which way it is going.
1198 pub fn region(&self) -> Option<Key> {
1199 self.core.region()
1200 }
1201
1202 /// An editor's current text, by its key; `None` for a key no editor
1203 /// holds.
1204 pub fn edit_text(&self, key: Key) -> Option<String> {
1205 self.core.edit_text(key)
1206 }
1207
1208 /// Replaces an editor's text, caret at the end (`Core::set_edit_text`).
1209 /// Returns whether it reached an editor now, or was held for the
1210 /// frame that declares the key.
1211 pub fn set_edit_text(&mut self, key: Key, text: &str) -> bool {
1212 self.core.set_edit_text(key, text)
1213 }
1214
1215 /// The same by the label the view declares, for a caller with no key
1216 /// yet: an `update` opening a field the editor has not fired an
1217 /// event from (`Core::set_edit_text_by_label`).
1218 pub fn set_edit_text_by_label(&mut self, label: &str, text: &str) -> bool {
1219 self.core.set_edit_text_by_label(label, text)
1220 }
1221
1222 /// Says something once, with no node behind it (`Core::announce`).
1223 /// Takes effect at once, unlike `focus_next`: the queue is not made of
1224 /// a finished tree.
1225 ///
1226 /// A view runs every frame, so a call made from here needs a guard the
1227 /// app clears; the core reports the unguarded case as
1228 /// `announcement-repeated`. A region whose message is on screen is the
1229 /// `live` prop instead.
1230 pub fn announce(&mut self, text: &str, live: crate::access::Live) {
1231 self.core.announce(text, live);
1232 }
1233
1234 /// Scrolls whatever contains `key` so the node shows — "scroll to the
1235 /// selected row", without the container geometry the app cannot see.
1236 /// Resolved when this frame finishes laying out, so a row the view is
1237 /// declaring right now reveals fine; a key the frame does not declare,
1238 /// or one nothing scrollable contains, is a no-op. See `Core::reveal`.
1239 #[track_caller]
1240 pub fn reveal(&mut self, key: Key) {
1241 self.core.reveal(key);
1242 }
1243
1244 /// The handle for an installed or loaded font family by name (what
1245 /// `family = "Name"` resolves to in the declarative bindings), so a
1246 /// Rust view names a face without reaching for the core. `None` when
1247 /// no face matches. Idempotent.
1248 pub fn system_font(&mut self, name: &str) -> Option<crate::resources::FontId> {
1249 self.core.add_system_font(name)
1250 }
1251
1252 /// [`Self::reveal`] by label, resolved when this frame finishes; see
1253 /// `Core::reveal_label`.
1254 #[track_caller]
1255 pub fn reveal_label(&mut self, label: &str) {
1256 self.core.reveal_label(label);
1257 }
1258
1259 /// `set_scroll` by label, resolved before this frame lays out; see
1260 /// `Core::set_scroll_label`.
1261 #[track_caller]
1262 pub fn set_scroll_label(&mut self, label: &str, offset: Vec2) {
1263 self.core.set_scroll_label(label, offset);
1264 }
1265
1266 /// A scroll container's retained offset, clamped as of the last
1267 /// layout — the number to stash in a model and hand back to
1268 /// `set_scroll` later. Zero for a node that never scrolled.
1269 pub fn scroll_offset(&self, key: Key) -> Vec2 {
1270 self.core.scroll_offset(key)
1271 }
1272
1273 /// Sets that offset, the way the wheel would: `Vec2::ZERO` jumps to
1274 /// the top, a large value to the end (the next layout clamps it).
1275 #[track_caller]
1276 pub fn set_scroll(&mut self, key: Key, offset: Vec2) {
1277 self.core.set_scroll(key, offset);
1278 }
1279
1280 /// Moves a container's scroll state by the content that moved under
1281 /// it (`drawn` for the drawn place and an eased leg's start, `target`
1282 /// for the offset) with no ease asked or ended: a variable-height
1283 /// list's height correction. See `Core::shift_scroll`.
1284 pub fn shift_scroll(&mut self, key: Key, drawn: Vec2, target: Vec2) {
1285 self.core.shift_scroll(key, drawn, target);
1286 }
1287
1288 /// What the last layout resolved for a scroll container — its box, its
1289 /// content size and the clamped offset — so a view can build only the
1290 /// rows that fit and two spacers instead of ten thousand rows. `None`
1291 /// until a layout has resolved `key` as a container. It describes the
1292 /// previous frame; see `Core::scroll_geometry`, or
1293 /// `widgets::uniform_list` for the uniform-row case.
1294 pub fn scroll_geometry(&self, key: Key) -> Option<crate::scroll::ScrollGeometry> {
1295 self.core.scroll_geometry(key)
1296 }
1297
1298 /// The rect the last frame laid an `on_layout` node out at; see
1299 /// `Core::layout_of`.
1300 pub fn layout_of(&self, key: Key) -> Option<Rect> {
1301 self.core.layout_of(key)
1302 }
1303
1304 /// Declares this node focused: it takes keyboard focus when the
1305 /// declaration *starts* (the first frame it is made), and a Tab press
1306 /// afterwards is not clobbered by the view repeating it. An `on_key`
1307 /// sink then gets presses in `on_event` as `{kind="key", code, ctrl,
1308 /// alt, shift, super, text, repeat, tag}`. To move focus at any time,
1309 /// `focus(key)`.
1310 pub fn take_key_focus(&mut self, key: Key) {
1311 self.core.set_key_focus(Some(key));
1312 }
1313
1314 /// The node holding keyboard focus (the same as `focused`).
1315 pub fn key_focus(&self) -> Option<Key> {
1316 self.core.key_focus()
1317 }
1318
1319 /// Asks the frame driver to apply a window command (close from a
1320 /// keymap, minimize from a command line).
1321 pub fn window_command(&mut self, cmd: WindowCommand) {
1322 self.core.push_window_command(cmd);
1323 }
1324
1325 /// Asks the driver to resize a window (logical px) — a request applied
1326 /// on the driver's next pump and ignored headlessly, not a declaration:
1327 /// the user owns a window's size once it exists. `ui.env().window.id`
1328 /// is the window this view is drawing. See `Core::set_window_size`.
1329 pub fn set_window_size(&mut self, window: WindowId, size: Size) {
1330 self.core.set_window_size(window, size);
1331 }
1332
1333 /// Asks the driver to give a window keyboard focus; queued the same
1334 /// way. See `Core::focus_window`.
1335 pub fn focus_window(&mut self, window: WindowId) {
1336 self.core.focus_window(window);
1337 }
1338
1339 /// Runs layout and emission; results land in `Core::output()`. A frame
1340 /// begun with a filler lets it finish first: the `"root"` fill, unless
1341 /// the view declared it, and the `unknown-slot` check. An open context
1342 /// menu is drawn after both, which is what makes it the frame's modal
1343 /// scope and its topmost float.
1344 pub fn finish(self) {
1345 let Ui { core, filler } = self;
1346 // Not in the devtools' own window: nothing the host or an
1347 // extension declares there is built.
1348 if let Some(filler) = filler {
1349 if !core.devtools_window() {
1350 // A declared tab's extension fill first — a layer
1351 // anchored to a body the panel builds below, so the
1352 // filler's own `unknown-slot` check sees the slot
1353 // declared — then the root fills.
1354 core.devtools_fill_mount(filler);
1355 filler.finish(&mut Ui::new(core));
1356 }
1357 // The panel, after the fills and before the menu: the menu is
1358 // the last thing declared and so the topmost float.
1359 core.devtools_finish();
1360 if core.devtools_window() {
1361 // The panel's own window: the extension form's fill lands
1362 // here too, when the pane gave the frame a filler.
1363 core.devtools_fill_mount(filler);
1364 }
1365 } else {
1366 core.devtools_finish();
1367 }
1368 Core::build_menu(&mut Ui::new(core));
1369 core.finish_frame();
1370 }
1371}