refactor(ui-egui): move SheetApp methods and UI types out of lib.rs

This commit is contained in:
aiongg
2026-10-26 21:16:41 +00:00
parent 6b743e4622
commit e0211fbb4f
5 changed files with 1889 additions and 1877 deletions
File diff suppressed because it is too large Load Diff
+25
View File
@@ -0,0 +1,25 @@
/// Wall-clock milliseconds since the Unix epoch, for user-visible timestamps (Backstage's "last
/// changed", toasts). It can jump when the system clock is adjusted, so it must not drive
/// deadlines or pacing; use [`monotonic_ms`] for those.
pub fn now_ms() -> f64 {
gridcraft_engine::core::date::unix_now_ms().unwrap_or(0.0)
}
/// Monotonic milliseconds since the first call, for screenshot deadlines and AutoSave pacing.
///
/// Deliberately separate from [`now_ms`]: a wall-clock adjustment must not extend (or expire) a
/// screenshot deadline or save a workbook twice. Native uses [`std::time::Instant`]; wasm uses the
/// browser's `performance.now()`, which is monotonic too.
#[cfg(not(target_arch = "wasm32"))]
#[allow(clippy::disallowed_methods)] // the std clock is read only off wasm
pub fn monotonic_ms() -> f64 {
use std::sync::OnceLock;
static START: OnceLock<std::time::Instant> = OnceLock::new();
START.get_or_init(std::time::Instant::now).elapsed().as_secs_f64() * 1000.0
}
#[cfg(target_arch = "wasm32")]
/// Web monotonic clock: the browser's `performance.now()`, or 0 when it is unavailable.
pub fn monotonic_ms() -> f64 {
web_sys::window().and_then(|window| window.performance()).map_or(0.0, |performance| performance.now())
}
+9 -1877
View File
File diff suppressed because it is too large Load Diff
+61
View File
@@ -0,0 +1,61 @@
use super::*;
/// Host services the platform shell provides (file pickers, opening URLs).
#[derive(Default)]
pub struct Services {
/// Desktop: discover configured printers on a worker.
pub printers: Option<printing::Discover>,
/// Desktop: submit the PDF with the chosen settings on a worker.
pub print_native: Option<printing::Submit>,
pub pick_open: Option<Box<dyn Fn() -> Option<String>>>,
pub pick_save: Option<Box<dyn Fn(&str) -> Option<String>>>,
/// Native picture picker (PNG/JPEG), separate from workbook opening.
pub pick_picture: Option<Box<dyn Fn() -> Option<String>>>,
/// Browser picture picker. Each dialog owns its inbox, so canceled picks cannot
/// insert into a subsequent dialog or open the image as a workbook.
pub pick_picture_async: Option<Box<dyn Fn(Inbox)>>,
pub open_url: Option<Box<dyn Fn(&str)>>,
/// Publish HTML and its plain-text alternative together. `Ok` means the host owns the
/// write (including any asynchronous fallback); an immediate error uses egui's text path.
pub copy_html: Option<Box<dyn FnMut(&str, &str) -> Result<(), String>>>,
/// Read the HTML flavour of the system clipboard when the user pastes text, so cells the
/// source spreadsheet recorded as numbers stay numbers ("Rp10,000.00" → 10000). `None` from
/// the host pastes the plain text alone.
pub paste_html: Option<Box<dyn FnMut() -> Option<String>>>,
/// Read the system clipboard's plain text for the ribbon and menu Paste commands (Cmd/Ctrl+V
/// gets it from egui's `Event::Paste`). `None` from the host, or no service (web), pastes
/// GridCraft's own clipboard.
pub paste_text: Option<Box<dyn FnMut() -> Option<String>>>,
/// Web: deliver a file to the user (name, bytes).
pub download: Option<Box<dyn Fn(&str, &[u8])>>,
/// Web: start an asynchronous file pick; the bytes arrive later in `inbox`.
pub open_async: Option<Box<dyn Fn()>>,
/// Files delivered asynchronously (name, bytes), opened on the next frame.
pub inbox: Option<Inbox>,
/// Web: hand PDF bytes to the browser's print flow directly (no download,
/// no intermediate file) — opens the system print dialog on that PDF.
pub print: Option<Box<dyn Fn(&[u8])>>,
}
/// Shared queue of files read asynchronously (browser file picker, drag and drop).
pub type Inbox = std::sync::Arc<std::sync::Mutex<Vec<(String, Vec<u8>)>>>;
/// Which parts of the app's own window chrome are drawn. Everything is on by default; a host
/// that embeds the sheet in its own window (and owns files itself) can turn off the title bar
/// and the File tab, whose Backstage view then never opens. Not persisted: it describes the
/// host, not a user preference.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Chrome {
/// The title bar: AutoSave, Home, Save, Undo/Redo, the file name, Comments, Share, Search and
/// the theme switch.
pub title_bar: bool,
/// The ribbon's File tab and the Backstage view it opens (also reached by keyboard and
/// commands such as `ui.backstage`).
pub file_tab: bool,
}
impl Default for Chrome {
fn default() -> Self {
Chrome { title_bar: true, file_tab: true }
}
}
+40
View File
@@ -0,0 +1,40 @@
/// Per-sheet view state (scroll position in sheet points).
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SheetView {
pub scroll: egui::Vec2,
}
/// Severity of a notice shown in the info bar under the ribbon.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NoticeKind {
Info,
Warning,
}
/// An answer to the Save Changes question.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SaveAnswer {
Save,
DontSave,
Cancel,
}
/// One open window, as View › Switch Windows and the macOS Window menu list it.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct OpenWindow {
/// The window's id, the `window` param of `view.switchWindows`.
pub window: u64,
/// Its workbook's `DocState::uid`.
pub doc: u64,
pub title: String,
pub dirty: bool,
pub active: bool,
}
impl OpenWindow {
/// The title ("Book1", or "Book1:2" for a workbook's second window), with a bullet after it
/// while the workbook has unsaved changes.
pub fn label(&self) -> String {
if self.dirty { format!("{} •", self.title) } else { self.title.clone() }
}
}