Skip to main content

kui_core/
runtime.rs

1//! [`Core`]: one window's runtime, the state machine a runner drives frame
2//! by frame.
3//!
4//! A core owns everything that survives across frames and belongs to one
5//! window: the per-frame tree and its builder state, interaction state
6//! (hover, press, drag), focus, the scroll and edit stores, animations,
7//! the glyph atlas and the finished [`DisplayList`]. What a window must
8//! not duplicate — the font database, the resource registry, the audio
9//! store — lives in the [`Session`] a core is constructed against;
10//! [`Core::new`] makes a private one, so a single-window app never sees
11//! it. The frame lifecycle, with an example, is on [`Core`].
12//!
13//! Builder state lives in the core itself rather than in a borrowing
14//! wrapper so that flat C bindings can drive a frame through one opaque
15//! pointer; the Rust [`Ui`] is a thin safe facade over it.
16
17use std::rc::Rc;
18
19use rustc_hash::{FxHashMap, FxHashSet};
20
21use crate::anim::{AnimStore, Slot, Track};
22use crate::atlas::GlyphAtlas;
23use crate::color::Color;
24use crate::depart::{At, DepartStore, Ghost, GhostContent, Place, Playback, Replay};
25use crate::diag::{Diagnostics, Warning};
26use crate::display::{Clip, ClipId, DisplayList, NO_CLIP_ID, Quad, QuadKind};
27use crate::edit::{EditOptions, EditStore};
28use crate::env::{Env, SystemEnv};
29use crate::geom::{Rect, Size, Vec2};
30use crate::input::{
31    EditKey, HitRegion, InputEvent, Interaction, KeyCode, KeyMods, KeyPhase, KeyPress, MouseButton,
32    ScrollAxis, ScrollRegion, ScrollbarRegion, UiEvent,
33};
34use crate::key::{Key, LabelIndex};
35use crate::keyframes;
36use crate::layout::{self, TextMeasure};
37use crate::line::Stroke;
38use crate::resources::{FontId, Resources};
39use crate::scroll::ScrollStore;
40use crate::session::{
41    MAIN_WINDOW_NAME, Session, SharedAudio, SharedResources, WindowChange, WindowDecl,
42};
43use crate::spec::{NodeSpec, Sizing, TextStyle};
44use crate::stats::FrameStats;
45use crate::text::TextHit;
46use crate::text::{Span, TextMetrics, TextSystem};
47use crate::theme::{Theme, ThemeSource};
48use crate::tree::{NIL, NodeContent, OriginId, Tree};
49use crate::ui::Ui;
50use crate::value::Value;
51use crate::window::{WindowConfig, WindowId};
52
53// `impl Core` continues in these, one concern per file (each opens with
54// what it holds). Children of this module, so the fields stay private.
55mod builder;
56pub use builder::Content;
57pub use replay::SlotFill;
58pub mod cause;
59mod composites;
60pub mod devtools;
61mod dispatch;
62mod emit;
63mod fills;
64mod focus;
65pub mod follow;
66mod gesture;
67pub mod inspect;
68mod menu_api;
69/// A slot replayed by its host (ADR 0045).
70pub(crate) mod replay;
71pub(crate) use menu_api::MenuSurface;
72mod menubar_api;
73mod resources_api;
74mod scrolling;
75mod select_api;
76mod windows;
77
78/// Wheel line-delta to logical px.
79const SCROLL_LINE_PX: f32 = 40.0;
80
81/// What a `Core::focus_region` call asked to enter, held until the frame
82/// finishes: the main ring,
83/// a region by key, or one by the label its node declares — the spelling
84/// a caller has for a node the last frame did not build.
85#[derive(Clone, Debug, PartialEq)]
86pub(crate) enum RegionTarget {
87    Main,
88    Key(Key),
89    Label(String),
90}
91
92/// One window's runtime: the state a runner drives frame by frame.
93///
94/// Construct one with [`Core::new`] (a private [`Session`]) or
95/// [`Core::new_in`] (joining a session another window shares). The public
96/// fields are the stores a runner or a binding reads directly:
97/// `resources` and `audio` for registration and playback commands, `env`
98/// for the host facts a driver pushes, `stats` for frame timing.
99///
100/// # One frame, in order
101///
102/// 1. [`Core::set_time`] with the frame clock, so transitions advance.
103/// 2. [`Core::frame`] with the viewport (logical px) and the scale. It
104///    begins the frame and returns the [`Ui`] builder; build the tree
105///    through it.
106/// 3. [`Ui::finish`] runs layout and emission. The draw data is now in
107///    [`Core::output`]: the [`DisplayList`] and the [`GlyphAtlas`] a
108///    renderer mirrors to a texture.
109/// 4. [`Core::take_pending_events`] drains events the frame itself raised
110///    (a `resize`, a hover change under a still pointer); route them like
111///    any other.
112/// 5. Between frames, feed input through [`Core::handle_input`]. Each
113///    call returns the [`UiEvent`]s it resolved to, hit-tested against
114///    the frame that finished.
115/// 6. [`Core::take_warnings`] for misconfigurations the core noticed, and
116///    [`Core::animating`] to decide whether to draw another frame without
117///    waiting for input.
118///
119/// A headless core needs no window or GPU, so a test can drive it:
120///
121/// ```rust
122/// use kui_core::{Color, Core, InputEvent, NodeSpec, QuadKind, Size, TextStyle, Vec2};
123///
124/// let mut core = Core::new();
125///
126/// // 1–3: a frame. `frame` begins it, `finish` lays it out and emits.
127/// core.set_time(0.0);
128/// let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
129/// ui.with_keyed("toolbar", NodeSpec::row().pad(8.0).gap(8.0), |ui| {
130///     // A clickable node needs an accessible name, or `take_warnings`
131///     // reports `control-without-name`.
132///     let button = NodeSpec::row().size(80.0, 32.0).bg(Color::hex(0x3366cc));
133///     ui.leaf_keyed("save", button.on_click("save").label("Save"));
134///     ui.text("Untitled", TextStyle::new(14.0));
135/// });
136/// ui.finish();
137///
138/// // 4: what the frame itself raised (nothing on a first frame).
139/// assert!(core.take_pending_events().is_empty());
140///
141/// // The renderer's view of the frame: quads in physical pixels.
142/// let (list, atlas) = core.output();
143/// assert!(list.quads.iter().any(|q| q.kind == QuadKind::Solid));
144/// atlas.dirty = false; // after uploading `atlas.pixels`
145///
146/// // 5: input, hit-tested against the frame that finished.
147/// core.handle_input(InputEvent::CursorMoved(Vec2::new(20.0, 20.0)));
148/// core.handle_input(InputEvent::mouse_down(1));
149/// let events = core.handle_input(InputEvent::mouse_up());
150/// assert_eq!(events.len(), 1);
151/// assert_eq!(events[0].payload.as_str(), Some("save"));
152///
153/// // 6: diagnostics, and whether another frame is owed.
154/// assert!(core.take_warnings().is_empty());
155/// assert!(!core.animating());
156/// ```
157///
158/// A windowed runner does the same with real time, real input and a
159/// renderer consuming [`Core::output`]; `kui-native` is that runner.
160pub struct Core {
161    /// The caches and registries this window shares with the rest of its
162    /// session. Everything a frame needs from it is borrowed inside a
163    /// method and dropped before it returns; see `session`'s module doc.
164    session: Session,
165    /// This window's shaped-text cache and rasterizer. Not the session's:
166    /// its cache entries are stamped with `atlas`'s epoch, and `TextId`
167    /// indexes its per-frame list.
168    pub text: TextSystem,
169    /// The frame's cell grids and their glyph tables.
170    pub cells: crate::cells::CellStore,
171    /// This window's glyph atlas — the CPU side of its renderer's texture,
172    /// handed out by `output`.
173    pub atlas: GlyphAtlas,
174    /// The session's resource registry. A font, image or sound registered
175    /// through it is registered for every window in the session.
176    pub resources: SharedResources,
177    /// The session's audio store: one device for the process, so playback
178    /// bookkeeping and the command queue are shared, not per window.
179    pub audio: SharedAudio,
180    /// The family names of the session's fonts as of this window's last
181    /// frame, so `font_family` can lend one out. Refreshed from
182    /// `SessionState::fonts_rev`.
183    font_names: FxHashMap<FontId, std::rc::Rc<str>>,
184    fonts_rev: u64,
185    /// The session's `weights_rev` this core's shaped text is of
186    /// (`sync_weights`).
187    weights_rev: u64,
188    /// The session's `images_rev` this core last checked its atlas
189    /// against (`sync_dropped`).
190    images_rev: u64,
191    pub interaction: Interaction,
192    pub scroll: ScrollStore,
193    pub edit: EditStore,
194    /// Transition tweens, keyed by node; see `anim`. Fed by `set_time`.
195    pub anim: AnimStore,
196    /// Subtrees the view stopped declaring, played out and then dropped;
197    /// see `depart`. Empty unless something declares `exit`.
198    pub depart: DepartStore,
199    /// Frame timing pushed by the frame driver; see `widgets::latency_graph`.
200    pub stats: FrameStats,
201    /// Host facts pushed by the frame driver (refresh rate, focus).
202    pub env: Env,
203    /// Where this window's palette comes from; see [`crate::theme`].
204    /// `Derived` unless the app said otherwise, so an app that never
205    /// mentions themes still follows the OS.
206    theme_source: ThemeSource,
207    /// `theme_source` resolved against `env.system`, re-resolved at the
208    /// start of every frame. Read by the stock widgets, by the core's own
209    /// chrome (ring, scrollbar, selection) and by any view that asks.
210    theme: Theme,
211    /// The sizes the stock widgets are built from: the
212    /// palette's other axis, set by the app or
213    /// [`Metrics::default`](crate::metrics::Metrics::default).
214    metrics: crate::metrics::Metrics,
215    /// The titlebar height the driver opened this window's strip at, over
216    /// the platform's caption height ([`Core::set_platform_titlebar_h`]);
217    /// `None` is the platform's.
218    platform_titlebar_h: Option<f32>,
219    /// The named colours and lengths each origin declared: the
220    /// host's under `OriginId::HOST`, an extension's under its own, so a
221    /// guest's declaration never replaces the host's palette. Read through
222    /// [`Core::token_lookup`], which puts the running origin's table over
223    /// the host's.
224    tokens: rustc_hash::FxHashMap<OriginId, crate::tokens::Tokens>,
225    /// Window title declared this frame (immediate-mode: cleared each
226    /// `begin_frame`; the driver diffs and applies). None = leave as-is.
227    window_title: Option<String>,
228    /// Whether this frame asked for the window to stay above every other
229    /// app's. The title's shape — cleared each `begin_frame`,
230    /// the driver diffs and applies on change — but a bool with a default
231    /// rather than an option, so a frame that stops asking is what lowers
232    /// the window again: the pin button an app draws for this is a
233    /// toggle, and the fact follows it.
234    always_on_top: bool,
235    /// Whether this frame asked for secure keyboard entry while its window
236    /// has the keyboard. `always_on_top`'s shape: cleared each
237    /// `begin_frame`, so a frame that stops asking is what turns it off.
238    secure_input: bool,
239    /// Which Option keys this frame asked to act as Alt on macOS.
240    /// `always_on_top`'s shape: cleared each `begin_frame`, so a
241    /// frame that stops declaring it gives the Option keys back to the
242    /// layout's composition.
243    option_as_alt: crate::input::OptionAsAlt,
244    /// Whether this frame asked for the platform's input method off in
245    /// its window. `always_on_top`'s shape: cleared each `begin_frame`,
246    /// so a frame that stops asking gives the window its IME back.
247    ime_off: bool,
248    /// Keyboard focus: the one node key input goes to — an editor (the
249    /// edit store mirrors it), an `on_key` sink, a control Tab landed on.
250    /// `set_focus` is
251    /// the only writer.
252    focus: Option<Key>,
253    /// Focus got there by keyboard or assistive technology, so it shows:
254    /// the default ring, or the node's `focus_bg`. A mouse press clears it.
255    focus_visible: bool,
256    /// Nodes that declared focus this frame and last (`set_key_focus`):
257    /// a declaration takes focus only when it starts, so a repeated one
258    /// does not clobber a Tab press.
259    declared_focus: Vec<Key>,
260    declared_focus_last: Vec<Key>,
261    /// Whether the app moved focus through `set_focus` since the last
262    /// frame began — the edge a closing modal's restore yields to, like a
263    /// `keyFocus` edge. Cleared by `begin_frame`.
264    focus_asked: bool,
265    /// The `.str`-keyed nodes this frame and last, with their labels
266    /// (`open_keyed`): what `key_of` resolves a name through. The same
267    /// swap-and-clear pair as the focus declarations, so a frame that
268    /// keys nothing costs two clears.
269    key_labels: LabelIndex,
270    key_labels_last: LabelIndex,
271    /// The slots this frame declared (`begin_slot`), by name: what the
272    /// `"root"` fill and the `unknown-slot` check read, and what makes a
273    /// second declaration of one name a `duplicate-slot`. Cleared each
274    /// frame; never compared to the last one, since a slot is a position
275    /// and not a declaration the core diffs.
276    slot_labels: LabelIndex,
277    /// A slot replayed by its host (ADR 0045, `runtime::replay`): the
278    /// fill being kept, the ask for the next one to keep, what each slot
279    /// kept between frames, and what `slot_replay` answered this frame.
280    recording: Option<replay::Recording>,
281    keep_next: Option<(String, Key, Value)>,
282    kept: rustc_hash::FxHashMap<String, replay::Kept>,
283    slot_fills: Vec<(String, replay::SlotFill, Option<String>)>,
284    slot_fills_last: Vec<(String, replay::SlotFill, Option<String>)>,
285    /// The key namespace of the fill in progress:
286    /// while the node stack is exactly `ns_depth` deep, a child's key is
287    /// derived from `ns_key` instead of from the node it is opened under,
288    /// so an extension's nodes are keyed by the slot and the extension
289    /// rather than by whatever the host built around them. `usize::MAX`
290    /// when no fill is in progress — one compare on the auto-key path.
291    ns_depth: usize,
292    ns_key: Key,
293    /// Between `begin_frame` and `finish_frame`: `key_labels` is partial
294    /// and `key_labels_last` is the last whole frame, and `key_of` reads
295    /// both; outside a build `key_labels` is the whole last frame and is
296    /// the only one read.
297    building: bool,
298    /// Windows this frame and last declared (`declare_window`): the same
299    /// edge-triggered shape as the focus pair, one level up. The session's
300    /// registry diffs the union across every core at `finish_frame`; the
301    /// pair here is what makes a frame that changed nothing cost nothing.
302    declared_windows: Vec<WindowDecl>,
303    declared_windows_last: Vec<WindowDecl>,
304    /// The presses delivered to the current focus and not yet released,
305    /// in press order. A `KeyUp` is routed only when its press is in here
306    /// (so a sink never sees a release it did not see the press of), and
307    /// focus leaving synthesizes the missing releases from it.
308    keys_held: Vec<KeyPress>,
309    /// The modifiers of the `KeyDown` the next input event is the second
310    /// channel of (`KeyPress::edit_event`), so the `Key` and `Text` arms
311    /// ask the same chord question the raw press did. Set by a `KeyDown`, read
312    /// and cleared by whatever
313    /// comes next: a `Key` or `Text` with no press before it — a test
314    /// driving one channel, a host's editing-key door — has no chord to
315    /// agree with and falls back to what its own `Mods` say.
316    pressed_mods: Option<KeyMods>,
317    pub(crate) tree: Tree,
318    /// Whether the host asked for `finish_frame` to copy the frame into
319    /// `inspected` (see `runtime/inspect.rs`, `set_inspect`); off unless
320    /// it did. The panel's own need is `dt_inspect`, derived each frame
321    /// and kept apart, so neither ask can turn the other
322    /// off.
323    inspect: bool,
324    /// The devtools panel's need for the snapshot this frame: its tree
325    /// tab is showing, or it is picking.
326    dt_inspect: bool,
327    inspected: Vec<inspect::NodeInfo>,
328    /// The devtools' hold on this window's frame: the
329    /// tree index of the app container the host's tree is wrapped in
330    /// while the panel is docked, whether this core draws the panel's own
331    /// window, and — while the panel's theme override is in force — the
332    /// source the host had before it, beside the override applied, so a
333    /// source the host sets *under* the override is told from it.
334    dt_app: Option<usize>,
335    /// The dock the frame was wrapped for at `begin_frame`, `None` when
336    /// it was not: a panel turned on or re-docked mid-frame (an app's
337    /// `set_devtools` from its `view`) is built next frame, which is
338    /// asked for, rather than into a root laid out for something else.
339    dt_dock: Option<devtools::Dock>,
340    dt_window: bool,
341    dt_theme: Option<(ThemeSource, ThemeSource)>,
342    /// The menu mode the host had before the panel's override went on —
343    /// drawn or native, the bar with it — for the override's `platform`
344    /// choice to put back.
345    dt_menus: Option<(bool, bool)>,
346    /// The panel was built at `begin_frame` (a left dock precedes the
347    /// app's container in tree order), on this tab, so `finish` must not
348    /// build again — and a door that moved the panel, turned it off or
349    /// changed its tab since is the next frame's, which `finish` asks
350    /// for.
351    dt_built: Option<devtools::Shown>,
352    /// The devtools tabs declared this frame, in order: what
353    /// the panel's strip lists, moved into the session's state at the
354    /// end of the main window's frame. Empty on a frame nobody declares
355    /// one, which is what every other frame pays.
356    dt_tabs: Vec<devtools::TabDecl>,
357    /// The host's viewport in window coordinates: the whole window, or
358    /// what the dock leaves of it while the panel is docked.
359    /// What `Core::viewport` reports, what a `resize` is measured on,
360    /// what the host's viewport floats resolve against, and the origin
361    /// every coordinate the host is handed or hands in is relative to.
362    dt_area: Rect,
363    /// The previous frame's tree, kept only while a frame declares `exit`
364    /// — `begin_frame` swaps the two buffers instead of clearing one, so
365    /// the frame that notices a node gone still has the node. Empty (and
366    /// untouched) for every frame that declares no exit.
367    prev_tree: Tree,
368    /// The frame's strokes, indexed by the `line` nodes' `LineId`s; the
369    /// previous frame's kept alongside on the same terms as `prev_tree`.
370    pub(crate) lines: crate::line::LineStore,
371    /// The frame's paths, on the same terms as the strokes.
372    pub(crate) paths: crate::path::PathStore,
373    /// What each `path` key last declared and when its ops last changed,
374    /// and whether the key is animating: one whose ops changed twice
375    /// within `path::ANIMATING_WINDOW` frames, whose masks leave the atlas
376    /// until it has held still for `path::SETTLED_AFTER`.
377    pub(crate) path_motion: rustc_hash::FxHashMap<Key, crate::path::Motion>,
378    /// The ops of each `d` string a `path` key last declared, so a string
379    /// handed over every frame is parsed once.
380    pub(crate) path_parsed: rustc_hash::FxHashMap<Key, crate::path::Parsed>,
381    /// The masks drawn from a texture of their own rather than the atlas:
382    /// too big for a page, or animating.
383    pub(crate) path_textures: crate::path::PathTextures,
384    /// Whether [`Core::output`] has been asked for since the frame was
385    /// built: what says its `dropped_textures` and `dropped_fragments`
386    /// reached a backend. A frame nobody read hands them to the next.
387    output_read: bool,
388    pub(crate) fragments: crate::fragment::FragmentList,
389    /// The stock polygon fragment's handle, once a `polygon` node has
390    /// asked for it this session. Forgotten by
391    /// `remove_fragment` if a host removes it, so the next node registers
392    /// it again rather than drawing nothing — and not re-checked per node,
393    /// which was a session lock per polygon and cost more than the six
394    /// segment quads a closed stroke of the same outline emits.
395    pub(crate) stock_polygon: Option<crate::resources::FragmentId>,
396    /// The hit shapes the frame being emitted builds beside its regions,
397    /// handed to `interaction` with them at the end of
398    /// emission; the previous frame's buffers, cleared, in between.
399    pub(crate) hit_shapes: crate::input::HitShapes,
400    pub(crate) display: DisplayList,
401    pub(crate) viewport: Size,
402    pub(crate) scale: f32,
403    // Frame-builder state.
404    stack: Vec<u32>,
405    counters: Vec<u64>,
406    origin: OriginId,
407    /// Per-node inherited clip (logical), rebuilt each finish_frame.
408    /// The access tree cuts each node's rect to it, so what a
409    /// reader finds is what a pointer can hit.
410    clips: Vec<Clip>,
411    /// The `DisplayList::clips` index each of those became, so a node
412    /// whose clip is its parent's names the entry the parent already
413    /// interned instead of asking again. Parallel to `clips`.
414    clip_ids: Vec<ClipId>,
415    /// Per-node inherited group opacity (the product down the ancestors),
416    /// rebuilt each finish_frame and only materialized when something
417    /// actually fades.
418    opacity: Vec<f32>,
419    /// Per node, the layer it paints in: the index of its nearest floating
420    /// ancestor-or-self, `NIL` in flow. Only filled on a frame
421    /// that floats something.
422    float_root: Vec<u32>,
423    /// The float layers as the last frame painted them, bottom to top:
424    /// each root's key and its rank among that frame's float roots in
425    /// tree order. A root the next frame keeps stays where it is, one it
426    /// opens goes on top, one it closes leaves — so the stack is the
427    /// order the layers opened in. Bounded by the
428    /// frame's own float count; nothing to evict.
429    float_stack: Vec<(Key, u32)>,
430    /// The hover hints of the nodes open right now that declared a
431    /// `tooltip` (`Core::hint`), each with the stack depth it was opened
432    /// at, so `close` knows whose turn it is. Only nodes that declared one
433    /// are here: a frame without a tooltip pays one length check per close.
434    hints: Vec<(usize, Key, String)>,
435    /// The context menu this window has open, the keys the stock renderer
436    /// gave its rows (so their clicks can be told from the app's), and
437    /// what choosing one left for the host to do.
438    menu: Option<crate::menu::Menu>,
439    menu_actions: Vec<crate::menu::MenuAction>,
440    /// The editor that held focus when the menu opened, since the menu's
441    /// own rows take focus from it — what Cut, Copy and Select All act on.
442    menu_editor: Option<Key>,
443    /// The node that held focus when the menu opened, whatever it was:
444    /// where a `replay` row's chord goes once the menu is gone (backlog
445    /// F151), since a key goes where the keyboard was.
446    menu_focus: Option<Key>,
447    /// Whether the host draws menus itself (`set_native_menus`).
448    native_menus: bool,
449    /// The submenus open in the drawn context menu and in the drawn bar's
450    /// open menu (backlog F128): retained, like `menu_bar_open`, because
451    /// the frame cannot derive which row the pointer last rested on.
452    menu_sub: menu_api::Submenus,
453    menu_bar_sub: menu_api::Submenus,
454    /// The application menu this frame has in force, and a count bumped
455    /// whenever it changes, so a driver diffs against one integer rather
456    /// than against a tree.
457    /// `None` is a declaration nobody has made; an empty `MenuBar` is one
458    /// that took the bar away.
459    menu_bar: Option<crate::menu::MenuBar>,
460    menu_bar_rev: u64,
461    /// Who declared it, and so who hears the events its items post — the
462    /// host, or the extension whose view declared the bar.
463    menu_bar_origin: OriginId,
464    /// Which of the drawn bar's menus is open, and the bar's root this
465    /// frame — where a menu-bar event lands. Its titles and rows are known
466    /// by their origin (`OriginId::MENU_BAR`), not by key.
467    menu_bar_open: Option<usize>,
468    menu_bar_root: Option<Key>,
469    /// The select fields this frame built, each with its menu's rows
470    /// (`widgets::select`); a click on one opens that menu.
471    selects: Vec<(Key, Vec<crate::menu::MenuItem>)>,
472    /// Whether the platform owns the menu bar (`set_native_menu_bar`), in
473    /// which case the drawn one draws nothing and the driver hands the
474    /// declaration over instead.
475    native_menu_bar: bool,
476    /// Whether the host can show a definition panel
477    /// (`set_lookup_available`).
478    lookup_available: bool,
479    /// The window's text selection outside an editor, and what the
480    /// frame resolved it to: `sel_ords` numbers the text nodes of the
481    /// selection's scope in emission order (`u32::MAX` for a node
482    /// outside it), and `sel_ends` is the pair of ends in reading order,
483    /// `None` when this frame builds neither end.
484    selection: Option<crate::select::Selection>,
485    /// The window's selection when it is in a `cells` grid instead of in
486    /// text. One selection per window: starting either clears the other,
487    /// which `Core::set_selection` / `set_cell_selection` enforce in one
488    /// place each.
489    cell_selection: Option<crate::select::CellSelection>,
490    /// Whether a `selectionrange` ask is outstanding.
491    awaiting_selection: bool,
492    /// Whether a paste ask is outstanding — queued, or taken by the
493    /// driver and not yet answered with a `Commit`. A
494    /// second ask while one is out is dropped, so a view that asks every
495    /// frame until the answer lands asks once.
496    awaiting_paste: bool,
497    /// The one file-dialog ask: queued, taken by the host,
498    /// or none.
499    file_ask: crate::dialog::FileAsk,
500    /// The drag a press is running through a selection scope, if any: set
501    /// on the press inside a scope, cleared on release. The counterpart of
502    /// `EditStore::dragging` for text nobody is editing.
503    select_dragging: Option<crate::select::SelectDrag>,
504    /// The held drag — a caret drag or a drag-select — following its
505    /// scroller: the pointer's last position, the scroller found for it,
506    /// and what that scroller was at when the live end was last placed.
507    /// Set with either drag, cleared with both.
508    drag_follow: Option<follow::DragFollow>,
509    /// The frame clock's reading at the last frame, for the edge drag's
510    /// rate (`follow::follow_drag`); `None` before a clock is set.
511    last_frame_time: Option<f64>,
512    /// The fraction of a line the last delta over an `on_scroll` grid —
513    /// a wheel notch or an edge step — did not cover, with the grid it
514    /// was over: the next delta on the same grid adds to it.
515    line_carry: Option<(Key, f32)>,
516    /// The targets the scroll gesture under way latched, per axis.
517    scroll_latch: gesture::ScrollLatch,
518    sel_ords: Vec<u32>,
519    sel_ends: Option<crate::select::Ends>,
520    /// Per-node innermost enclosing selection scope — the key of the
521    /// nearest ancestor (or the node itself) declaring `selectable`, and
522    /// `None` outside every scope. Filled only on a frame that declares
523    /// one at all (`Tree::any_selectable`), so an app that never selects
524    /// anything pays nothing for it.
525    scopes: Vec<Option<Key>>,
526    /// Per-node enclosing virtualised row index, filled beside `scopes`:
527    /// what places an endpoint whose own node is no longer built.
528    rows: Vec<Option<u64>>,
529    /// The frame's modal scope: the tree range `[i, subtree_end(i))` of the
530    /// last node declaring `modal`, and its key. Everything outside it is
531    /// inert and out of the Tab ring. Recomputed by `finish_frame`.
532    modal: Option<(usize, usize, Key)>,
533    /// The modals declared by the last finished frame, in tree order, each
534    /// with the focus it displaced: a modal that stops being declared gives
535    /// that focus back.
536    modal_focus: Vec<(Key, Option<Key>)>,
537    /// Per-node inherited opacity while a departing subtree is replayed
538    /// (`depart`), reused across ghosts and frames.
539    ghost_opacity: Vec<f32>,
540    /// Per-node inherited clip and painted rect while a departing subtree
541    /// is replayed: the clips its own clippers establish, since a ghost
542    /// draws outside every clip its ancestors held (`depart`).
543    ghost_clip: Vec<Clip>,
544    /// The interned index of each of those, as `clip_ids` is for `clips`.
545    ghost_clip_ids: Vec<ClipId>,
546    ghost_rect: Vec<Rect>,
547    /// A view asked for one more frame (`request_frame`); cleared by
548    /// `begin_frame`, reported through `animating`.
549    frame_requested: bool,
550    /// The earliest frame-clock time a view or a handler asked for a frame
551    /// at (`request_frame_at`, backlog F135): kept until a frame is begun
552    /// at or after it, and read by a driver through `next_frame_at` as a
553    /// deadline to wake for rather than a frame to draw now.
554    frame_due: Option<f64>,
555    /// Exits named for a removal (`set_exit`, backlog F136), by key: read
556    /// by the departures of the frame that finishes next, over the exit
557    /// the kept frame declared, and cleared when it finishes.
558    exits_named: rustc_hash::FxHashMap<Key, crate::enter::Enter>,
559    /// Why frames run: the reasons, and — traced — who held an owed one
560    /// and whether a frame changed anything (`runtime/cause.rs`).
561    trace: cause::Trace,
562    /// The focused editor's caret rect (logical, viewport coords) as of the
563    /// last finish_frame — where drivers should anchor the OS IME window.
564    ime_rect: Option<Rect>,
565    /// A custom editor's caret as of the last frame: the `line` under the
566    /// focused sink that declares `caret`, and the offset it declares.
567    /// What the blink clock is armed on when no stock
568    /// editor is focused; `None` with nothing to blink.
569    sink_caret: Option<(Key, u32)>,
570    /// Whether that line declared its caret `caret_solid` — a block caret
571    /// in a modal editor's normal mode: still the IME's anchor and the
572    /// access tree's caret, but not a caret to blink, so `has_caret`
573    /// leaves it out and an idle app draws no frame for it.
574    sink_caret_solid: bool,
575    /// Bumped whenever `sink_caret` changes between frames — the caret
576    /// moved, or focus came to or left a custom editor — so the driver
577    /// re-arms the blink solid, the way `EditStore::caret_stamp` does for
578    /// the stock editor. The two are summed in `caret_stamp`.
579    sink_caret_stamp: u64,
580    /// Events raised by the frame driver's own reports rather than by input
581    /// — a changed viewport becoming a `resize`. Drained alongside the
582    /// interaction's pending queue.
583    pending: Vec<UiEvent>,
584    /// Whether any frame has begun yet: the first one establishes the
585    /// viewport instead of resizing it.
586    framed: bool,
587    /// The OS settings the last frame was begun with. A driver pushes
588    /// them into `env` whenever it learns of a change, and the difference
589    /// between two frames is what becomes a `system` event — the same
590    /// bookkeeping `viewport` does for `resize`.
591    system_seen: SystemEnv,
592    /// The session's `system_fonts_rev` the last frame was begun with; the
593    /// difference is what becomes a `fonts` event.
594    system_fonts_seen: u64,
595    /// Frames begun so far; stamps the per-key stores below.
596    frame_no: u64,
597    /// The rect last reported for each `on_layout` node and the frame it
598    /// was seen: a different rect, or a node not seen last frame, posts a
599    /// `layout` event (see `emit_layout_events`).
600    layouts: FxHashMap<Key, (Rect, u64)>,
601    /// One-off announcements queued since the last drain (see
602    /// [`Self::announce`]). The fourth of
603    /// the four drained channels, and the same shape as the other three:
604    /// the core appends, a driver drains, a headless test asserts on what
605    /// it drained.
606    announcements: Vec<crate::access::Announcement>,
607    /// The last announcement's text, the frame it was queued on and the
608    /// [`Self::events_answered`] reading then: the same text on two
609    /// consecutive frames with no event handed to the app between them is
610    /// what an unguarded `ui.announce(...)` in a view looks like, and
611    /// `announcement-repeated` says so. The event count is what tells a
612    /// window that redraws only on input apart from one shouting every
613    /// frame — two Copy presses in a row are two consecutive frames there.
614    last_announcement: Option<(String, u64, u64)>,
615    /// How many times `handle_input` handed the app at least one event.
616    events_answered: u64,
617    /// The `reveal(key)`s waiting for a layout to resolve against, in the
618    /// order asked: the next `finish_frame` scrolls each node's scrolling
619    /// ancestor to show it, then clears them. Within one container the
620    /// last ask wins; asks aimed at different containers all land.
621    pending_reveal: Vec<Key>,
622    /// `reveal_label` and `set_scroll_label` asks, with the origin that
623    /// asked: resolved when the frame finishes, where a label named before
624    /// its node is declared — later in the same build, or by the next
625    /// frame — has a node to find.
626    pending_reveal_labels: Vec<(String, crate::tree::OriginId)>,
627    /// The `on_focus` nodes the focus was last reported inside, outermost
628    /// first, with each one's origin and tag — what `report_focus` diffs
629    /// the focus against.
630    focus_reported: Vec<(Key, crate::tree::OriginId, Value)>,
631    pending_scroll_labels: Vec<(String, crate::tree::OriginId, Vec2)>,
632    /// Type-ahead inside a composite: the
633    /// characters typed so far, and the frame clock reading of the last
634    /// keystroke. The buffer is cleared at the start of the first frame,
635    /// or the first keystroke, more than [`TYPE_AHEAD_SECS`] after it —
636    /// the keystroke's against the clock its driver stamped before the
637    /// input (backlog F139), since a parked window draws no frame in
638    /// between. With no
639    /// clock set `type_ahead_at` is None and every keystroke starts a
640    /// fresh search, which is the useful half of type-ahead.
641    type_ahead: String,
642    type_ahead_at: Option<f64>,
643    /// Reused by the composite walks (the ring, arrow motion), so a frame
644    /// with composites in it pays one allocation rather than one each.
645    items_scratch: Vec<usize>,
646    /// A `focus_next` / `focus_prev` waiting for a frame to walk: `true`
647    /// forward. The Tab ring is the finished tree's, and a request made
648    /// while a frame is being built has no tree to walk yet (`begin_frame`
649    /// cleared it), so the step is held until `finish_frame` — after the
650    /// modal scope is resolved, which is what scopes the ring. Last writer
651    /// wins, and an applied step beats a `set_focus` from the same frame.
652    pending_focus_step: Option<bool>,
653    /// The focus region in effect — the key of the node whose subtree Tab
654    /// walks — or `None` for the main ring (the tree minus every region).
655    /// Follows focus: `set_focus` moves it to the region enclosing the
656    /// focused node, a press settles it on the region under the pointer,
657    /// and it is kept across a blur so Tab re-enters where the user was.
658    region: Option<Key>,
659    /// Whether a press settled `region` somewhere other than the focused
660    /// node's own region — dead space in the dock, focus on the root sink
661    /// the press bubbled to — so the region stops following that focus
662    /// until it moves (decision 3's second sentence). Cleared by any
663    /// `set_focus` that changes the focus.
664    region_held: bool,
665    /// The focus each region last held, main (`None`) included: what
666    /// `focus_region` lands on when entering it again, and what main gets
667    /// back when the region in effect stops being declared.
668    region_focus: Vec<(Option<Key>, Option<Key>)>,
669    /// A `focus_region` waiting for the frame to finish, for the reason
670    /// `pending_focus_step` waits: the ring it enters is the finished
671    /// tree's, and the caller may name a node the last frame did not have.
672    /// Last writer wins.
673    pending_region: Option<RegionTarget>,
674    /// Silent-misconfiguration detection; see `diag`.
675    diag: Diagnostics,
676    /// The access tree of the last finished frame, built on demand (see
677    /// `access_tree`) and stamped with the frame it was built from.
678    access: crate::access::AccessTree,
679    access_built: u64,
680    /// The hash of the inputs `self.access` was derived from, so a frame
681    /// whose access-relevant state is unchanged keeps it. `None` when the last
682    /// frame could not be hashed, which
683    /// forces the next derivation.
684    access_inputs: Option<u64>,
685    /// How many times the access tree has actually been derived, as against
686    /// asked for. Tests read it to tell a cache hit from a miss; nothing in
687    /// the library acts on it.
688    access_rebuilds: u64,
689}
690
691/// How long a type-ahead search buffer survives without a keystroke.
692/// Aged at
693/// the start of a frame, so a core with no clock never ages one.
694const TYPE_AHEAD_SECS: f64 = 1.0;
695
696/// One step along a list of `len` items from `at`, wrapping or clamping.
697/// None = the step ran off the end of a list that clamps.
698fn step(at: usize, delta: isize, len: usize, wrap: bool) -> Option<usize> {
699    let n = len as isize;
700    let p = at as isize + delta;
701    if (0..n).contains(&p) {
702        return Some(p as usize);
703    }
704    wrap.then(|| (((p % n) + n) % n) as usize)
705}
706
707/// What a frame left owed, by kind: [`Core::owed`]. `any()` is what
708/// [`Core::animating`] answers; `beyond_cycles()` is the same with a
709/// keyframe cycle — which never ends — left out.
710#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
711pub struct Owed {
712    /// A finite transition — a leg or a spring — still mid-flight.
713    pub transition: bool,
714    /// A keyframe cycle running; it always is, while its node is drawn.
715    pub cycle: bool,
716    /// An exit animation (a ghost) still departing.
717    pub depart: bool,
718    /// A frame a view asked for: `request_frame`, or an `animate` row —
719    /// or one the session's fonts ask for, moved under the text this
720    /// window shaped through another window (a new fallback list, a
721    /// rescan).
722    pub requested: bool,
723    /// A held drag scrolling its container.
724    pub autoscroll: bool,
725    /// A scroll container easing a programmatic offset change — a
726    /// `reveal` or a `set_scroll` on a container with a `transition`.
727    pub scroll: bool,
728}
729
730impl Owed {
731    /// Anything at all: the driver's reading.
732    pub fn any(self) -> bool {
733        self.transition
734            || self.cycle
735            || self.depart
736            || self.requested
737            || self.autoscroll
738            || self.scroll
739    }
740
741    /// Anything but a cycle: what a test waits on when the view has a
742    /// cycle that will never let `any()` clear. An eased scroll is a
743    /// finite leg like a transition, so it is in.
744    pub fn beyond_cycles(self) -> bool {
745        self.transition || self.depart || self.requested || self.autoscroll || self.scroll
746    }
747}
748
749impl Core {
750    /// This window's palette, as of this frame.
751    /// Resolved from [`Core::theme_source`] and `env.system` at the start
752    /// of every frame, so it is already right by the time a view runs.
753    ///
754    /// Frame-stable on purpose: every widget in one frame paints from the
755    /// same palette, whatever the driver does to `env` while the view
756    /// runs. A host that writes `env.system` *directly* and wants the new
757    /// answer before its next frame calls [`Core::refresh_theme`]; every
758    /// env setter a binding exposes already does.
759    ///
760    /// Read inside a fill being kept (ADR 0045), the palette is one of
761    /// the facts the replay is checked against: a fill that drew from it
762    /// runs again when it changes (backlog F155).
763    pub fn theme(&self) -> &Theme {
764        self.note_read_once(replay::ONCE_THEME, || {
765            replay::Read::Theme(Box::new(self.theme))
766        });
767        &self.theme
768    }
769
770    /// Re-resolve the palette from `env.system` now, rather than at the
771    /// start of the next frame. What an env setter calls after writing.
772    pub fn refresh_theme(&mut self) {
773        self.theme = self.theme_source.resolve(&self.env.system);
774    }
775
776    /// Writes what the user set in the OS and re-resolves the palette from
777    /// it, so a host that pushes the appearance and reads the theme back
778    /// before its next frame sees the answer. The one door for
779    /// a binding's env setter: writing `env.system` by hand and forgetting
780    /// the refresh was a decision each of them had to remember.
781    pub fn set_system(&mut self, system: SystemEnv) {
782        self.env.system = system;
783        self.refresh_theme();
784    }
785
786    /// The env reading's inputs (`schema::ENV_FIELDS`): the stored env and
787    /// the frame's own facts — viewport, scale, focus — that ride in the
788    /// same reading.
789    pub fn env_facts(&self) -> crate::schema::EnvFacts {
790        let facts = self.env_facts_raw();
791        // A fill being kept read the frame's facts (ADR 0045).
792        self.note_read(|| replay::Read::Env(facts));
793        facts
794    }
795
796    pub(crate) fn env_facts_raw(&self) -> crate::schema::EnvFacts {
797        crate::schema::EnvFacts {
798            env: self.env,
799            // The frame's viewport, as its `ENV_FIELDS` row says: what the
800            // dock leaves, not the window it was begun with (backlog F43
801            // — this read `self.viewport`, and an app under `KUI_DEVTOOLS`
802            // sized its tiers to a window it did not have).
803            viewport: self.viewport(),
804            scale: self.scale,
805            focus: self.focus(),
806            focus_visible: self.focus_visible(),
807            region: self.region(),
808            // Read raw, not through the noting doors: the reading carries
809            // both for a binding to hand out, and a kept fill compares the
810            // reading less these two (`replay::env_same`); a view that
811            // wants them counted reads `Ui::caret_visible` / `Ui::now`.
812            caret_visible: self.edit.blink_visible(),
813            now: self.anim.time().unwrap_or(0.0),
814        }
815    }
816
817    /// Whether anyone actually *chose* the accent — the OS reported one,
818    /// or the app set or pinned one — as opposed to the palette falling
819    /// back to kui's own blue.
820    ///
821    /// The question the `accent` row asks before it repaints anything:
822    /// that row has always meant "the accent colour where there is one,
823    /// the `bg` I declared where there is not", so a view can name its
824    /// own fallback and a host that knows nothing changes nothing. The
825    /// theme widened *where* the accent comes from without widening
826    /// *whether* there is one.
827    pub fn has_accent(&self) -> bool {
828        let chosen = self.has_accent_raw();
829        self.note_read_once(replay::ONCE_ACCENT, || replay::Read::Accent(chosen));
830        chosen
831    }
832
833    /// [`Self::has_accent`], not noted for a kept fill.
834    pub(crate) fn has_accent_raw(&self) -> bool {
835        match self.theme_source {
836            ThemeSource::Derived => self.env.system.accent.is_some(),
837            ThemeSource::DerivedWithAccent(_) | ThemeSource::Pinned(_) => true,
838        }
839    }
840
841    /// Where the palette comes from. [`ThemeSource::Derived`] by default.
842    pub fn theme_source(&self) -> ThemeSource {
843        self.theme_source
844    }
845
846    /// Change where the palette comes from. Takes effect on the next
847    /// frame, and immediately for anything reading [`Core::theme`] after
848    /// this call, so a host may set it before its first frame or in a
849    /// handler and get the same answer either way.
850    pub fn set_theme_source(&mut self, source: ThemeSource) {
851        self.theme_source = source;
852        self.refresh_theme();
853    }
854
855    /// Pin a palette: this exact [`Theme`], following neither the OS's
856    /// appearance nor its accent. Shorthand for
857    /// [`ThemeSource::Pinned`].
858    pub fn set_theme(&mut self, theme: Theme) {
859        self.set_theme_source(ThemeSource::Pinned(theme));
860    }
861
862    /// Keep following the OS's light/dark, but paint this accent instead
863    /// of the OS's. Shorthand for [`ThemeSource::DerivedWithAccent`], and
864    /// what an app with a brand colour wants.
865    pub fn set_accent(&mut self, accent: Color) {
866        self.set_theme_source(ThemeSource::DerivedWithAccent(accent));
867    }
868
869    /// Go back to following the OS for both — the default.
870    pub fn derive_theme(&mut self) {
871        self.set_theme_source(ThemeSource::Derived);
872    }
873
874    /// The sizes the stock widgets are built from — the palette's other
875    /// axis ([`crate::metrics`]). [`Metrics::default`](crate::metrics::Metrics::default) until
876    /// the app sets one; nothing in the OS is followed. Noted for a fill
877    /// being kept, as [`Self::theme`] is.
878    pub fn metrics(&self) -> &crate::metrics::Metrics {
879        self.note_read_once(replay::ONCE_METRICS, || {
880            replay::Read::Metrics(Box::new(self.metrics))
881        });
882        &self.metrics
883    }
884
885    /// Declare the tokens the running origin references by name:
886    /// the host's outside a
887    /// fill, the filling extension's inside one. Replaces that origin's
888    /// table whole, so an app whose lengths change with a viewport tier
889    /// declares again on `resize`. A name a role owns is dropped with a
890    /// `reserved-token` warning, once per name. A derived token whose
891    /// source did not resolve is dropped with `unknown-token`, naming both.
892    pub fn set_tokens(&mut self, tokens: crate::tokens::Tokens) {
893        for name in tokens.reserved() {
894            let w = Warning {
895                code: crate::diag::RESERVED_TOKEN,
896                key: Key::ROOT.str(crate::diag::RESERVED_TOKEN).str(name),
897                message: format!(
898                    "`{name}` is a theme or metrics role, so the token is dropped: `${name}` \
899                     always means the role's value, and an app does not shadow one"
900                ),
901            };
902            self.diag.raise(w);
903        }
904        // A derived token whose source did not resolve (ADR 0028) is
905        // dropped the same way, and the warning names both ends: the
906        // token that is gone and the name that was not there for it.
907        for u in tokens.unresolved() {
908            let w = Warning {
909                code: crate::diag::UNKNOWN_TOKEN,
910                key: Key::ROOT.str(crate::diag::UNKNOWN_TOKEN).str(&u.token),
911                message: format!(
912                    "`${}` is dropped: it derives from `${}`, which is no colour token \n                     declared before it and no theme role",
913                    u.token, u.source
914                ),
915            };
916            self.diag.raise(w);
917        }
918        self.tokens.insert(self.origin, tokens);
919    }
920
921    /// Whether `origin` has declared a table — what an extension asks
922    /// before declaring the one it was loaded with, so a per-frame `view`
923    /// declares once.
924    pub fn tokens_declared(&self, origin: OriginId) -> bool {
925        self.tokens.contains_key(&origin)
926    }
927
928    /// The running origin's own table, if it declared one.
929    pub fn tokens(&self) -> Option<&crate::tokens::Tokens> {
930        self.tokens.get(&self.origin)
931    }
932
933    /// What a `$name` in a prop resolves to this frame: the running
934    /// origin's table over the host's, the theme's and metrics' roles in
935    /// front of both. What every binding lowers a reference through.
936    ///
937    /// A lookup can answer a theme or metrics role, and a binding bakes
938    /// what it answers into the spec, so a fill being kept that looks a
939    /// name up has read both (ADR 0045, backlog F155).
940    pub fn token_lookup(&self) -> crate::tokens::TokenLookup<'_> {
941        self.note_read_once(replay::ONCE_THEME, || {
942            replay::Read::Theme(Box::new(self.theme))
943        });
944        self.note_read_once(replay::ONCE_METRICS, || {
945            replay::Read::Metrics(Box::new(self.metrics))
946        });
947        let own = self.tokens.get(&self.origin);
948        let host = if self.origin == OriginId::HOST {
949            None
950        } else {
951            self.tokens.get(&OriginId::HOST)
952        };
953        crate::tokens::TokenLookup {
954            own,
955            host,
956            theme: &self.theme,
957            metrics: &self.metrics,
958            session: Some(&self.session),
959        }
960    }
961
962    /// A binding lowered a `family` that names nothing installed or
963    /// loaded: raise `unknown-family`, once per name. The text
964    /// shapes as sans, which is what the message says.
965    pub fn warn_unknown_family(&mut self, name: &str) {
966        self.diag.raise(Warning {
967            code: crate::diag::UNKNOWN_FAMILY,
968            key: Key::ROOT.str(crate::diag::UNKNOWN_FAMILY).str(name),
969            message: format!(
970                "`family` named {name:?}, and no installed or loaded font family has that name, \
971                 so the text shapes as sans; `systemFonts()` lists the names there are, and \
972                 `sans`, `serif` and `mono` are kui's own"
973            ),
974        });
975        // A family a sibling prop registered in the same parse is listed
976        // by the next frame's sync; nothing to do here.
977    }
978
979    /// A binding lowered a reference that did not resolve: raise
980    /// `unknown-token`, once per name, saying which slot asked. The slot
981    /// keeps its default, which is what the message says.
982    pub fn warn_unknown_token(&mut self, err: &crate::tokens::TokenError) {
983        let name = match err {
984            crate::tokens::TokenError::Unknown(n)
985            | crate::tokens::TokenError::Kind { name: n, .. } => n,
986        };
987        let w = Warning {
988            code: crate::diag::UNKNOWN_TOKEN,
989            key: Key::ROOT.str(crate::diag::UNKNOWN_TOKEN).str(name),
990            message: err.to_string(),
991        };
992        self.diag.raise(w);
993    }
994
995    /// Makes `metrics` the frame's: every stock widget from the next node
996    /// on is built from it, and `ui.metrics()` reads it back. Logical px,
997    /// before `env.scale`; a density is the app's to choose
998    /// (`Metrics::compact`, `Metrics::scaled`).
999    ///
1000    /// `titlebar_h` is the platform's row: left at the stock number (the
1001    /// platform's caption height, which every stock set carries), it is
1002    /// the height the driver opened this window's strip at
1003    /// ([`Core::set_platform_titlebar_h`]), so an app that sets a density
1004    /// keeps the titlebar its launcher asked for. Any other number is the
1005    /// app's and stands.
1006    pub fn set_metrics(&mut self, metrics: crate::metrics::Metrics) {
1007        let mut metrics = metrics;
1008        if metrics.titlebar_h == crate::metrics::Metrics::comfortable().titlebar_h {
1009            metrics.titlebar_h = self.platform_titlebar_h();
1010        }
1011        self.metrics = metrics;
1012    }
1013
1014    /// What the platform's caption height is for this window: `h` for a
1015    /// strip the driver knows is another height (backlog W22) — the
1016    /// runner's `Launcher::titlebar` under custom chrome off macOS, the
1017    /// titlebar it measured under macOS custom chrome — `None` for the
1018    /// platform's own. The `titlebar_h` metric follows it
1019    /// while it is at the stock number, now and through every later
1020    /// [`Core::set_metrics`]; an app that set a number of its own keeps
1021    /// it. A driver's call, made before the first frame.
1022    pub fn set_platform_titlebar_h(&mut self, h: Option<f32>) {
1023        let stock = self.metrics.titlebar_h == self.platform_titlebar_h();
1024        self.platform_titlebar_h = h.filter(|h| h.is_finite() && *h > 0.0);
1025        if stock {
1026            self.metrics.titlebar_h = self.platform_titlebar_h();
1027        }
1028    }
1029
1030    /// The titlebar height that stands for "the platform's" in this
1031    /// window: the driver's, or the stock caption height.
1032    fn platform_titlebar_h(&self) -> f32 {
1033        self.platform_titlebar_h
1034            .unwrap_or(crate::metrics::Metrics::comfortable().titlebar_h)
1035    }
1036
1037    /// A core with a session of its own — one window, nothing shared.
1038    pub fn new() -> Self {
1039        Self::new_in(&Session::new())
1040    }
1041
1042    /// A core joining an existing session: it draws with the same fonts,
1043    /// images, sounds, shaping caches and glyph atlas as every other core
1044    /// constructed against `session`, and plays through the same audio
1045    /// device. Everything else — tree, focus, scroll, viewport — is this
1046    /// window's alone.
1047    pub fn new_in(session: &Session) -> Self {
1048        let mut core = Self {
1049            session: session.clone(),
1050            text: TextSystem::new(),
1051            cells: crate::cells::CellStore::new(),
1052            atlas: GlyphAtlas::new(),
1053            resources: SharedResources::new(session),
1054            audio: SharedAudio::new(session),
1055            font_names: FxHashMap::default(),
1056            fonts_rev: u64::MAX,
1057            weights_rev: 0,
1058            images_rev: 0,
1059            interaction: Interaction::default(),
1060            scroll: ScrollStore::default(),
1061            edit: EditStore::default(),
1062            anim: AnimStore::default(),
1063            depart: DepartStore::default(),
1064            stats: FrameStats::default(),
1065            env: Env::default(),
1066            theme_source: ThemeSource::Derived,
1067            theme: Theme::default(),
1068            tokens: Default::default(),
1069            metrics: crate::metrics::Metrics::default(),
1070            platform_titlebar_h: None,
1071            window_title: None,
1072            always_on_top: false,
1073            secure_input: false,
1074            option_as_alt: crate::input::OptionAsAlt::None,
1075            ime_off: false,
1076            focus: None,
1077            focus_visible: false,
1078            declared_focus: Vec::new(),
1079            declared_focus_last: Vec::new(),
1080            focus_asked: false,
1081            key_labels: LabelIndex::default(),
1082            key_labels_last: LabelIndex::default(),
1083            slot_labels: LabelIndex::default(),
1084            recording: None,
1085            keep_next: None,
1086            kept: Default::default(),
1087            slot_fills: Vec::new(),
1088            slot_fills_last: Vec::new(),
1089            ns_depth: usize::MAX,
1090            ns_key: Key::ROOT,
1091            dt_app: None,
1092            dt_dock: None,
1093            dt_window: false,
1094            dt_theme: None,
1095            dt_menus: None,
1096            dt_built: None,
1097            dt_tabs: Vec::new(),
1098            dt_area: Rect::new(0.0, 0.0, 0.0, 0.0),
1099            building: false,
1100            declared_windows: Vec::new(),
1101            declared_windows_last: Vec::new(),
1102            keys_held: Vec::new(),
1103            pressed_mods: None,
1104            access: Default::default(),
1105            access_built: 0,
1106            access_inputs: None,
1107            access_rebuilds: 0,
1108            tree: Tree::new(),
1109            inspect: false,
1110            dt_inspect: false,
1111            inspected: Vec::new(),
1112            prev_tree: Tree::new(),
1113            lines: Default::default(),
1114            paths: Default::default(),
1115            path_motion: Default::default(),
1116            path_parsed: Default::default(),
1117            path_textures: crate::path::PathTextures::new(session.clone()),
1118            output_read: true,
1119            fragments: Default::default(),
1120            stock_polygon: None,
1121            hit_shapes: Default::default(),
1122            display: DisplayList::default(),
1123            viewport: Size::ZERO,
1124            scale: 1.0,
1125            stack: Vec::new(),
1126            counters: Vec::new(),
1127            origin: OriginId::HOST,
1128            clips: Vec::new(),
1129            clip_ids: Vec::new(),
1130            opacity: Vec::new(),
1131            float_root: Vec::new(),
1132            float_stack: Vec::new(),
1133            hints: Vec::new(),
1134            menu: None,
1135            menu_actions: Vec::new(),
1136            menu_editor: None,
1137            menu_focus: None,
1138            native_menus: false,
1139            menu_sub: Default::default(),
1140            menu_bar_sub: Default::default(),
1141            menu_bar: None,
1142            menu_bar_rev: 0,
1143            menu_bar_origin: OriginId::HOST,
1144            menu_bar_open: None,
1145            menu_bar_root: None,
1146            selects: Vec::new(),
1147            native_menu_bar: false,
1148            lookup_available: false,
1149            selection: None,
1150            cell_selection: None,
1151            awaiting_selection: false,
1152            awaiting_paste: false,
1153            file_ask: Default::default(),
1154            select_dragging: None,
1155            drag_follow: None,
1156            last_frame_time: None,
1157            line_carry: None,
1158            scroll_latch: Default::default(),
1159            sel_ords: Vec::new(),
1160            sel_ends: None,
1161            scopes: Vec::new(),
1162            rows: Vec::new(),
1163            modal: None,
1164            modal_focus: Vec::new(),
1165            type_ahead: String::new(),
1166            type_ahead_at: None,
1167            items_scratch: Vec::new(),
1168            ghost_opacity: Vec::new(),
1169            ghost_clip: Vec::new(),
1170            ghost_clip_ids: Vec::new(),
1171            ghost_rect: Vec::new(),
1172            frame_requested: false,
1173            frame_due: None,
1174            exits_named: Default::default(),
1175            trace: cause::Trace::default(),
1176            pending_reveal: Vec::new(),
1177            pending_reveal_labels: Vec::new(),
1178            focus_reported: Vec::new(),
1179            pending_scroll_labels: Vec::new(),
1180            pending_focus_step: None,
1181            region: None,
1182            region_held: false,
1183            region_focus: Vec::new(),
1184            pending_region: None,
1185            ime_rect: None,
1186            sink_caret: None,
1187            sink_caret_solid: false,
1188            sink_caret_stamp: 0,
1189            pending: Vec::new(),
1190            framed: false,
1191            system_seen: SystemEnv::default(),
1192            system_fonts_seen: 0,
1193            frame_no: 0,
1194            layouts: FxHashMap::default(),
1195            announcements: Vec::new(),
1196            last_announcement: None,
1197            events_answered: 0,
1198            diag: Diagnostics::default(),
1199        };
1200        core.sync_font_names();
1201        core
1202    }
1203
1204    // -- Measurement ----------------------------------------------------
1205    // The layout engine measures text every frame; these hand the same
1206    // numbers to the view, so it never re-derives them by hand.
1207
1208    /// Measures `content` in `style` without adding a node: its unwrapped
1209    /// size, or with `max_w` (logical px) its size once wrapped to that
1210    /// width — what layout would give a text node with that content and
1211    /// style, `wrap` / `max_lines` / `ellipsis` included. Logical px at
1212    /// the scale of the current or last frame (1 before any frame).
1213    /// Shapes through the text cache, so measuring a string and then
1214    /// drawing it shapes once.
1215    pub fn measure_text(
1216        &mut self,
1217        content: &str,
1218        style: &TextStyle,
1219        max_w: Option<f32>,
1220    ) -> TextMetrics {
1221        self.note_read(|| replay::Read::Measure(self.text_rev()));
1222        let sess = &mut *self.session.state();
1223        self.text
1224            .measure(content, style, &sess.resources, &mut sess.fonts, max_w)
1225    }
1226
1227    /// `measure_text` for a rich-text paragraph.
1228    pub fn measure_rich_text(
1229        &mut self,
1230        spans: &[Span<'_>],
1231        base: &TextStyle,
1232        max_w: Option<f32>,
1233    ) -> TextMetrics {
1234        let sess = &mut *self.session.state();
1235        self.text
1236            .measure_rich(spans, base, &sess.resources, &mut sess.fonts, max_w)
1237    }
1238
1239    /// Where a point lands in the text node `key` drew: a byte offset into
1240    /// its content and the visual row within that node — counted across
1241    /// every run the key covers by where the rows sit, so a `line` row of
1242    /// inline runs is one row and a wrapped run as many as it wrapped to;
1243    /// not the ordinal `line` node a pointer event names
1244    /// — or `None` for a key that is not a text node or was not drawn.
1245    /// A `role="none"` subtree under the key (a gutter) is
1246    /// not its text, as the access tree reads it. `point` is logical
1247    /// viewport px — the `x`/`y` a click or drag event carries — so a
1248    /// custom editor turns the event into a caret position with one call
1249    /// instead of measuring prefixes or assuming a cell width. Answered
1250    /// from the frame that finished: between frames that is the layout the
1251    /// pointer was over, and during a build it is the last one, since the
1252    /// node being declared has no layout yet. A wrapped node answers in
1253    /// the width it was drawn at.
1254    pub fn text_hit(&self, key: Key, point: Vec2) -> Option<TextHit> {
1255        let hit = self.text_hit_raw(key, point);
1256        self.note_read(|| replay::Read::TextHit(key, point, hit));
1257        hit
1258    }
1259
1260    pub(crate) fn text_hit_raw(&self, key: Key, point: Vec2) -> Option<TextHit> {
1261        // The host's point is its own viewport's; the turns, like the
1262        // text, are the window's, so the shift comes first.
1263        self.text.hit_at(
1264            key,
1265            self.unturned(key, point.plus(self.dt_shift())),
1266            self.building,
1267        )
1268    }
1269
1270    /// `p`, a window point, pulled back through every turn the node `key`
1271    /// was drawn under in the frame that finished (ADR 0043): where on the
1272    /// node's upright layout — the space its text, its caret and its
1273    /// content origin are in — the point falls. The point itself on a
1274    /// frame that turns nothing, which pays one branch, and during a
1275    /// build, which has no finished node to read the turn off.
1276    pub(crate) fn unturned(&self, key: Key, p: Vec2) -> Vec2 {
1277        if self.building || !self.tree.any_transform {
1278            return p;
1279        }
1280        self.tree
1281            .index_of(key)
1282            .map_or(p, |i| self.unturned_at(i, p))
1283    }
1284
1285    /// [`Self::unturned`] for the node at tree index `i` of the frame that
1286    /// finished, for a caller that has the index.
1287    pub(crate) fn unturned_at(&self, i: usize, p: Vec2) -> Vec2 {
1288        if self.building || !self.tree.any_transform {
1289            return p;
1290        }
1291        match self.clips.get(i) {
1292            Some(c) if c.turned() => c.transform.unapply(p),
1293            _ => p,
1294        }
1295    }
1296
1297    /// `r`, a rect in the node `key`'s upright layout, as drawn: the box
1298    /// it covers through the node's turns (ADR 0043), the way the access
1299    /// rect is a turned node's bounding box. `r` itself when nothing turns.
1300    pub(crate) fn turned_rect(&self, key: Key, r: Rect) -> Rect {
1301        if self.building || !self.tree.any_transform {
1302            return r;
1303        }
1304        match self.tree.index_of(key).and_then(|i| self.clips.get(i)) {
1305            Some(c) if c.turned() => c.transform.bounds(r),
1306            _ => r,
1307        }
1308    }
1309
1310    /// The caret rect for byte `byte` of the text node `key` drew: logical
1311    /// viewport px, zero wide, one line tall — where a caret, an IME
1312    /// candidate window or a selection edge goes. `byte` past the content
1313    /// is the end. Answered from the same frame `text_hit` is.
1314    pub fn caret_rect(&self, key: Key, byte: usize) -> Option<Rect> {
1315        let shift = self.dt_shift();
1316        self.text
1317            .caret_at(key, byte, self.building)
1318            // Turned in the window's space, then moved into the host's.
1319            .map(|r| self.turned_rect(key, r))
1320            .map(|r| Rect::new(r.x - shift.x, r.y - shift.y, r.w, r.h))
1321    }
1322
1323    // -- Announcements ---------------------------------------------------
1324
1325    /// Says something once, with no node behind it: "Saved", "3 results".
1326    /// Queued for [`Self::take_announcements`], the way `play` queues an
1327    /// audio command — an announcement is a consequence of an event, and
1328    /// the frame's tree, which is a function of state, has no place to
1329    /// keep one.
1330    /// A region whose text changes on screen is the other half, and is
1331    /// the `live` prop instead.
1332    ///
1333    /// [`Live::Off`](crate::access::Live::Off) and an empty string are both no-ops — the first so a
1334    /// caller can gate politeness without an `if`, the second because
1335    /// every platform needs a name to say.
1336    pub fn announce(&mut self, text: &str, live: crate::access::Live) {
1337        if live == crate::access::Live::Off || text.is_empty() {
1338            return;
1339        }
1340        if let Some((last, frame, answered)) = &self.last_announcement
1341            && last == text
1342            && *frame + 1 >= self.frame_no
1343            && *answered == self.events_answered
1344        {
1345            self.diag.raise(crate::diag::announcement_repeated(text));
1346        }
1347        self.last_announcement = Some((text.to_string(), self.frame_no, self.events_answered));
1348        self.announcements.push(crate::access::Announcement {
1349            text: text.to_string(),
1350            live,
1351        });
1352    }
1353
1354    /// Drains the announcements queued since the last drain. Windowed
1355    /// runners drain every frame whether or not assistive technology is
1356    /// attached, and discard what they cannot deliver, so a real app never
1357    /// accumulates and nothing is spoken minutes late; headless drivers
1358    /// assert on what comes back.
1359    pub fn take_announcements(&mut self) -> Vec<crate::access::Announcement> {
1360        std::mem::take(&mut self.announcements)
1361    }
1362
1363    /// Announcements queued and not yet drained (what `pending` is for
1364    /// audio commands).
1365    pub fn pending_announcements(&self) -> &[crate::access::Announcement] {
1366        &self.announcements
1367    }
1368
1369    // -- Diagnostics ----------------------------------------------------
1370
1371    /// Drains the warnings raised since the last drain (see [`crate::diag`]):
1372    /// silent misconfigurations the core noticed while finishing frames,
1373    /// each distinct (code, node) pair once. Windowed runners print them;
1374    /// headless tests assert on them.
1375    pub fn take_warnings(&mut self) -> Vec<Warning> {
1376        // Handles of other sessions resolve where the registry can see
1377        // them and this core cannot (shaping, the audio backend), so the
1378        // registry keeps them and the core draining warnings reports them.
1379        for f in self.session.state().resources.take_foreign() {
1380            self.diag.raise(crate::diag::foreign_resource(&f));
1381        }
1382        if let Some(w) = crate::diag::size_expressions_full() {
1383            self.diag.raise(w);
1384        }
1385        self.diag.take()
1386    }
1387
1388    /// Every warning this core has raised, drained or not, oldest first —
1389    /// for a reader that is not the driver. The runner drains
1390    /// [`Self::take_warnings`] after every frame and prints them, so a
1391    /// view that wants to *show* them (a development overlay) would
1392    /// otherwise never see one; this is the log the drain leaves behind.
1393    pub fn warnings_raised(&self) -> &[Warning] {
1394        self.diag.raised()
1395    }
1396
1397    /// Raises a warning a binding built (see [`crate::diag::unknown_prop`]):
1398    /// a frontend sees declarations the tree walk cannot, because a prop
1399    /// name nothing claims never becomes part of a node. Behind the same
1400    /// [`Self::set_diagnostics`] gate and the same once-per-(code, key)
1401    /// dedup as the checks, so a binding may raise one per node per frame.
1402    pub fn warn(&mut self, warning: Warning) {
1403        self.diag.raise(warning);
1404    }
1405
1406    /// Turns the diagnostic checks on or off. A bare `Core` has them on;
1407    /// drivers set them for the build they are in (the runner: debug on,
1408    /// release off; Node loops: off under `NODE_ENV=production`; a
1409    /// standalone C context: off until asked).
1410    pub fn set_diagnostics(&mut self, on: bool) {
1411        self.diag.enabled = on;
1412    }
1413
1414    pub fn diagnostics(&self) -> bool {
1415        self.diag.enabled
1416    }
1417
1418    /// Wheel line-deltas (e.g. winit's LineDelta) to logical px.
1419    pub fn lines_to_px(lines: f32) -> f32 {
1420        lines * SCROLL_LINE_PX
1421    }
1422
1423    /// Rasterizes outline glyphs as LCD subpixel coverage
1424    /// (`QuadKind::GlyphSubpixel`) instead of alpha masks. Drivers set it
1425    /// from what their renderer can blend per channel; flipping it drops
1426    /// the glyph atlas so every glyph re-rasterizes in the new mode.
1427    pub fn set_subpixel_text(&mut self, on: bool) {
1428        if self.text.set_subpixel(on) {
1429            self.atlas.clear();
1430        }
1431    }
1432
1433    pub fn subpixel_text(&self) -> bool {
1434        self.text.subpixel()
1435    }
1436
1437    /// The byte budget for the shaped-text cache: every
1438    /// text a frame draws is shaped once and kept, and past this many
1439    /// estimated bytes the least recently drawn entries go, down to three
1440    /// quarters of it, at the start of the next frame. What the last
1441    /// frame drew is never evicted, so a budget too small for one
1442    /// screenful costs re-shaping nothing — it only stops keeping what
1443    /// scrolled away. Default `DEFAULT_TEXT_CACHE_BYTES` (64 MB): a
1444    /// terminal streaming new lines lowers it, a document viewer that
1445    /// wants every page it showed to stay warm raises it. The clock that
1446    /// empties an idle cache after 300 frames is unchanged.
1447    pub fn set_text_cache_budget(&mut self, bytes: usize) {
1448        self.text.set_budget(bytes);
1449    }
1450
1451    pub fn text_cache_budget(&self) -> usize {
1452        self.text.budget()
1453    }
1454
1455    /// What the shaped-text cache holds, as the estimate the budget is
1456    /// charged against (a fixed floor per entry plus a per-glyph rate,
1457    /// calibrated against a counting allocator; see `text.rs`).
1458    pub fn text_cache_bytes(&self) -> usize {
1459        self.text.bytes()
1460    }
1461
1462    /// How many shaped texts the cache holds (a long line's chunks each
1463    /// count).
1464    pub fn text_cache_len(&self) -> usize {
1465        self.text.len()
1466    }
1467
1468    /// How many long lines — no-wrap texts past `LONG_LINE_BYTES`, shaped
1469    /// in chunks — are held.
1470    pub fn long_lines(&self) -> usize {
1471        self.text.long_lines()
1472    }
1473
1474    /// The frame clock for transitions: monotonic seconds, any origin.
1475    /// Drivers set it before every frame, and before handing the core
1476    /// input or the app its events (backlog F139) — the windowed runner
1477    /// does both — so a handler that reads [`Self::now`] after an idle
1478    /// stretch reads the time it runs at, not the last frame's. A driver
1479    /// that never sets it gets snapping instead of animation.
1480    pub fn set_time(&mut self, now_secs: f64) {
1481        self.anim.set_time(now_secs);
1482        self.scroll.set_time(now_secs);
1483    }
1484
1485    /// The frame clock (backlog F134): the driver's monotonic seconds the
1486    /// transitions and cycles of this frame read, from whatever origin the
1487    /// driver chose; 0 before a driver sets one. What a view reads for
1488    /// "is this toast due", so that the app's deadlines and the core's
1489    /// easing agree, and a test that moves the clock (`Drive::advance`,
1490    /// Node's `advance`, `kui_set_time`) moves both. Read from an event
1491    /// handler it is the time the input was handled at: the windowed
1492    /// runner stamps the clock before input as well as before a frame
1493    /// (backlog F139), so a deadline set in a click handler after the
1494    /// window sat idle counts from the click.
1495    pub fn now(&self) -> f64 {
1496        self.note_read(|| replay::Read::Clock);
1497        self.anim.time().unwrap_or(0.0)
1498    }
1499
1500    /// True when the last frame left a transition mid-flight, or a view
1501    /// asked for another frame — drivers schedule one without waiting for
1502    /// input. One bool over every source; [`owed`](Self::owed) is the
1503    /// same reading by kind.
1504    pub fn animating(&self) -> bool {
1505        self.owed().any()
1506    }
1507
1508    /// What the last frame left owed, by kind. To a driver
1509    /// the kinds are one — it schedules the frame either way — but a
1510    /// test that wants to know whether the *transitions* have run out
1511    /// under a keyframe cycle that never will reads `cycle` apart from
1512    /// the rest: [`Owed::beyond_cycles`] is that wait's predicate.
1513    pub fn owed(&self) -> Owed {
1514        let (transition, cycle) = self.anim.owes();
1515        Owed {
1516            transition,
1517            cycle,
1518            depart: self.depart.animating(),
1519            requested: self.frame_requested
1520                || self.tree.any_animate
1521                || self.fonts_moved()
1522                || self.submenu_waiting(),
1523            autoscroll: self.autoscrolling(),
1524            scroll: self.scroll.animating(),
1525        }
1526    }
1527
1528    /// Whether the session's fonts moved under the text this window shaped
1529    /// since its last frame began — a new fallback list, a rescan, a face of
1530    /// a registered family come or gone, through whichever window of the
1531    /// session — so the window owes a frame to shape it again (backlog
1532    /// RG118). The window that made the change asks for its own frame;
1533    /// this is how every other window hears, through the `animating` a
1534    /// driver polls for each of its windows, with no list of the session's
1535    /// windows to wake. A window that has not drawn has nothing to shape
1536    /// again. `try_state`, since a driver may ask while the session is
1537    /// borrowed, where it reads as nothing owed.
1538    fn fonts_moved(&self) -> bool {
1539        self.framed
1540            && self
1541                .session
1542                .try_state()
1543                .is_some_and(|sess| sess.weights_rev != self.weights_rev)
1544    }
1545
1546    /// Asks the driver for one more frame right after this one. A view
1547    /// that sets up a transition by drawing a starting state (a new split
1548    /// drawn collapsed so it can slide open) needs the next frame to come
1549    /// without waiting for input — the starting state itself snaps, so
1550    /// nothing is mid-flight yet to request it.
1551    ///
1552    /// Traced ([`Self::set_frame_trace`]), the calling line is kept as
1553    /// the frame's [`cause::FrameRequest`], which is why this tracks its
1554    /// caller.
1555    #[track_caller]
1556    pub fn request_frame(&mut self) {
1557        self.taint_kept("it asked for a frame");
1558        self.frame_requested = true;
1559        self.trace_request();
1560    }
1561
1562    /// Asks for a frame at `at` on the frame clock (`Core::now`'s seconds,
1563    /// backlog F135): a toast's expiry, a sequence's next beat. Not a
1564    /// frame now and not one every vsync — the driver wakes at that time
1565    /// (`next_frame_at`) and the view runs; until then nothing is owed, so
1566    /// `animating()` stays false. The earliest of the times asked for wins
1567    /// and is kept until a frame is begun at or past it, so a view need not
1568    /// ask again every frame (asking again is harmless). A time already
1569    /// past, or with no clock, is a frame now, as `request_frame` is. A
1570    /// time that is not a number is ignored.
1571    pub fn request_frame_at(&mut self, at: f64) {
1572        self.taint_kept("it asked for a frame at a time");
1573        // NaN asks for nothing, and so does a time that never comes.
1574        if at.is_nan() || at == f64::INFINITY {
1575            return;
1576        }
1577        if self.anim.time().is_none_or(|now| at <= now) {
1578            self.request_frame();
1579            return;
1580        }
1581        self.frame_due = Some(self.frame_due.map_or(at, |d| d.min(at)));
1582    }
1583
1584    /// The exit `key` leaves by if it leaves in the frame that finishes
1585    /// next (backlog F136), over the `exit` the node declared: a card
1586    /// thrown left by a button that was resting with its exit aimed the
1587    /// other way. Called from a handler between frames, or from the view
1588    /// of the frame that stops declaring the node; cleared when that frame
1589    /// finishes, so a node that stays is not left with it. It aims an
1590    /// `exit` the node declares — a node that declares none leaves no
1591    /// picture to replay — with the node's own `transition`.
1592    pub fn set_exit(&mut self, key: Key, exit: crate::enter::Enter) {
1593        self.exits_named.insert(key, exit);
1594    }
1595
1596    /// The frame-clock time a driver should next draw at for a
1597    /// `request_frame_at`, `None` when nothing was asked for: what a
1598    /// runner folds into the deadline it sleeps to.
1599    pub fn next_frame_at(&self) -> Option<f64> {
1600        self.frame_due
1601    }
1602
1603    /// Starts a frame. Build the tree through the returned `Ui` (or the
1604    /// `Core` builder methods directly), then `Ui::finish` — the one door
1605    /// out of a frame, which runs the extension fills, the devtools panel
1606    /// and the open menu before layout. A driver holding a bare `Core`
1607    /// mid-frame finishes through `Ui::wrap(core).finish()`.
1608    pub fn frame(&mut self, viewport: Size, scale: f32) -> Ui<'_> {
1609        self.begin_frame(viewport, scale);
1610        Ui::new(self)
1611    }
1612
1613    /// `frame` with something to fill the slots the view declares — the
1614    /// runner's extension list (`[Box<dyn Extension>]` is a `Fill`), or a
1615    /// test's stand-in. `Ui::slot` calls it in place, and `Ui::finish`
1616    /// lets it fill `"root"` and report unknown slots before layout.
1617    pub fn frame_with<'a>(
1618        &'a mut self,
1619        viewport: Size,
1620        scale: f32,
1621        filler: &'a mut dyn crate::slot::Fill,
1622    ) -> Ui<'a> {
1623        self.begin_frame(viewport, scale);
1624        Ui::with_filler(self, filler)
1625    }
1626
1627    /// The session this core draws from. Hand it to `Core::new_in` to open
1628    /// another window sharing its fonts, images, sounds and glyph atlas.
1629    pub fn session(&self) -> &Session {
1630        &self.session
1631    }
1632
1633    /// Runs an edit-store operation against the session's font system —
1634    /// the one the text cache shapes with, so an editor and a text node
1635    /// measure the same. The session borrow lasts exactly the call.
1636    fn edit_with_fonts<T>(
1637        &mut self,
1638        f: impl FnOnce(&mut EditStore, &mut cosmic_text::FontSystem) -> T,
1639    ) -> T {
1640        let sess = &mut *self.session.state();
1641        f(&mut self.edit, &mut sess.fonts)
1642    }
1643
1644    /// The viewport (logical px) the current frame was begun with — the
1645    /// window, less the devtools' dock while the panel is docked:
1646    /// what the host lays out into. Changes to it
1647    /// arrive as `resize` events (see `take_pending_events`), a dock
1648    /// coming, going or resizing among them.
1649    pub fn viewport(&self) -> Size {
1650        Size::new(self.dt_area.w, self.dt_area.h)
1651    }
1652
1653    /// The device pixel ratio the current frame was begun with.
1654    pub fn scale(&self) -> f32 {
1655        self.scale
1656    }
1657
1658    /// The origin nodes opened right now are tagged with: `OriginId::HOST`
1659    /// in the host's own view, the filling extension's inside a fill (see
1660    /// `Core::fill`).
1661    pub fn origin(&self) -> OriginId {
1662        self.origin
1663    }
1664
1665    /// The finished frame's draw data: display list plus the glyph atlas the
1666    /// renderer mirrors (mutable so it can clear the dirty flag).
1667    pub fn output(&mut self) -> (&DisplayList, &mut GlyphAtlas) {
1668        self.output_read = true;
1669        (&self.display, &mut self.atlas)
1670    }
1671
1672    /// Begins a frame without handing out a [`Ui`]: what [`Core::frame`]
1673    /// calls first. A binding that drives the builder methods on the core
1674    /// directly starts here and ends with `Ui::wrap(core).finish()`.
1675    pub fn begin_frame(&mut self, viewport: Size, scale: f32) {
1676        // A frame at or past the time asked for is that frame.
1677        if let (Some(due), Some(now)) = (self.frame_due, self.anim.time())
1678            && due <= now
1679        {
1680            self.frame_due = None;
1681        }
1682        // First, while the last frame's tree and every store's reading of
1683        // it are still whole: why this frame runs, and who held it
1684        // (backlog F111).
1685        self.trace_begin_frame();
1686        // Against the finished frame, before anything below clears it: a
1687        // held drag re-places its live end where the last layout moved
1688        // the text under the pointer, and steps its scroller when the
1689        // pointer is past the edge (ADR 0029).
1690        self.follow_drag();
1691        self.age_type_ahead();
1692        // A window that changed size is a fact the driver reports, so the
1693        // core turns it into data like any other: `{kind="resize", width,
1694        // height, scale}` on the root, pending for the driver to route
1695        // after the frame. The first frame establishes the viewport rather
1696        // than resizing it.
1697        // The host's viewport is what the dock leaves of the window
1698        // (ADR 0024), so a dock that comes, goes or is dragged is a
1699        // resize too.
1700        let area = self.devtools_area(viewport);
1701        if self.framed
1702            && (area.w != self.dt_area.w || area.h != self.dt_area.h || scale != self.scale)
1703        {
1704            self.pending.push(UiEvent {
1705                origin: OriginId::HOST,
1706                window: WindowId::MAIN,
1707                key: Key::ROOT,
1708                payload: Value::map([
1709                    ("kind", Value::str("resize")),
1710                    ("width", Value::Float(area.w as f64)),
1711                    ("height", Value::Float(area.h as f64)),
1712                    ("scale", Value::Float(scale as f64)),
1713                ]),
1714                slot: None,
1715            });
1716        }
1717        self.dt_area = area;
1718        // And what the user set in the OS — and whether assistive
1719        // technology is listening, which rides in the same reading. A
1720        // driver that learns of a change writes it into `env` and asks
1721        // for a redraw — which is
1722        // enough for a host whose view is a function the runner calls
1723        // every frame, and nothing at all for one that retains the tree
1724        // it was handed (Node, C, Lua): its `view` runs when a message
1725        // changes the model, so the change has to *be* a message. The
1726        // first frame establishes the reading rather than reporting it,
1727        // the way the viewport does.
1728        if self.framed && self.env.system != self.system_seen {
1729            let sys = self.env.system;
1730            self.pending.push(UiEvent {
1731                origin: OriginId::HOST,
1732                window: WindowId::MAIN,
1733                key: Key::ROOT,
1734                payload: Value::map([
1735                    ("kind", Value::str("system")),
1736                    ("appearance", Value::str(sys.appearance.name())),
1737                    (
1738                        "accent",
1739                        sys.accent
1740                            .map_or(Value::Null, |c| Value::Int(c.to_hex() as i64)),
1741                    ),
1742                    ("motion", Value::str(sys.motion.name())),
1743                    (
1744                        "locale",
1745                        sys.locale.map_or(Value::Null, |l| Value::str(l.as_str())),
1746                    ),
1747                    ("assistive", Value::str(sys.assistive.name())),
1748                ]),
1749                slot: None,
1750            });
1751        }
1752        self.system_seen = self.env.system;
1753        // And the installed fonts, rescanned since the last frame and found
1754        // changed (`reload_system_fonts`, which the winit runner calls when
1755        // the OS says so): a message for the same reason as `system`, an
1756        // app holding `systemFonts()` in its model having nothing else to
1757        // re-read it on. The first frame establishes it, as above.
1758        let fonts_rev = self.session.state().system_fonts_rev;
1759        if self.framed && fonts_rev != self.system_fonts_seen {
1760            self.pending.push(UiEvent {
1761                origin: OriginId::HOST,
1762                window: WindowId::MAIN,
1763                key: Key::ROOT,
1764                payload: Value::map([("kind", Value::str("fonts"))]),
1765                slot: None,
1766            });
1767        }
1768        self.system_fonts_seen = fonts_rev;
1769        // The palette is a function of what the OS said and what the app
1770        // asked for, so it is recomputed rather than invalidated: a
1771        // couple of dozen float ops once a frame, against a cache that
1772        // would have to be poked from every writer of `env.system`.
1773        self.refresh_theme();
1774        self.framed = true;
1775        self.frame_no += 1;
1776        self.replay_begin_frame();
1777        // The layout rects a node reported, swept on the one cadence
1778        // every by-last-use store sweeps on (`retain::sweep_cutoff`, AR45).
1779        if let Some(cutoff) = crate::retain::sweep_cutoff(self.frame_no) {
1780            self.layouts.retain(|_, (_, seen)| *seen >= cutoff);
1781        }
1782        self.viewport = viewport;
1783        self.scale = scale;
1784        self.window_title = None;
1785        self.always_on_top = false;
1786        self.secure_input = false;
1787        self.option_as_alt = crate::input::OptionAsAlt::None;
1788        self.ime_off = false;
1789        // The drawn menu bar's root is this frame's: a view that stops
1790        // calling `widgets::menu_bar` leaves nothing behind for the next
1791        // event to land on. Re-recorded while the widget builds.
1792        self.menu_bar_root = None;
1793        // And the select fields, re-declared by the ones the view builds.
1794        self.selects.clear();
1795        // Last frame's focus declarations are what this frame's are
1796        // compared against (see `set_key_focus`).
1797        std::mem::swap(&mut self.declared_focus, &mut self.declared_focus_last);
1798        self.declared_focus.clear();
1799        // And the labels `key_of` resolves through, the same way.
1800        std::mem::swap(&mut self.key_labels, &mut self.key_labels_last);
1801        self.key_labels.clear();
1802        // Slots are positions, not declarations to diff: one clear.
1803        self.slot_labels.clear();
1804        self.ns_depth = usize::MAX;
1805        self.building = true;
1806        // And the window declarations, which `finish_frame` diffs the same
1807        // way (see `declare_window`).
1808        std::mem::swap(&mut self.declared_windows, &mut self.declared_windows_last);
1809        self.declared_windows.clear();
1810        // A frame that declared an `exit` may be the last one some node is
1811        // ever seen in, so it is kept whole: the two tree buffers swap
1812        // roles instead of one being cleared, which costs an allocation
1813        // that already existed and no copying. Nothing else keeps it — a
1814        // frame with no exits empties the spare, so a stale tree can never
1815        // be diffed against.
1816        let keep_prev = self.tree.any_exit;
1817        if keep_prev {
1818            std::mem::swap(&mut self.tree, &mut self.prev_tree);
1819        } else {
1820            self.prev_tree.clear();
1821        }
1822        self.tree.clear();
1823        // The drops a frame carried and no backend read - a frame built
1824        // twice before one is drawn, a window that framed and did not
1825        // render - ride on, or the texture a path animated out of would
1826        // be the backend's for good (RG112). Bounded, for a core that is
1827        // never read at all and so has no backend to tell.
1828        let unread = (!self.output_read).then(|| {
1829            const MOST: usize = 4096;
1830            let mut t = std::mem::take(&mut self.display.dropped_textures);
1831            let mut f = std::mem::take(&mut self.display.dropped_fragments);
1832            t.drain(..t.len().saturating_sub(MOST));
1833            f.drain(..f.len().saturating_sub(MOST));
1834            (t, f)
1835        });
1836        self.display.clear();
1837        if let Some((t, f)) = unread {
1838            self.display.dropped_textures = t;
1839            self.display.dropped_fragments = f;
1840        }
1841        self.output_read = false;
1842        // Removed handles the backend has not heard of, and this window's
1843        // atlas slots for removed images (AR8); kept on the session
1844        // because a removal can land between frames, after the list was
1845        // cleared, and through a window that never draws again.
1846        self.sync_dropped();
1847        // Before anything is emitted: the one point where the atlas may
1848        // empty its page — one the last frame extended, or one about to
1849        // fill — without a quad sampling what it dropped (F99).
1850        self.atlas.begin_frame();
1851        // An image's spare buffer outlives its stream by a few frames (W20).
1852        self.session.state().resources.release_spares();
1853        // Before the text store begins its frame, as a scale change is.
1854        self.sync_weights();
1855        // The text list goes with the tree: a kept frame's text nodes carry
1856        // that frame's `TextId`s, and nothing else can resolve them.
1857        self.text.begin_frame(
1858            &mut self.session.state().fonts,
1859            scale,
1860            keep_prev,
1861            self.frame_no,
1862        );
1863        // And the strokes, for the same reason: a kept frame's `line`
1864        // nodes index that frame's list.
1865        self.lines.begin_frame(keep_prev);
1866        self.paths.begin_frame(keep_prev);
1867        let cutoff = self.frame_no.saturating_sub(crate::path::ANIMATING_WINDOW);
1868        if self.path_motion.len() > 1024 {
1869            self.path_motion.retain(|_, m| m.seen >= cutoff);
1870        }
1871        if self.path_parsed.len() > 1024 {
1872            self.path_parsed.retain(|_, p| p.seen >= cutoff);
1873        }
1874        self.fragments.begin_frame(keep_prev);
1875        self.cells.begin_frame(scale);
1876        self.sync_font_names();
1877        self.anim.begin_frame(self.frame_no);
1878        self.depart.begin_frame(self.frame_no);
1879        // The two stores that keep state by key across a key's absence:
1880        // they stamp this frame onto what it declares, and cap what it
1881        // does not (backlog F26).
1882        self.edit.begin_frame(self.frame_no);
1883        self.scroll.begin_frame(self.frame_no);
1884        self.tree.push(
1885            NIL,
1886            Key::ROOT,
1887            OriginId::HOST,
1888            NodeSpec::column().fill(),
1889            NodeContent::Container,
1890        );
1891        self.stack.clear();
1892        self.stack.push(0);
1893        self.counters.clear();
1894        self.counters.push(0);
1895        // A frame that ended with nodes unclosed must not leak its hints
1896        // into the next one.
1897        self.hints.clear();
1898        self.origin = OriginId::HOST;
1899        self.frame_requested = false;
1900        // And the lines that asked, with it: an ask the reset above
1901        // forgets is not one the next frame was held by.
1902        self.trace_forget_requests();
1903        self.devtools_begin_frame();
1904    }
1905}
1906
1907impl Default for Core {
1908    fn default() -> Self {
1909        Self::new()
1910    }
1911}
1912
1913/// A frontend that draws into the shared tree each frame — the trait the
1914/// runner uses to host Lua (or any other) extensions without knowing what
1915/// they are. Origins are assigned by the runner.
1916///
1917/// Where it draws is a slot the host declared:
1918/// `slots` names
1919/// the ones it fills, `view` is called once per frame for each of them
1920/// with which one it is, and an extension naming none is called once
1921/// after the host's view for the reserved `"root"` slot — the sequence
1922/// every extension got before slots existed. `on_event` answers with
1923/// replies: values the runner hands to the host's own `on_event`, with
1924/// this extension's origin on them.
1925pub trait Extension {
1926    fn name(&self) -> &str;
1927    /// The slot names this extension fills; empty means `"root"`. The one
1928    /// entry [`crate::slot::ANY_SLOT`] (`"*"`) means every name declared
1929    /// under its namespace, for an extension whose slots are not known
1930    /// when it loads — a Lua host whose `init.lua` registers views at
1931    /// runtime, one slot per view — and it then gets no `unknown-slot`
1932    /// warning, since there is no list to check against.
1933    fn slots(&self) -> &[String] {
1934        &[]
1935    }
1936    fn view(&mut self, slot: &crate::slot::Slot<'_>, ui: &mut Ui<'_>) -> Result<(), String>;
1937    /// One of this extension's events; the values returned are replies
1938    /// to the host, delivered in order.
1939    fn on_event(&mut self, ev: &UiEvent) -> Vec<Value>;
1940}