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