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;