Skip to main content

ui/
lib.rs

1// ui -- the immediate-mode runtime: one Ui context instead of ASMLIB's g_* globals  [src/ui]
2//
3//   run(desc, app, style) -> exit code   command line, headless (--shot / --script) or window
4//   App: init(ui) / frame(ui) builds the UI every pass / command(ui, id, arg) -> handled
5//   Style: how widgets look (style.rs, the contract of ASMLIB/inc/style.inc); the app
6//   passes the style crate it was linked with (build_rs.py --style)
7//   main!(expr): the app's entry point (jfgui_main, called by plat's process entry)
8//
9// Modules (one per ASMLIB file): ctx (ui_ctx.s: input, hit testing, behaviour, slots,
10// timers, commands, semantics, clip, layers, passes), layout (ui_layout.s), cmd (ui_cmd.s:
11// draw command lists and their replay), dirty (ui_dirty.s), theme (ui_theme.s; app themes
12// over the style and the run-time style switch, theme_app.rs: Theme and its file), basic
13// (ui_basic.s), auto (ui_auto.s: scripts and the widget dump), runtime (ui_main.s), cursor
14// (ui_cursor.s), drop (ui_drop.s), dnd (drag and drop between widgets and programs), dock
15// (docking: panels at the sides in tab groups, floating tool windows), kbnav (keyboard
16// navigation between widgets, panes and windows), clipimg
17// (ui_clipimg.s), style (style.inc); the widget families popup, edit, list, value, color, win,
18// chrome, filedlg (R5 agents); rtl (the right-to-left mirror of the recorded commands and the
19// input); ids (widget ids from names), settings_page (the page of a base::settings schema),
20// winstate (window placement kept between runs, AF_SAVE_STATE); fontpick (system fonts: loading
21// a choice, the font picker and dialog); tasks (async tasks on the UI thread: ui.spawn, run
22// between passes; plat::task).
23//
24// State: ASMLIB keeps every window's UI state in per-module blocks (`*_wb0` .. `*_wb1`)
25// that ui_win.s swaps when it builds another window.  Here that state is `WinParts` (the
26// live window's part of every module: `ui.wp.ctx`, `ui.wp.lay`, `ui.wp.cmd`, `ui.wp.dirty`,
27// `ui.wp.rt`, `ui.wp.popup`, ...); win.rs swaps it with a window's saved WinParts
28// (core::mem::swap).  State shared by all windows lives in `Ui` itself (core) and in
29// `ui.m` (the widget families' shared state).
30//
31// For the widget families (R5): the asm globals map to fields -- g_mx / g_my / g_focus /
32// g_hot / g_active / g_last_rect / g_keyq ... are `self.wp.ctx.*`, g_boxes / g_next_* are
33// `self.wp.lay.*`, g_dcol / the command lists `self.wp.cmd.*`, g_fb / g_ui_dpi
34// `self.wp.rt.fb` / `.dpi`; g_frame / g_now_ms / g_repass / g_mods / g_sems / slots / timers /
35// g_uiw_cur are fields of Ui.  A family's per-window state goes into its `W` (in WinParts),
36// its shared state into its `S` (in Mods).  Style calls: `let st = self.style; let mut sp =
37// Sp { .. }; let (w, h) = st.xxx_size(self, &mut sp); self.place(w, h); let ev =
38// self.behave(id, hb); self.sp_placed(&mut sp, ev); st.xxx(self, &sp);
39// self.focus_ring_draw(ev); self.sem(K_*, id, label, value)` (basic.rs shows every pattern).
40// The core calls into the families through these functions (all ported, R5): win.rs uiw_*,
41// chrome.rs titlebar_begin / titlebar_end / edge_grips (ui-win), popup.rs accel_dispatch /
42// tooltip / popup_count (ui-popup), edit.rs text_key_filter, clip_set / clip_get /
43// hclip_text_* / open_folder / open_url (ui-edit).
44#![no_std]
45#![forbid(unsafe_code)]
46
47extern crate alloc;
48
49pub mod consts;
50pub mod keys;
51mod a11y;
52mod auto;
53mod auto_perm;
54mod agent;
55// the app's file types at start-up (--register / --unregister)
56mod assoc;
57mod basic;
58mod chart;
59mod chrome;
60mod clipimg;
61mod cmd;
62mod color;
63mod ctx;
64mod cursor;
65mod data;
66mod datepick;
67mod diagram; // node / edge canvas (diagram.rs)
68pub use diagram::{
69    dg_edge_points, dg_hairline, dg_length, dg_paint_grid, dg_point_at, dg_stroke_edge, DEdge, DEv, DHit, DModel, DNode, DPort, DgFrame, DgGeo,
70    DgPaint, DgRoute, DiagramState, Viewport, DG_CONNECT, DG_PAN, DG_PLACE, DG_SELECT, DN_LOCKED, DN_NOSELECT, DPT_HIDDEN,
71    DPT_USED, DR_BEZIER, DR_ORTHO, DR_STRAIGHT,
72};
73pub use diagram::{dg_toggle_rect, DN_FRAME, DN_OUTLINE, DN_RESIZE, DN_STEP, DN_TOGGLE}; // frames, groups, a backdrop (G1)
74pub use diagram::DN_HITMIN; // a small text stays selectable at low zoom
75mod dirty;
76mod dnd;
77// (ROADMAP §6) the editors with a text model of their own: the shared core, rich text, code
78mod ed_core;
79pub use ed_core::{TextPos, TextSel};
80mod rich_doc;
81pub use rich_doc::{
82    list_marks, Fmt, Para, RichDoc, RA_CENTER, RA_JUSTIFY, RA_LEFT, RA_RIGHT, LEVEL_MAX, LS_BULLET, LS_NONE, LS_NUMBER, PS_H1, PS_H2,
83    PS_H3, PS_NORMAL, PS_QUOTE, RF_ALL, RF_BOLD, RF_ITALIC, RF_STRIKE, RF_UNDER,
84};
85mod rich_edit;
86pub use rich_edit::{
87    RTB_ALIGN, RTB_ALL, RTB_CHARS, RTB_CLEAR, RTB_COLOR, RTB_HISTORY, RTB_LINK, RTB_LISTS, RTB_STYLE, RT_NO_FRAME, RT_OPEN_LINKS, RT_PLAIN_PASTE,
88    RT_READONLY, RT_TAB,
89};
90mod rich_html;
91// (ROADMAP §6) the Markdown / HTML viewer: its document (md_doc.rs) and the view (md_view.rs)
92mod md_doc;
93pub use md_doc::{
94    MdBlock, MdBox, MdDoc, MdField, MdFieldSpot, MdForm, MdImage, MdLinkSpot, MdOption, MdSpan, MdTable, MA_CENTER, MA_LEFT, MA_NONE, MA_RIGHT,
95    MB_CODE, MB_HEADING, MB_PARA, MB_ROW, MB_RULE, MF_BUTTON, MF_CHECKBOX, MF_HIDDEN, MF_PASSWORD, MF_RADIO, MF_RESET, MF_SELECT, MF_SUBMIT,
96    MF_TEXT, MF_TEXTAREA, MK_BULLET, MK_NONE, MK_NUMBER, MS_BOLD, MS_CODE, MS_ITALIC, MS_STRIKE, MS_UNDER,
97};
98// (ROADMAP §7) the element tree of an HTML document: queries and changes (md_dom.rs)
99mod md_dom;
100pub use md_dom::MdNode;
101mod md_view;
102pub use md_view::{md_field_id, MdEv, MdSubmit, MdViewState, MI_APP, MI_ASK, MI_LOCAL, MV_LTR, MV_NO_FRAME, MV_OPEN_LINKS, MV_RTL};
103// (ROADMAP §6) lists whose rows have different heights (ui.vlist_begin)
104mod vlist;
105pub use vlist::VListState;
106mod code_doc;
107pub use code_doc::{CodeDoc, LineChange, FIND_CASE, FIND_MAX, FIND_WORD};
108pub use code_doc::{Diag, DIAG_ERROR, DIAG_INFO, DIAG_WARNING}; // (L6) a code editor's diagnostics
109mod highlight;
110pub use highlight::{
111    CodeTheme, Grammar, Highlighter, Rule, Token, INI, JSON, MARKDOWN, PLAIN, RUST, SY_CODE, SY_COMMENT, SY_CONST, SY_COUNT, SY_EMPH, SY_ESCAPE,
112    SY_FUNC, SY_HEADING, SY_KEY, SY_KEYWORD, SY_LINK, SY_META, SY_NUMBER, SY_PUNCT, SY_STRING, SY_STRONG, SY_TEXT, SY_TYPE, TOML,
113};
114pub use highlight::JFS; // (L6) the grammar of apps/jfs/crates/script's .jfs
115mod code_edit;
116mod code_long;
117pub use code_edit::{CodeStatus, CE_NO_FIND, CE_NO_FRAME, CE_NO_GUTTER, CE_NO_LINE, CE_READONLY};
118pub use code_edit::CE_MARKS; // (L6) the gutter's column of diagnostic markers
119pub use code_long::code_long_check; // (tests) what the code editor keeps of long lines
120mod dock;
121mod drop;
122mod edit;
123mod filedlg;
124mod fontpick;
125pub use fontpick::{font_language, FontSel};
126mod grid;
127mod hexview;
128pub use hexview::{HexEv, HexMark, HexState};
129// (ROADMAP §6) SVG pictures (ui.svg) and the image viewer (ui.image_view)
130mod imgview;
131pub use imgview::{ImageViewState, SvgFonts, SvgText, ViewImage, IV_NO_BARS, IV_NO_CHECKER, IV_NO_GRID, IV_SMOOTH, IV_TEXT_COLOR, IV_WHEEL_SCROLL};
132pub use img_svg::Svg;
133mod history;
134pub use history::{Edit, History, Snapshot};
135// hot reload: the session's input recorded and replayed after a restart (build_rs.py watch;
136// development builds only)
137mod hotreload;
138// hyphenation of wrapped texts and text boxes (TX_HYPHENATE, TB_HYPHENATE)
139mod hyph;
140pub mod i18n;
141mod ids;
142// the output through a swapchain: Windows and Linux programs against glibc, their windows only
143// (docs/GPU.md "Ausgabe der UI"), Android, the browser (WebGPU: "Ausgabe im Browser"); static Linux
144// programs link nothing of it
145#[cfg(any(windows, jfgui_glibc, target_os = "android", target_arch = "wasm32"))]
146mod gpu_out;
147#[cfg(not(any(windows, jfgui_glibc, target_os = "android", target_arch = "wasm32")))]
148#[path = "gpu_out_off.rs"]
149mod gpu_out;
150#[cfg(any(windows, jfgui_glibc, target_os = "android"))]
151pub use gpu_out::{GPU_OUT_FS, GPU_OUT_FS_IMAGE, GPU_OUT_FS_TILE, GPU_OUT_VS, GPU_OUT_WGSL};
152// a canvas whose zoom and pan go through GPU textures (canvas.rs); on every target
153mod canvas;
154pub use canvas::{CanvasContent, CanvasDesc, CanvasGpu, CanvasView};
155// the live inspector: overlay and tool window (--inspect, F12 / Ctrl+Shift+I)
156mod inspect;
157// ... its editing: development builds only (build_rs.py --cfg jfgui_dev), --release reads only
158#[cfg(jfgui_dev)]
159mod inspect_edit;
160#[cfg(not(jfgui_dev))]
161#[path = "inspect_edit_off.rs"]
162mod inspect_edit;
163// ... the sources of the widgets (#[track_caller] on the widget calls): development builds only
164#[cfg(jfgui_dev)]
165mod inspect_src;
166#[cfg(not(jfgui_dev))]
167#[path = "inspect_src_off.rs"]
168mod inspect_src;
169// keyboard navigation: focus groups, panes and windows (F6), splitters (F8), the menu key
170mod kbnav;
171pub use ids::{ID_HASH_BASE, wid, wid_path};
172mod layout;
173mod list;
174mod notify;
175mod office_gallery;
176mod perm;
177mod popup;
178// the app's recent files and the system's (ui.recent_add / recent_files)
179mod recent;
180// large redraws on several threads: row bands of the dirty rectangles (render_live's hook)
181mod render_par;
182mod ribbon;
183mod settings_page;
184mod shell;
185mod slider;
186// the system's sounds: ui.beep, the sound of a message dialog, the app's switch (sound.rs)
187mod sound;
188mod rtl;
189mod runtime;
190// the monitors, global shortcuts, the colour pick, pictures of the screen (plat::screen, hotkey)
191mod screen;
192mod sensors;
193// scroll copies: a scrolled view's still valid pixels are moved instead of drawn (scrollcopy.rs)
194mod scrollcopy;
195mod stock;
196mod style;
197mod symbol;
198mod taskbar;
199mod tasks;
200mod term;
201mod touch;
202pub use touch::{Finger, TwoFingers};
203pub use term::{TermEv, TermState, TF_CTRL_SHIFT6, TF_NO_FRAME, TF_NO_INPUT, TF_QHELP, TK_KEY, TM_BUSY, TM_LINE, TM_RAW, TM_SECRET};
204pub mod theme;
205mod timeline;
206pub use timeline::{TimelineState, TlTrack};
207mod tray;
208mod value;
209mod vicon;
210mod win;
211mod winstate;
212// (L7) windows inside an area of a window and a taskbar (desk.rs)
213mod desk;
214pub use desk::{
215    DeskEv, DeskState, DeskWin, DTB_ICONS, DWF_FIXED, DWF_MAXIMIZED, DWF_NOCLOSE, DWF_NOMAX, DWF_NOMIN, DWF_NOTASK, DW_LEFT, DW_MAX, DW_MIN,
216    DW_RIGHT,
217};
218
219use alloc::collections::{BTreeMap, VecDeque};
220use alloc::string::String;
221use alloc::vec::Vec;
222
223use gfx::Dc;
224use text::Text;
225
226pub use clipimg::{clip_bmp_from_sf, clipimg_parse, clipimg_record};
227pub use cmd::{Cmd, Draw, DrawRef, Gfx, IconHook};
228pub use consts::*;
229pub use ctx::{Sem, Slot, ease};
230pub use cursor::CursorDraw;
231pub use edit::TextboxPresentation;
232pub use edit::TextSurfaceLine;
233pub use keys::*;
234pub use layout::{dp_at, px2dip_at};
235pub use perm::boot;
236pub use assoc::{assoc_switch, desk_boot};
237pub use recent::{RECENT_KEEP, RecentOpen};
238// the library's version (the root Cargo.toml, docs/VERSIONING.md) and the app's own (apps/NAME/PACKAGE)
239pub use rt::{app_version, VERSION, VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH};
240// drag and drop (dnd.rs): what a drag carries, its kinds and effects
241pub use plat::{DE_ALL, DE_COPY, DE_LINK, DE_MOVE, DT_FILES, DT_HTML, DT_IMAGE, DT_STD, DT_TEXT, DT_URL, DT_USER0, DragData};
242pub use popup::{Command, Mi}; // ui-popup: the command table and menu records
243pub use ribbon::{RibbonGroup, RibbonGroupMode, RibbonLayout, RibbonMetrics, RibbonSample};
244pub use runtime::{Runtime, launch, run, run_window};
245pub use style::{Sp, Style, StyleBasic, StyleChrome, StyleData, StyleEdit, StyleList, StyleMisc, StylePopup, StyleShell, StyleValue};
246pub use data::{GridCol, GridEv, GridState, TreeEv, TreeRow, TreeState};
247pub use chart::{BarSeries, DonutSlice};
248// (L5) line charts of time series (chart.rs): the ring of points, what a chart shows, the axis maths
249pub use chart::{
250    lc_nice, lc_time_step, lc_time_text, LcMarker, LcSeries, LcThreshold, LcUnit, LineChart, SeriesBuf, SeriesCol, LC_NO_GRID, LC_NO_HOVER,
251    LC_NO_LEGEND, LX_NANOS, LX_NUM, LX_SECS,
252};
253pub use shell::{CmdItem, NavState};
254// docking (dock.rs): the panels, the layout, the sides and flags
255pub use dock::{
256    DockPanel, DockState, DOCK_BOTTOM, DOCK_CENTER, DOCK_FLOAT, DOCK_FLOAT_MAX, DOCK_LEFT, DOCK_MK_BOTTOM, DOCK_MK_CENTER, DOCK_MK_LEFT,
257    DOCK_MK_RIGHT, DOCK_MK_TOP, DOCK_RIGHT, DP_HIDDEN, DP_NOCLOSE, DP_NOFLOAT, DOCK_HOLD_MS, DOCK_TAB_HOLD,
258};
259// the taskbar button's progress and jump list tasks (ui.taskbar_progress / taskbar_tasks)
260pub use plat::taskbar::{JumpTask, TaskbarProgress};
261pub use plat::hotkey::{Chord, HotkeyError};
262pub use plat::screen::{Monitor, ScreenError};
263pub use stock::{card_opaque, kind_colors, Stock};
264pub use vicon::{paint_svg_icon, paint_svg_icon2};
265pub use symbol::{SymPalette, Symbol, SymbolCache, SYM_BODY, SYM_CACHE_MAX_W, SYM_CRIT, SYM_HI, SYM_INK, SYM_OK, SYM_PORT, SYM_ROLES,
266    SYM_SCREEN, SYM_SHADE, SYM_TINT, SYM_WARN}; // multi-colour vector symbols (tools/vsym.py)
267pub use theme::color_mix;
268pub use theme::{contrast_ratio, luminance};
269pub use theme::{Theme, ThemeError}; // app themes over the style (theme.rs, theme_app.rs)
270pub use win::Shared; // ui-win: the app's open flags / WINRECTs the library keeps
271// ui-list: the value / colour / list API beside the Ui methods
272pub use color::{hsv_to_rgb, rgb_to_hsv};
273pub use value::{cvtd, deg_atan2, deg_sincos, grad_at, part_id, px_mix, RangeF, Track, NO_DEFAULT};
274
275/// the app's entry point: `ui::main!(ui::run(&DESC, &mut MyApp::default(), &style::STYLE));`
276/// (expands to the `jfgui_main` symbol the platform entry calls; the app crate denies
277/// unsafe code, this one item is the exception).  It first sets up what build_rs.py built in
278/// (perm.rs): the app's name for its folders (JFGUI_APP), its assets as app://
279/// (apps/NAME/assets, packed to JFGUI_ASSETS with --cfg jfgui_assets) and its permissions
280/// (apps/NAME/PERMISSIONS as JFGUI_PERMISSIONS), then its file types and URL schemes
281/// (apps/NAME/PACKAGE as JFGUI_PACKAGE: the switches --register / --register-replace /
282/// --unregister, assoc.rs).  A Cargo build hands both over as files (JFGUI_PERMISSIONS_FILE with
283/// --cfg jfgui_manifest, JFGUI_PACKAGE_FILE with --cfg jfgui_package), because an environment
284/// variable of a build script holds one line only.
285///
286/// In a program with std (feature `std` of jflib) it also defines `fn main`, which hands over to
287/// plat's start of a std program (`plat::std_start`: crash handlers, the panic hook, then
288/// `jfgui_main`, the end through `std::process::exit`); the crate then has neither `#![no_std]`
289/// nor `#![no_main]`.  In a library crate (the template's app) that `fn main` stays unused and
290/// the program's own calls `jflib::plat::std_start()`.
291#[cfg(not(feature = "std"))]
292#[macro_export]
293macro_rules! main {
294    ($body:expr) => {
295        $crate::__jfgui_main!($body);
296    };
297}
298
299/// (see above)
300#[cfg(feature = "std")]
301#[macro_export]
302macro_rules! main {
303    ($body:expr) => {
304        $crate::__jfgui_main!($body);
305        #[allow(unsafe_code, dead_code)]
306        fn main() {
307            unsafe extern "Rust" {
308                fn jfgui_std_start() -> !;
309            }
310            // SAFETY: plat defines it in a program with std; it takes no arguments.
311            unsafe { jfgui_std_start() }
312        }
313    };
314}
315
316/// ui::main!'s `jfgui_main`, without and with std
317#[doc(hidden)]
318#[macro_export]
319macro_rules! __jfgui_main {
320    ($body:expr) => {
321        #[cfg(panic = "unwind")]
322        compile_error!("JFLib needs panic = \"abort\": add [profile.dev] and [profile.release] with panic = \"abort\" to this project's Cargo.toml");
323        #[allow(unsafe_code, unexpected_cfgs)]
324        #[unsafe(no_mangle)]
325        fn jfgui_main() -> i32 {
326            #[cfg(jfgui_manifest)]
327            const JF_MANIFEST: Option<&str> = Some(include_str!(env!("JFGUI_PERMISSIONS_FILE")));
328            #[cfg(not(jfgui_manifest))]
329            const JF_MANIFEST: Option<&str> = option_env!("JFGUI_PERMISSIONS");
330            #[cfg(jfgui_package)]
331            const JF_PACKAGE: Option<&str> = Some(include_str!(env!("JFGUI_PACKAGE_FILE")));
332            #[cfg(not(jfgui_package))]
333            const JF_PACKAGE: Option<&str> = option_env!("JFGUI_PACKAGE");
334            #[cfg(jfgui_assets)]
335            static ASSETS: &[u8] = include_bytes!(env!("JFGUI_ASSETS"));
336            #[cfg(not(jfgui_assets))]
337            static ASSETS: &[u8] = &[];
338            if let Err(code) = $crate::boot(option_env!("JFGUI_APP"), JF_MANIFEST, ASSETS) {
339                return code;
340            }
341            if let Some(code) = $crate::desk_boot(JF_PACKAGE) {
342                return code;
343            }
344            $body
345        }
346    };
347}
348
349/// the app descriptor (APP_DESC): title, AF_* flags, sizes in DIP, accent 0xRRGGBB.  Write
350/// `AppDesc { title: "..", w: .., ..AppDesc::NONE }` so later fields keep their defaults.
351#[derive(Clone, Copy)]
352pub struct AppDesc {
353    pub title: &'static str,
354    pub flags: u32,
355    pub w: u16,
356    pub h: u16,
357    pub min_w: u16,
358    pub min_h: u16,
359    pub accent: u32,
360    /// (ui-popup) the command table (AD_CMDS: ids, labels, icons, accelerators, CF_* flags)
361    pub cmds: &'static [Command],
362    /// (ui-popup) the menu bar of menubar(None) (AD_MENUBAR: Mi::Menu records)
363    pub menubar: &'static [Mi],
364}
365
366impl AppDesc {
367    /// every field empty / zero
368    pub const NONE: AppDesc =
369        AppDesc { title: "", flags: 0, w: 0, h: 0, min_w: 0, min_h: 0, accent: 0, cmds: &[], menubar: &[] };
370
371    /// a copy in the UI language (after i18n::set_catalog): the title, the command table's and
372    /// the menu bar's labels
373    pub fn translated(&self) -> AppDesc {
374        AppDesc { title: i18n::t(self.title), cmds: i18n::commands(self.cmds), menubar: i18n::menu(self.menubar), ..*self }
375    }
376}
377
378/// the app's callbacks (APP_DESC on_init, on_frame, on_command)
379pub trait App {
380    /// Optional domain actions, in addition to the universal UI action catalogue.
381    fn actions(&self) -> &'static [agent_core::ActionSpec] { &[] }
382    /// Domain revision. None uses the UI runtime's own: it moves with the controls (their set,
383    /// labels, values and states, the focus, an open modal dialog, the title, enabled commands)
384    /// and with every agent call that changes something, not with frames or the pointer, and it
385    /// knows nothing of a change the user makes to the app's data.
386    fn action_revision(&self) -> Option<u64> { None }
387    /// Dynamic domain constraints such as read-only files or an absent selection.
388    fn action_available(&self, _spec: &agent_core::ActionSpec) -> Result<(), agent_core::ActionError> { Ok(()) }
389    /// Domain action handler; adapters should call the same editing functions as the GUI.
390    fn action(&mut self, _ui: &mut Ui, _spec: &agent_core::ActionSpec, _input: &agent_core::Value,
391        _context: &agent_core::ActionContext) -> Result<agent_core::ActionResult, agent_core::ActionError> {
392        Err(agent_core::ActionError::new(agent_core::ErrorCode::NotFound, "No domain action with this name"))
393    }
394    /// Filtered domain state; the default universal inspection includes the widget tree.
395    fn action_inspect(&self, _query: &agent_core::Value) -> Result<agent_core::Value, agent_core::ActionError> {
396        Ok(agent_core::Value::object([]))
397    }
398    /// Describe an application-owned binary resource returned by one of its actions.
399    fn action_resource_info(&self, _uri: &str) -> Result<agent_core::Resource, agent_core::ActionError> {
400        Err(agent_core::ActionError::new(agent_core::ErrorCode::NotFound, "Resource not found"))
401    }
402    /// Read a bounded byte range of an application-owned resource, never an arbitrary path.
403    fn action_resource_read(&self, _uri: &str, _offset: u64, _limit: usize) -> Result<Vec<u8>, agent_core::ActionError> {
404        Err(agent_core::ActionError::new(agent_core::ErrorCode::NotFound, "Resource not found"))
405    }
406    /// called before the first frame; false = initialisation failed (exit code 2)
407    fn init(&mut self, _ui: &mut Ui) -> bool {
408        true
409    }
410    /// builds the UI (every pass; no side effects except through widgets)
411    fn frame(&mut self, ui: &mut Ui);
412    /// commands queued with ui.cmd_run and the built-in UIC_* ids; true = handled
413    fn command(&mut self, _ui: &mut Ui, _id: u32, _arg: u32) -> bool {
414        false
415    }
416}
417
418/// the per-window state of the live window: the core's per-window blocks (ui_ctx_wb0,
419/// ui_layout_wb0, ui_cmd_wb0, ui_dirty_wb0, ui_main_wb0) and the families' (ui_popup,
420/// ui_list, ui_chrome).  win.rs keeps one of these per window and swaps it with `Ui::wp`
421/// when another window is built (core::mem::swap); a new window starts from `WinParts::new`.
422pub struct WinParts {
423    pub(crate) ctx: ctx::W,
424    pub(crate) lay: layout::W,
425    pub(crate) cmd: cmd::W,
426    pub(crate) dirty: dirty::W,
427    pub(crate) rt: runtime::W,
428    #[allow(dead_code)] // R5: filled by the widget-family ports
429    pub(crate) popup: popup::W,
430    #[allow(dead_code)]
431    pub(crate) list: list::W,
432    #[allow(dead_code)]
433    pub(crate) chrome: chrome::W,
434    /// right-to-left: the regions that keep their LTR content (rtl.rs)
435    pub(crate) rtl: rtl::W,
436    /// the scroll views of the pass, their content grids of the last frame (scrollcopy.rs)
437    pub(crate) scopy: scrollcopy::W,
438    /// the canvases of the pass (canvas.rs)
439    pub(crate) canv: canvas::W,
440}
441
442impl WinParts {
443    /// the state of a window before its first pass (the main window: ui_init)
444    pub(crate) fn new() -> WinParts {
445        WinParts {
446            ctx: ctx::W::new(),
447            lay: layout::W::default(),
448            cmd: cmd::W::new(&cmd::CAPS_MAIN),
449            dirty: dirty::W::default(),
450            rt: runtime::W::new(),
451            popup: Default::default(),
452            list: Default::default(),
453            chrome: Default::default(),
454            rtl: Default::default(),
455            scopy: Default::default(),
456            canv: Default::default(),
457        }
458    }
459}
460
461/// the shared state of the widget-family modules
462#[derive(Default)]
463#[allow(dead_code)] // R5: filled by the widget-family ports
464pub(crate) struct Mods {
465    pub(crate) popup: popup::S,
466    pub(crate) edit: edit::S,
467    pub(crate) list: list::S,
468    pub(crate) value: value::S,
469    pub(crate) color: color::S,
470    pub(crate) win: win::S,
471    pub(crate) chrome: chrome::S,
472    pub(crate) filedlg: filedlg::S,
473    pub(crate) chart: chart::S,
474    pub(crate) shell: shell::S,
475    pub(crate) notify: notify::S,
476    pub(crate) a11y: a11y::S,
477    pub(crate) dock: dock::S,
478    pub(crate) kbnav: kbnav::S,
479    pub(crate) fonts: fontpick::S,
480    pub(crate) term: term::S,
481    pub(crate) perm: perm::S,
482    /// (ROADMAP §6) the rich-text editors' views (rich_edit.rs)
483    pub(crate) rich: rich_edit::S,
484    /// (ROADMAP §6) the code editors' views, theme and font size (code_edit.rs)
485    pub(crate) code: code_edit::S,
486    /// (ROADMAP §12) the live inspector (inspect.rs)
487    pub(crate) ins: inspect::S,
488    /// (ROADMAP §11) hot reload: the session's record and replay (hotreload.rs)
489    pub(crate) hr: hotreload::S,
490    /// (ROADMAP §11) the inspector's editing: the changes, their history (inspect_edit.rs)
491    pub(crate) ied: inspect_edit::S,
492    /// (ROADMAP §11) the inspector's sources: the call being built, the records' sources
493    /// (inspect_src.rs)
494    pub(crate) isrc: inspect_src::S,
495    /// (ROADMAP §6) ui.svg's draw objects, the faces of SVG text (imgview.rs)
496    pub(crate) svg: imgview::S,
497    /// (ROADMAP §4) hyphenation: languages, dictionaries (hyph.rs)
498    pub(crate) hyph: hyph::S,
499    /// (ROADMAP §6) the open lists of ui.vlist_begin (vlist.rs)
500    pub(crate) vlist: vlist::S,
501    /// (ROADMAP §3) the app's sound switch, the last sound asked for (sound.rs)
502    pub(crate) sound: sound::S,
503    /// (ROADMAP §3) the colour pick, the last picture, dropped shortcuts (screen.rs)
504    pub(crate) screen: screen::S,
505    /// (ROADMAP §2) the reopen of a recent file waiting for the browser (recent.rs)
506    pub(crate) recent: recent::S,
507    /// the scroll copies since the last `expect scrollcopy` (scrollcopy.rs)
508    pub(crate) scopy: scrollcopy::S,
509    /// (ROADMAP §8) the band buffers and glyph record of multi-threaded renders (render_par.rs)
510    pub(crate) rpar: render_par::S,
511    /// (L7) the window areas (desk.rs)
512    pub(crate) desk: desk::S,
513    /// the finger: the gesture recogniser (touch.rs)
514    pub(crate) touch: touch::S,
515    /// (G3a) the output through a swapchain (gpu_out.rs)
516    pub(crate) gout: gpu_out::S,
517}
518
519/// what a window shows through (Ui::gpu_output): status and counters of the GPU output, for
520/// tests and measurements
521#[derive(Clone, Debug, Default)]
522pub struct GpuOutput {
523    /// the output is switched on for this run (jfgui_GPU, the `gpu on` of a script)
524    pub wanted: bool,
525    /// the window's frames go through its swapchain now
526    pub on: bool,
527    /// its swapchain is being built
528    pub building: bool,
529    /// a frame waits for a free image, or for the window to come back
530    pub pending: bool,
531    /// no output in this run: no Vulkan, no usable adapter or surface, this target
532    pub never: bool,
533    /// the adapter (empty: none yet) and the present mode in use
534    pub adapter: alloc::string::String,
535    pub present_mode: &'static str,
536    /// what the last failure said (None: the last switch went well)
537    pub error: Option<alloc::string::String>,
538    /// bytes of GPU and staging memory the device holds in blocks
539    pub reserved: u64,
540    /// frames presented through the swapchain / the CPU path
541    pub frames: u64,
542    pub cpu_frames: u64,
543    /// bytes uploaded to the window's textures, those of them to the base texture
544    pub upload_bytes: u64,
545    pub upload_base: u64,
546    /// pixels of the base layer drawn again (what a layer above it no longer costs)
547    pub base_px: u64,
548    /// frames drawn layer by layer
549    pub split_frames: u64,
550    /// the layers above the base with a texture now, its bytes, and the bytes kept of the pixels
551    /// below them
552    pub layer_textures: u32,
553    pub layer_bytes: u64,
554    pub layer_store: u64,
555    /// images drawn as textures in the window's last frame; of all windows: textures, their
556    /// bytes, uploads, frames that drew one, textures evicted
557    pub image_quads: u32,
558    pub image_textures: u32,
559    pub image_bytes: u64,
560    pub image_uploads: u64,
561    pub image_hits: u64,
562    pub image_evicted: u64,
563    /// canvases (canvas.rs): those whose view comes from tiles now in this window, those drawn so
564    /// in its last frame, its frames that moved one; of all windows: tiles, their bytes, tiles drawn
565    /// and the time that took (us), tiles evicted, gestures ended by the CPU's picture
566    pub canvas_apart: u32,
567    pub canvas_quads: u32,
568    pub canvas_frames: u64,
569    pub canvas_tiles: u32,
570    pub canvas_bytes: u64,
571    pub canvas_fills: u64,
572    pub canvas_fill_us: u64,
573    pub canvas_evicted: u64,
574    pub canvas_settles: u64,
575    /// jfgui_GPU_VERIFY: where the tiles' picture differed from the CPU's during a gesture: frames,
576    /// pixels, the sum of the largest channel difference per pixel, the largest one
577    pub canvas_diff_frames: u64,
578    pub canvas_diff_px: u64,
579    pub canvas_diff_sum: u64,
580    pub canvas_diff_max: u32,
581    /// time in the window's renders and presents (us)
582    pub render_us: u64,
583    pub present_us: u64,
584    /// switches to the swapchain, and back to the CPU path
585    pub switches: u32,
586    pub leaves: u32,
587    /// jfgui_GPU_VERIFY: images read back and compared, those that differed
588    pub verified: u64,
589    pub verify_bad: u64,
590}
591
592/// the UI context: everything ASMLIB keeps in g_* variables
593pub struct Ui {
594    pub(crate) agent: agent::State,
595    pub desc: AppDesc,
596    pub style: &'static dyn Style,
597    /// fonts, glyph cache, text runs (shared: the asm's font module)
598    pub text: Text,
599    /// the gfx draw context (the asm's g_dc; render time only)
600    pub dc: Dc,
601    /// the live window's per-window state (see WinParts)
602    pub(crate) wp: WinParts,
603    /// the widget families' shared state
604    pub(crate) m: Mods,
605
606    // ---- ctx: shared by all windows (ui_ctx.s after ui_ctx_wb1)
607    pub(crate) sems: Vec<Sem>,
608    pub(crate) semtext_len: usize,
609    pub(crate) sem_dummy: Sem,
610    pub(crate) slots: BTreeMap<u32, Slot>,
611    pub(crate) keydown: [u8; 256],
612    pub(crate) mpts: Vec<[i32; 2]>,
613    pub(crate) mprs: Vec<u32>,
614    pub(crate) mtilt: [i32; 2],
615    pub(crate) mpress: u32,
616    pub(crate) mptype: u32,
617    pub(crate) mpts_win: u32,
618    /// commands for App::command in the order queued (ctx.rs cmd_run, CMDQ_MAX)
619    pub(crate) cmdq: VecDeque<(u32, u32)>,
620    /// typed text that waits for the next passes (beyond a pass's TEXTQ_MAX, or left by a
621    /// text_take cap): (window, codepoints) in typing order (ctx.rs text_refill)
622    pub(crate) text_more: Vec<(u32, Vec<u32>)>,
623    /// (id, deadline ms) in the order set (ctx.rs timer, TIMERS_MAX)
624    pub(crate) timers: Vec<(u32, u32)>,
625    /// what each ceiling held back (ctx.rs limit_hit, LIM_*)
626    pub(crate) limits: [u32; ctx::LIM_N],
627    pub(crate) mods: u32,
628    pub(crate) frame: u32,
629    pub(crate) now_ms: u32,
630    pub(crate) repass: bool,
631    pub(crate) animating: bool,
632    pub(crate) anim_off: bool,
633    /// the windows are mirrored this frame (rtl.rs); rtl_mode 0 = from the UI language,
634    /// 1 = on, 2 = off
635    pub(crate) rtl: bool,
636    pub(crate) rtl_mode: u8,
637    /// lifecycle: the windows that have the keyboard (bit per window), UIC_ACTIVE's state
638    pub(crate) app_focus: u32,
639    pub(crate) app_active: bool,
640    /// the theme follows the system (theme_set(2))
641    pub(crate) theme_sys: bool,
642    /// system settings (theme.rs): the light / dark choice before high contrast, high
643    /// contrast (mode 0 system, 1 on, 2 off) and its colours, the accent follows the system,
644    /// --anim / --text-scale given, the text scale (%), double-click and caret blink times
645    pub(crate) theme_base_dark: bool,
646    pub(crate) contrast_mode: u8,
647    pub(crate) high_contrast: bool,
648    pub(crate) hc_sys: Option<plat::HcColors>,
649    pub(crate) accent_sys: bool,
650    pub(crate) anim_forced: bool,
651    /// the system's reduced motion as last applied (set_reduced_motion(None) returns to it)
652    pub(crate) anim_sys: bool,
653    pub(crate) text_scale_forced: bool,
654    pub(crate) text_scale: u32,
655    pub(crate) dbl_ms: u32,
656    /// the system's font rendering: smoothing, hinting, subpixel order (SysSettings)
657    pub(crate) font_prefs: (Option<bool>, Option<u8>, Option<u8>),
658    /// the system's title bar: buttons and click actions (SysSettings::titlebar, chrome.rs)
659    pub(crate) tb_sys: plat::Titlebar,
660    pub(crate) blink_ms: u32,
661    pub(crate) warns: u32,
662    pub(crate) ui_debug: bool,
663    /// scripts and jfgui_DEBUG: warn about an id two widgets of one pass share
664    pub(crate) dup_check: bool,
665    pub(crate) dup_warned: alloc::collections::BTreeSet<u32>,
666    /// widget ids from names: the scope stack (ids.rs)
667    pub(crate) ids: ids::Ids,
668    pub(crate) focus_req: u32,
669    pub(crate) focus_req_win: u32,
670    pub(crate) focus_req_old: u32,
671    pub(crate) focus_req_frame: u32,
672    /// window being built / live (g_uiw_cur of ui_win.s; 0 = the main window)
673    pub(crate) uiw_cur: u32,
674
675    // ---- theme (ui_theme.s)
676    pub(crate) theme: [u32; theme::THEME_N],
677    pub(crate) theme_dark: bool,
678    pub(crate) accent: u32,
679    /// the app's theme over the style and the styles to switch to (theme.rs)
680    pub(crate) look: theme::Look,
681
682    // ---- runtime (ui_main.s, shared part)
683    pub(crate) title: String,
684    pub(crate) t0_us: u64,
685    pub(crate) headless: bool,
686    pub(crate) need: bool,
687    pub(crate) need_render: bool,
688    pub(crate) quit: bool,
689    pub(crate) log_events: bool,
690    /// AF_SINGLE_INSTANCE: the requests open_take has not taken yet
691    pub(crate) open_reqs: Vec<plat::instance::Request>,
692    pub(crate) autoclose_ms: u32,
693    pub(crate) accent_raw: u32,
694    pub(crate) settle_warned: bool,
695    pub(crate) verify_dirty: bool,
696    pub(crate) frames_rendered: u32,
697    pub(crate) close_at: u32,
698
699    // ---- dirty regions (ui_dirty.s, shared part)
700    pub(crate) drects: Vec<[i32; 4]>,
701    pub(crate) dmg: Vec<dirty::Dmg>,
702    pub(crate) cvs: Vec<dirty::Cvs>,
703
704    // ---- commands (ui_cmd.s, shared part)
705    pub(crate) icon_hook: Option<IconHook>,
706
707    // ---- basic widgets (ui_basic.s)
708    pub(crate) uiinput: UiInput,
709
710    // ---- scripts (ui_auto.s), cursors (ui_cursor.s), drops and the headless image
711    // clipboard (ui_drop.s)
712    pub(crate) sc: auto::S,
713    pub(crate) ucur: cursor::S,
714    pub(crate) dr: drop::S,
715    /// drag and drop: the app's drags, the drag over a window, drops onto targets (dnd.rs)
716    pub(crate) dnd: dnd::S,
717    /// async tasks on the UI thread (tasks.rs: ui.spawn; run between passes)
718    pub(crate) tasks: plat::task::Executor,
719}
720
721// (SK6) the code editor's language services: completion, hover texts, signature help and its
722// requests (code_assist.rs), breakpoints, the execution line and folding (code_doc.rs, code_edit.rs)
723mod code_assist;
724pub use code_assist::{
725    CodeAssist, CodeEv, CompItem, CompList, SigHelp, CI_CONSTANT, CI_FIELD, CI_FUNCTION, CI_KEYWORD, CI_METHOD, CI_MODULE, CI_PARAM, CI_TEXT,
726    CI_VALUE, CI_VARIABLE, POP_ROWS,
727};
728pub use code_doc::{TextEdit, BP_COND, BP_DISABLED, BP_ON};
729pub use code_edit::{CE_BREAKPOINTS, CE_FOLDS};
730// (ST1) a card or list row that does one thing (action_card.rs)
731mod action_card;
732pub use action_card::CardDesc;