diff --git a/Cargo.lock b/Cargo.lock index a9dbefd..b5552a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -333,20 +333,6 @@ name = "bytemuck" version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] [[package]] name = "byteorder" @@ -560,29 +546,6 @@ dependencies = [ "libm", ] -[[package]] -name = "cosmic-text" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" -dependencies = [ - "bitflags 2.8.0", - "fontdb 0.16.2", - "log", - "rangemap", - "rayon", - "rustc-hash", - "rustybuzz 0.14.1", - "self_cell", - "swash", - "sys-locale", - "ttf-parser 0.21.1", - "unicode-bidi", - "unicode-linebreak", - "unicode-script", - "unicode-segmentation", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -904,15 +867,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" -[[package]] -name = "font-types" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" -dependencies = [ - "bytemuck", -] - [[package]] name = "fontconfig-parser" version = "0.5.7" @@ -922,20 +876,6 @@ dependencies = [ "roxmltree", ] -[[package]] -name = "fontdb" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "slotmap", - "tinyvec", - "ttf-parser 0.20.0", -] - [[package]] name = "fontdb" version = "0.21.0" @@ -947,7 +887,7 @@ dependencies = [ "memmap2", "slotmap", "tinyvec", - "ttf-parser 0.24.1", + "ttf-parser", ] [[package]] @@ -1056,12 +996,6 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" -[[package]] -name = "grid" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36119f3a540b086b4e436bb2b588cf98a68863470e0e880f4d0842f112a3183a" - [[package]] name = "half" version = "2.4.1" @@ -1650,16 +1584,6 @@ dependencies = [ "imgref", ] -[[package]] -name = "mark" -version = "0.0.0" -source = "git+https://github.com/Garmelon/mark.git?rev=2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4#2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4" -dependencies = [ - "image", - "palette", - "rand 0.9.0", -] - [[package]] name = "matchit" version = "0.8.4" @@ -2055,7 +1979,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d15afa937836bf3d876f5a04ce28810c06045857bf46c3d0d31073b8aada5494" dependencies = [ - "ttf-parser 0.24.1", + "ttf-parser", ] [[package]] @@ -2279,12 +2203,6 @@ dependencies = [ "zerocopy 0.8.20", ] -[[package]] -name = "rangemap" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" - [[package]] name = "rav1e" version = "0.7.1" @@ -2355,16 +2273,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "read-fonts" -version = "0.22.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" -dependencies = [ - "bytemuck", - "font-types", -] - [[package]] name = "redox_syscall" version = "0.5.9" @@ -2496,12 +2404,6 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustix" version = "0.38.44" @@ -2521,23 +2423,6 @@ version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" -[[package]] -name = "rustybuzz" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" -dependencies = [ - "bitflags 2.8.0", - "bytemuck", - "libm", - "smallvec", - "ttf-parser 0.21.1", - "unicode-bidi-mirroring 0.2.0", - "unicode-ccc 0.2.0", - "unicode-properties", - "unicode-script", -] - [[package]] name = "rustybuzz" version = "0.18.0" @@ -2549,9 +2434,9 @@ dependencies = [ "core_maths", "log", "smallvec", - "ttf-parser 0.24.1", - "unicode-bidi-mirroring 0.3.0", - "unicode-ccc 0.3.0", + "ttf-parser", + "unicode-bidi-mirroring", + "unicode-ccc", "unicode-properties", "unicode-script", ] @@ -2609,12 +2494,6 @@ dependencies = [ "libc", ] -[[package]] -name = "self_cell" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2fdfc24bc566f839a2da4c4295b82db7d25a24253867d5c64355abb5799bdbe" - [[package]] name = "serde" version = "1.0.218" @@ -2712,23 +2591,6 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" name = "showbits-assets" version = "0.0.0" -[[package]] -name = "showbits-common" -version = "0.0.0" -dependencies = [ - "anyhow", - "cosmic-text", - "image", - "mark", - "palette", - "paste", - "showbits-assets", - "taffy", - "typst", - "typst-assets", - "typst-render", -] - [[package]] name = "showbits-thermal-printer" version = "0.0.0" @@ -2803,16 +2665,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" -[[package]] -name = "skrifa" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" -dependencies = [ - "bytemuck", - "read-fonts", -] - [[package]] name = "slotmap" version = "1.0.7" @@ -2920,17 +2772,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "swash" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" -dependencies = [ - "skrifa", - "yazi", - "zeno", -] - [[package]] name = "syn" version = "2.0.98" @@ -2981,15 +2822,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "sys-locale" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" -dependencies = [ - "libc", -] - [[package]] name = "system-deps" version = "6.2.2" @@ -3003,18 +2835,6 @@ dependencies = [ "version-compare", ] -[[package]] -name = "taffy" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e3102cd96acfdb0b3f037cb06e9b40d3dca1d7c6e4742147441daf6f34e8a6" -dependencies = [ - "arrayvec", - "grid", - "serde", - "slotmap", -] - [[package]] name = "tar" version = "0.4.44" @@ -3297,18 +3117,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "ttf-parser" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" - -[[package]] -name = "ttf-parser" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" - [[package]] name = "ttf-parser" version = "0.24.1" @@ -3412,7 +3220,7 @@ dependencies = [ "ecow", "env_proxy", "flate2", - "fontdb 0.21.0", + "fontdb", "native-tls", "once_cell", "openssl", @@ -3443,9 +3251,9 @@ dependencies = [ "icu_provider_blob", "icu_segmenter", "kurbo", - "rustybuzz 0.18.0", + "rustybuzz", "smallvec", - "ttf-parser 0.24.1", + "ttf-parser", "typst-assets", "typst-library", "typst-macros", @@ -3474,7 +3282,7 @@ dependencies = [ "csv", "ecow", "flate2", - "fontdb 0.21.0", + "fontdb", "hayagriva", "icu_properties", "icu_provider", @@ -3494,7 +3302,7 @@ dependencies = [ "regex-syntax", "roxmltree", "rust_decimal", - "rustybuzz 0.18.0", + "rustybuzz", "serde", "serde_json", "serde_yaml", @@ -3503,7 +3311,7 @@ dependencies = [ "syntect", "time", "toml", - "ttf-parser 0.24.1", + "ttf-parser", "two-face", "typed-arena", "typst-assets", @@ -3561,7 +3369,7 @@ dependencies = [ "pixglyph", "resvg", "tiny-skia", - "ttf-parser 0.24.1", + "ttf-parser", "typst-library", "typst-macros", "typst-timing", @@ -3578,7 +3386,7 @@ dependencies = [ "ecow", "flate2", "image", - "ttf-parser 0.24.1", + "ttf-parser", "typst-library", "typst-macros", "typst-timing", @@ -3661,24 +3469,12 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" - [[package]] name = "unicode-bidi-mirroring" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f" -[[package]] -name = "unicode-ccc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" - [[package]] name = "unicode-ccc" version = "0.3.0" @@ -3691,12 +3487,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" -[[package]] -name = "unicode-linebreak" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" - [[package]] name = "unicode-math-class" version = "0.1.0" @@ -3785,13 +3575,13 @@ dependencies = [ "base64", "data-url", "flate2", - "fontdb 0.21.0", + "fontdb", "imagesize", "kurbo", "log", "pico-args", "roxmltree", - "rustybuzz 0.18.0", + "rustybuzz", "simplecss", "siphasher", "strict-num", @@ -4146,12 +3936,6 @@ dependencies = [ "linked-hash-map", ] -[[package]] -name = "yazi" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" - [[package]] name = "yoke" version = "0.7.5" @@ -4176,12 +3960,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zeno" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" - [[package]] name = "zerocopy" version = "0.7.35" diff --git a/Cargo.toml b/Cargo.toml index 8b1a574..05c32d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,6 @@ [workspace] resolver = "3" -members = [ - "showbits-assets", - "showbits-common", - "showbits-thermal-printer", - "showbits-typst", -] +members = ["showbits-assets", "showbits-thermal-printer", "showbits-typst"] [workspace.package] version = "0.0.0" @@ -15,18 +10,15 @@ edition = "2024" anyhow = "1.0.96" axum = "0.8.1" clap = { version = "4.5.30", features = ["derive", "deprecated"] } -cosmic-text = "0.12.1" escpos = "0.15.0" -image = { version = "0.25.5", default-features = false } +image = "0.25.5" jiff = "0.2.1" mime_guess = "2.0.5" palette = "0.7.6" -paste = "1.0.15" rand = "0.9.0" rust-embed = "8.5.0" serde = { version = "1.0.218", features = ["derive"] } showbits-assets.path = "./showbits-assets" -showbits-common.path = "./showbits-common" showbits-typst.path = "./showbits-typst" tokio = "1.43.0" typst = "0.13.0" @@ -34,15 +26,6 @@ typst-assets = { version = "0.13.0", features = ["fonts"] } typst-kit = "0.13.0" typst-render = "0.13.0" -[workspace.dependencies.taffy] -version = "0.7.6" -default-features = false -features = ["std", "taffy_tree", "flexbox", "grid", "block_layout"] - -[workspace.dependencies.mark] -git = "https://github.com/Garmelon/mark.git" -rev = "2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4" - [workspace.lints] rust.unsafe_code = { level = "forbid", priority = 1 } # Lint groups diff --git a/showbits-common/Cargo.toml b/showbits-common/Cargo.toml deleted file mode 100644 index 59865ff..0000000 --- a/showbits-common/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "showbits-common" -version = { workspace = true } -edition = { workspace = true } - -[dependencies] -anyhow = { workspace = true } -cosmic-text = { workspace = true } -image = { workspace = true } -mark = { workspace = true } -palette = { workspace = true } -paste = { workspace = true } -showbits-assets = { workspace = true } -taffy = { workspace = true } -typst = { workspace = true } -typst-assets = { workspace = true } -typst-render = { workspace = true } - -[lints] -workspace = true diff --git a/showbits-common/src/color.rs b/showbits-common/src/color.rs deleted file mode 100644 index 3774ad0..0000000 --- a/showbits-common/src/color.rs +++ /dev/null @@ -1,25 +0,0 @@ -use palette::Srgba; - -pub const TRANSPARENT: Srgba = Srgba::new(0.0, 0.0, 0.0, 0.0); -pub const BLACK: Srgba = Srgba::new(0.0, 0.0, 0.0, 1.0); -pub const WHITE: Srgba = Srgba::new(1.0, 1.0, 1.0, 1.0); - -pub fn from_image_color(color: image::Rgba) -> Srgba { - let [r, g, b, a] = color.0; - Srgba::new(r, g, b, a).into_format() -} - -pub fn to_image_color(color: Srgba) -> image::Rgba { - let color = color.into_format::(); - image::Rgba([color.red, color.green, color.blue, color.alpha]) -} - -pub fn from_text_color(color: cosmic_text::Color) -> Srgba { - let [r, g, b, a] = color.as_rgba(); - Srgba::new(r, g, b, a).into_format() -} - -pub fn to_text_color(color: Srgba) -> cosmic_text::Color { - let color = color.into_format::(); - cosmic_text::Color::rgba(color.red, color.green, color.blue, color.alpha) -} diff --git a/showbits-common/src/lib.rs b/showbits-common/src/lib.rs deleted file mode 100644 index 05cb266..0000000 --- a/showbits-common/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub use crate::{node::*, rect::*, tree::*, vec2::*, view::*, widget::*}; - -pub mod color; -mod node; -mod rect; -mod tree; -mod vec2; -mod view; -mod widget; -pub mod widgets; diff --git a/showbits-common/src/node.rs b/showbits-common/src/node.rs deleted file mode 100644 index 7170138..0000000 --- a/showbits-common/src/node.rs +++ /dev/null @@ -1,221 +0,0 @@ -use paste::paste; -use taffy::{ - AlignContent, AlignItems, AlignSelf, Dimension, Display, FlexDirection, FlexWrap, GridAutoFlow, - GridPlacement, JustifyContent, LengthPercentage, LengthPercentageAuto, Line, NodeId, - NonRepeatedTrackSizingFunction, Overflow, Point, Position, Rect, Size, Style, TaffyResult, - TrackSizingFunction, -}; - -use crate::{BoxedWidget, Tree, Widget}; - -pub struct Node { - layout: Style, - children: Vec, - widget: Option>, -} - -impl Node { - pub fn empty() -> Self { - Self { - layout: Style::default(), - children: vec![], - widget: None, - } - } - - pub fn and_child(mut self, node: NodeId) -> Self { - self.children.push(node); - self - } - - pub fn with_widget + 'static>(mut self, widget: W) -> Self { - self.widget = Some(Box::new(widget)); - self - } - - pub fn register(self, tree: &mut Tree) -> TaffyResult { - let tree = tree.taffy_tree(); - let id = tree.new_with_children(self.layout, &self.children)?; - tree.set_node_context(id, self.widget)?; - Ok(id) - } -} - -// Layout helper functions - -macro_rules! layout_setter { - ( $name:ident : $type:ty ) => { - paste! { - pub fn [](mut self, $name: $type) -> Self { - self.layout.$name = $name; - self - } - } - }; -} - -macro_rules! layout_setter_point { - ( $name:ident : Point<$type:ty> ) => { - paste! { - pub fn [](mut self, $name: Point<$type>) -> Self { - self.layout.$name = $name; - self - } - - pub fn [](mut self, x: $type) -> Self { - self.layout.$name.x = x; - self - } - - pub fn [](mut self, y: $type) -> Self { - self.layout.$name.y = y; - self - } - - pub fn [](mut self, all: $type) -> Self { - self.layout.$name.x = all; - self.layout.$name.y = all; - self - } - } - }; -} - -macro_rules! layout_setter_size { - ( $name:ident : Size<$type:ty> ) => { - paste! { - pub fn [](mut self, $name: Size<$type>) -> Self { - self.layout.$name = $name; - self - } - - pub fn [](mut self, width: $type) -> Self { - self.layout.$name.width = width; - self - } - - pub fn [](mut self, height: $type) -> Self { - self.layout.$name.height = height; - self - } - - pub fn [](mut self, all: $type) -> Self { - self.layout.$name.width = all; - self.layout.$name.height = all; - self - } - } - }; -} - -macro_rules! layout_setter_line { - ( $name:ident : Line<$type:ty> ) => { - paste! { - pub fn [](mut self, $name: Line<$type>) -> Self { - self.layout.$name = $name; - self - } - - pub fn [](mut self, start: $type) -> Self { - self.layout.$name.start = start; - self - } - - pub fn [](mut self, end: $type) -> Self { - self.layout.$name.end = end; - self - } - - pub fn [](mut self, all: $type) -> Self { - self.layout.$name.start = all; - self.layout.$name.end = all; - self - } - } - }; -} - -macro_rules! layout_setter_rect { - ( $name:ident : Rect<$type:ty> ) => { - paste! { - pub fn [](mut self, $name: Rect<$type>) -> Self { - self.layout.$name = $name; - self - } - - pub fn [](mut self, left: $type) -> Self { - self.layout.$name.left = left; - self - } - - pub fn [](mut self, right: $type) -> Self { - self.layout.$name.right = right; - self - } - - pub fn [](mut self, top: $type) -> Self { - self.layout.$name.top = top; - self - } - - pub fn [](mut self, bottom: $type) -> Self { - self.layout.$name.bottom = bottom; - self - } - - pub fn [](mut self, horizontal: $type) -> Self { - self.layout.$name.left = horizontal; - self.layout.$name.right = horizontal; - self - } - - pub fn [](mut self, vertical: $type) -> Self { - self.layout.$name.top = vertical; - self.layout.$name.bottom = vertical; - self - } - - pub fn [](mut self, all: $type) -> Self { - self.layout.$name.left = all; - self.layout.$name.right = all; - self.layout.$name.top = all; - self.layout.$name.bottom = all; - self - } - } - }; -} - -impl Node { - layout_setter!(display: Display); - layout_setter_point!(overflow: Point); - layout_setter!(scrollbar_width: f32); - layout_setter!(position: Position); - layout_setter_rect!(inset: Rect); - layout_setter_size!(size: Size); - layout_setter_size!(min_size: Size); - layout_setter_size!(max_size: Size); - layout_setter!(aspect_ratio: Option); - layout_setter_rect!(margin: Rect); - layout_setter_rect!(padding: Rect); - layout_setter_rect!(border: Rect); - layout_setter!(align_items: Option); - layout_setter!(align_self: Option); - layout_setter!(justify_items: Option); - layout_setter!(justify_self: Option); - layout_setter!(align_content: Option); - layout_setter!(justify_content: Option); - layout_setter_size!(gap: Size); - layout_setter!(flex_direction: FlexDirection); - layout_setter!(flex_wrap: FlexWrap); - layout_setter!(flex_basis: Dimension); - layout_setter!(flex_grow: f32); - layout_setter!(flex_shrink: f32); - layout_setter!(grid_template_rows: Vec); - layout_setter!(grid_template_columns: Vec); - layout_setter!(grid_auto_rows: Vec); - layout_setter!(grid_auto_columns: Vec); - layout_setter!(grid_auto_flow: GridAutoFlow); - layout_setter_line!(grid_row: Line); - layout_setter_line!(grid_column: Line); -} diff --git a/showbits-common/src/rect.rs b/showbits-common/src/rect.rs deleted file mode 100644 index e8e1e2e..0000000 --- a/showbits-common/src/rect.rs +++ /dev/null @@ -1,143 +0,0 @@ -use std::ops::{Add, Sub}; - -use crate::Vec2; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Rect { - north: i32, - south: i32, - west: i32, - east: i32, -} - -impl Rect { - /// A zero-sized rectangle located at the origin. - pub const ZERO: Self = Self::new(0, 0, -1, -1); - - /// Whenever a rectangle is constructed, this function must be used. This - /// ensures invariants are always checked. - const fn new(north: i32, south: i32, west: i32, east: i32) -> Self { - let result = Self { - north, - south, - west, - east, - }; - - let size = result.size(); - assert!(size.x >= 0); - assert!(size.y >= 0); - - result - } - - /// Construct a rectangle that is a bounding box around two points. - /// - /// It is not possible to construct a rectangle with a width or height of 0 - /// through this method. Use one of the other constructor functions instead. - pub fn from_points(a: Vec2, b: Vec2) -> Self { - Self::new(a.y.min(b.y), a.y.max(b.y), a.x.min(b.x), a.x.max(b.x)) - } - - /// Construct a rectangle from its north-west and south-east corners. - pub const fn from_nw_se(nw: Vec2, se: Vec2) -> Self { - Self::new(nw.y, se.y, nw.x, se.x) - } - - /// Construct a rectangle from its north-east and south-west corners. - pub const fn from_ne_sw(ne: Vec2, sw: Vec2) -> Self { - Self::new(ne.y, sw.y, sw.x, ne.x) - } - - /// Construct a rectangle from its north-west corner and size. - pub fn from_nw(nw: Vec2, size: Vec2) -> Self { - let se = nw + (size - 1); - Self::from_nw_se(nw, se) - } - - /// Construct a rectangle from its north-east corner and size. - pub fn from_ne(ne: Vec2, size: Vec2) -> Self { - let sw = ne + (size - 1).neg_x(); - Self::from_ne_sw(ne, sw) - } - - /// Construct a rectangle from its south-west corner and size. - pub fn from_sw(sw: Vec2, size: Vec2) -> Self { - let ne = sw + (size - 1).neg_y(); - Self::from_ne_sw(ne, sw) - } - - /// Construct a rectangle from its south-east corner and size. - pub fn from_se(se: Vec2, size: Vec2) -> Self { - let nw = se - (size - 1); - Self::from_nw_se(nw, se) - } - - pub const fn north(self) -> i32 { - self.north - } - - pub const fn south(self) -> i32 { - self.south - } - - pub const fn west(self) -> i32 { - self.west - } - - pub const fn east(self) -> i32 { - self.east - } - - pub const fn corner_nw(self) -> Vec2 { - Vec2::new(self.west, self.north) - } - - pub const fn corner_ne(self) -> Vec2 { - Vec2::new(self.east, self.north) - } - - pub const fn corner_sw(self) -> Vec2 { - Vec2::new(self.west, self.south) - } - - pub const fn corner_se(self) -> Vec2 { - Vec2::new(self.east, self.south) - } - - pub const fn size(self) -> Vec2 { - Vec2::new(self.east - self.west + 1, self.south - self.north + 1) - } - - /// An iterator of all points contained within the rectangle. - pub fn points(self) -> impl Iterator { - (self.north..=self.south) - .flat_map(move |y| (self.west..=self.east).map(move |x| Vec2::new(x, y))) - } -} - -impl Add for Rect { - type Output = Self; - - fn add(self, rhs: Vec2) -> Self::Output { - Self::new( - self.north + rhs.y, - self.south + rhs.y, - self.west + rhs.x, - self.east + rhs.x, - ) - } -} - -impl Sub for Rect { - type Output = Self; - - fn sub(self, rhs: Vec2) -> Self::Output { - Self::new( - self.north - rhs.y, - self.south - rhs.y, - self.west - rhs.x, - self.east - rhs.x, - ) - } -} diff --git a/showbits-common/src/tree.rs b/showbits-common/src/tree.rs deleted file mode 100644 index 830dc76..0000000 --- a/showbits-common/src/tree.rs +++ /dev/null @@ -1,110 +0,0 @@ -use image::RgbaImage; -use palette::Srgba; -use taffy::{AvailableSpace, NodeId, Point, Size, TaffyResult, TaffyTree}; - -use crate::{BoxedWidget, Rect, Vec2, View, color}; - -fn point_to_vec2(point: Point) -> Vec2 { - Vec2::new(point.x as i32, point.y as i32) -} - -fn size_to_vec2(size: Size) -> Vec2 { - Vec2::new(size.width as i32, size.height as i32) -} - -pub struct Tree { - tree: TaffyTree>, - background: Srgba, -} - -impl Tree { - pub fn new(background: Srgba) -> Self { - Self { - tree: TaffyTree::new(), - background, - } - } - - pub(crate) fn taffy_tree(&mut self) -> &mut TaffyTree> { - &mut self.tree - } - - fn layout( - &mut self, - ctx: &mut C, - root: NodeId, - available: Size, - ) -> TaffyResult<()> { - self.tree.enable_rounding(); // Just to make sure - self.tree.compute_layout_with_measure( - root, - available, - |known, available, _node, context, _style| { - if let Some(widget) = context { - widget.size(ctx, known, available) - } else { - Size::ZERO - } - }, - ) - } - - fn render_to_view( - &mut self, - ctx: &mut C, - node: NodeId, - view: &mut View<'_>, - ) -> anyhow::Result<()> { - let layout = *self.tree.layout(node)?; - let area = Rect::from_nw(point_to_vec2(layout.location), size_to_vec2(layout.size)); - let mut view = view.dup().zoom(area); - - // First pass - if let Some(widget) = self.tree.get_node_context_mut(node) { - widget.draw_below(ctx, &mut view, &layout)?; - } - - // Render children - let mut children = vec![]; - for child in self.tree.children(node)? { - let order = self.tree.layout(child)?.order; - children.push((order, child)); - } - children.sort_unstable_by_key(|(order, _)| *order); - for (_, child) in children { - self.render_to_view(ctx, child, &mut view)?; - } - - // Second pass - if let Some(widget) = self.tree.get_node_context_mut(node) { - widget.draw_above(ctx, &mut view, &layout)?; - } - - Ok(()) - } - - pub fn render( - &mut self, - ctx: &mut C, - root: NodeId, - available: Size, - ) -> anyhow::Result { - self.layout(ctx, root, available)?; - - let layout = self.tree.layout(root)?; - assert_eq!(layout.location.x, 0.0); - assert_eq!(layout.location.y, 0.0); - // TODO Check how taffy treats the border? - - let (width, height) = size_to_vec2(layout.size).to_u32(); - let mut image = - RgbaImage::from_pixel(width, height, color::to_image_color(self.background)); - self.render_to_view(ctx, root, &mut View::new(&mut image))?; - - Ok(image) - } - - pub fn print_debug(&mut self, root: NodeId) { - self.tree.print_tree(root) - } -} diff --git a/showbits-common/src/vec2.rs b/showbits-common/src/vec2.rs deleted file mode 100644 index b3e160d..0000000 --- a/showbits-common/src/vec2.rs +++ /dev/null @@ -1,187 +0,0 @@ -use std::{ - fmt, - ops::{Add, Mul, Neg, Sub}, -}; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub struct Vec2 { - pub x: i32, - pub y: i32, -} - -impl Vec2 { - pub const ZERO: Self = Self::new(0, 0); - pub const NORTH: Self = Self::new(0, -1); - pub const SOUTH: Self = Self::new(0, 1); - pub const WEST: Self = Self::new(-1, 0); - pub const EAST: Self = Self::new(1, 0); - - pub const fn new(x: i32, y: i32) -> Self { - Self { x, y } - } - - pub fn from_u32_checked(x: u32, y: u32) -> Option { - let x: i32 = x.try_into().ok()?; - let y: i32 = y.try_into().ok()?; - Some(Self::new(x, y)) - } - - pub fn from_u32(x: u32, y: u32) -> Self { - let x: i32 = x.try_into().expect("x too large"); - let y: i32 = y.try_into().expect("y too large"); - Self::new(x, y) - } - - pub fn to_u32_checked(self) -> Option<(u32, u32)> { - let x: u32 = self.x.try_into().ok()?; - let y: u32 = self.y.try_into().ok()?; - Some((x, y)) - } - - pub fn to_u32(self) -> (u32, u32) { - let x: u32 = self.x.try_into().expect("x too small"); - let y: u32 = self.y.try_into().expect("y too small"); - (x, y) - } - - pub fn with_x(self, x: i32) -> Self { - Self { x, ..self } - } - - pub fn with_y(self, y: i32) -> Self { - Self { y, ..self } - } - - /// The vector pointing from `self` to `other`. - /// - /// ``` - /// # use showbits_common::Vec2; - /// let a = Vec2::new(1, 3); - /// let b = Vec2::new(3, 7); - /// assert_eq!(a.to(b), b - a); - /// ``` - pub fn to(self, other: Self) -> Self { - other - self - } - - /// Negate the `x` component of the vector. - /// - /// ``` - /// # use showbits_common::Vec2; - /// let v = Vec2::new(3, 4); - /// assert_eq!(v.neg_x(), v * Vec2::new(-1, 1)); - /// ``` - pub fn neg_x(self) -> Self { - Self { x: -self.x, ..self } - } - - /// Negate the `y` component of the vector. - /// - /// ``` - /// # use showbits_common::Vec2; - /// let v = Vec2::new(3, 4); - /// assert_eq!(v.neg_y(), v * Vec2::new(1, -1)); - /// ``` - pub fn neg_y(self) -> Self { - Self { y: -self.y, ..self } - } -} - -impl fmt::Debug for Vec2 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Vec2").field(&self.x).field(&self.y).finish() - } -} - -impl Neg for Vec2 { - type Output = Self; - - fn neg(self) -> Self::Output { - Self { - x: -self.x, - y: -self.y, - } - } -} - -impl Add for Vec2 { - type Output = Self; - - fn add(self, rhs: Self) -> Self::Output { - Self { - x: self.x + rhs.x, - y: self.y + rhs.y, - } - } -} - -impl Add for Vec2 { - type Output = Self; - - fn add(self, rhs: i32) -> Self::Output { - self + Self::new(rhs, rhs) - } -} - -impl Sub for Vec2 { - type Output = Self; - - fn sub(self, rhs: Self) -> Self::Output { - Self { - x: self.x - rhs.x, - y: self.y - rhs.y, - } - } -} - -impl Sub for Vec2 { - type Output = Self; - - fn sub(self, rhs: i32) -> Self::Output { - self - Self::new(rhs, rhs) - } -} - -impl Mul for Vec2 { - type Output = Self; - - fn mul(self, rhs: Self) -> Self::Output { - Self { - x: self.x * rhs.x, - y: self.y * rhs.y, - } - } -} - -impl Mul for Vec2 { - type Output = Self; - - fn mul(self, rhs: i32) -> Self::Output { - self * Self::new(rhs, rhs) - } -} - -#[cfg(test)] -mod tests { - use crate::Vec2; - - #[test] - fn arithmetic() { - let a = Vec2::new(1, 3); - let b = Vec2::new(3, 7); - - assert_eq!(-a, Vec2::new(-1, -3)); - assert_eq!(a.neg_x(), Vec2::new(-1, 3)); - assert_eq!(a.neg_y(), Vec2::new(1, -3)); - - assert_eq!(a + b, Vec2::new(4, 10)); - assert_eq!(a + 2, Vec2::new(3, 5)); - - assert_eq!(a - b, Vec2::new(-2, -4)); - assert_eq!(a - 2, Vec2::new(-1, 1)); - assert_eq!(a - b, b.to(a)); - - assert_eq!(a * b, Vec2::new(3, 21)); - assert_eq!(a * 2, Vec2::new(2, 6)); - } -} diff --git a/showbits-common/src/view.rs b/showbits-common/src/view.rs deleted file mode 100644 index 077d913..0000000 --- a/showbits-common/src/view.rs +++ /dev/null @@ -1,89 +0,0 @@ -use image::RgbaImage; -use palette::{Srgba, blend::Compose}; - -use crate::{Rect, Vec2, color}; - -pub struct View<'a> { - area: Rect, - buffer: &'a mut RgbaImage, -} - -impl<'a> View<'a> { - pub fn new(buffer: &'a mut RgbaImage) -> Self { - let size = Vec2::from_u32(buffer.width(), buffer.height()); - let area = Rect::from_nw(Vec2::ZERO, size); - Self { area, buffer } - } - - pub fn dup(&mut self) -> View<'_> { - View { - area: self.area, - buffer: self.buffer, - } - } - - pub fn zoom(mut self, area: Rect) -> Self { - self.area = area + self.area.corner_nw(); - self - } - - pub fn size(&self) -> Vec2 { - self.area.size() - } - - pub fn area(&self) -> Rect { - Rect::from_nw(Vec2::ZERO, self.size()) - } - - fn pos_to_buffer_pos(&self, pos: Vec2) -> Vec2 { - pos + self.area.corner_nw() - } - - pub fn get(&self, pos: Vec2) -> Option { - let (x, y) = self.pos_to_buffer_pos(pos).to_u32_checked()?; - let pixel = self.buffer.get_pixel_checked(x, y)?; - Some(color::from_image_color(*pixel)) - } - - pub fn set(&mut self, pos: Vec2, color: Srgba) { - let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else { - return; - }; - - let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else { - return; - }; - - let below = color::from_image_color(*pixel); - *pixel = color::to_image_color(color.atop(below)); - } - - pub fn replace(&mut self, pos: Vec2, color: Srgba) { - let Some((x, y)) = self.pos_to_buffer_pos(pos).to_u32_checked() else { - return; - }; - - let Some(pixel) = self.buffer.get_pixel_mut_checked(x, y) else { - return; - }; - - *pixel = color::to_image_color(color); - } - - // More complicated drawing primitives - - pub fn rect(&mut self, area: Rect, color: Srgba) { - for pos in area.points() { - self.set(pos, color); - } - } - - pub fn image(&mut self, image: &RgbaImage) { - for y in 0..image.height() { - for x in 0..image.width() { - let color = color::from_image_color(*image.get_pixel(x, y)); - self.set(Vec2::from_u32(x, y), color); - } - } - } -} diff --git a/showbits-common/src/widget.rs b/showbits-common/src/widget.rs deleted file mode 100644 index c8988e4..0000000 --- a/showbits-common/src/widget.rs +++ /dev/null @@ -1,53 +0,0 @@ -use taffy::{AvailableSpace, Layout, Size}; - -use crate::{Node, View}; - -pub trait Widget { - /// Used for the measure function in - /// [`taffy::TaffyTree::compute_layout_with_measure`]. - #[allow(unused_variables)] - fn size( - &mut self, - ctx: &mut C, - known: Size>, - available: Size, - ) -> Size { - Size::ZERO - } - - /// Called before all children are drawn. - /// - /// Prefer this over [`Self::draw_above`] when implementing a leaf widget. - #[allow(unused_variables)] - fn draw_below( - &mut self, - ctx: &mut C, - view: &mut View<'_>, - layout: &Layout, - ) -> anyhow::Result<()> { - Ok(()) - } - - /// Called after all children are drawn. - #[allow(unused_variables)] - fn draw_above( - &mut self, - ctx: &mut C, - view: &mut View<'_>, - layout: &Layout, - ) -> anyhow::Result<()> { - Ok(()) - } -} - -pub type BoxedWidget = Box>; - -pub trait WidgetExt { - fn node(self) -> Node; -} - -impl + 'static> WidgetExt for W { - fn node(self) -> Node { - Node::empty().with_widget(self) - } -} diff --git a/showbits-common/src/widgets.rs b/showbits-common/src/widgets.rs deleted file mode 100644 index 921d3ec..0000000 --- a/showbits-common/src/widgets.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub use block::*; -pub use image::*; -pub use text::*; -pub use typst::*; - -mod block; -mod image; -mod text; -mod typst; diff --git a/showbits-common/src/widgets/block.rs b/showbits-common/src/widgets/block.rs deleted file mode 100644 index 25c0c34..0000000 --- a/showbits-common/src/widgets/block.rs +++ /dev/null @@ -1,77 +0,0 @@ -use palette::Srgba; -use taffy::Layout; - -use crate::{Rect, Vec2, View, Widget, color}; - -pub struct Block { - border: Srgba, - background: Srgba, -} - -impl Block { - pub fn new() -> Self { - Self { - border: color::TRANSPARENT, - background: color::TRANSPARENT, - } - } - - pub fn with_border(mut self, color: Srgba) -> Self { - self.border = color; - self - } - - pub fn with_background(mut self, color: Srgba) -> Self { - self.background = color; - self - } -} - -impl Default for Block { - fn default() -> Self { - Self::new() - } -} - -impl Widget for Block { - fn draw_below( - &mut self, - _ctx: &mut C, - view: &mut View<'_>, - layout: &Layout, - ) -> anyhow::Result<()> { - let area = view.area(); - - // Background - view.rect(area, self.background); - - // And now... the border! - // - // It is important not to draw pixels twice in case the border color is - // transparent. That's why the logic below is a bit more complex. - let left = layout.border.left as i32; - let right = layout.border.right as i32; - let top = layout.border.top as i32; - let bottom = layout.border.bottom as i32; - if top > 0 { - let border = Rect::from_nw(area.corner_nw(), area.size().with_y(top)); - view.rect(border, self.border); - } - if bottom > 0 { - let border = Rect::from_sw(area.corner_sw(), area.size().with_y(bottom)); - view.rect(border, self.border); - } - if left > 0 { - let nw = area.corner_nw() + Vec2::new(0, top); - let size = Vec2::new(left, area.size().y - top - bottom); - view.rect(Rect::from_nw(nw, size), self.border); - } - if right > 0 { - let ne = area.corner_ne() + Vec2::new(0, top); - let size = Vec2::new(right, area.size().y - top - bottom); - view.rect(Rect::from_ne(ne, size), self.border); - } - - Ok(()) - } -} diff --git a/showbits-common/src/widgets/image.rs b/showbits-common/src/widgets/image.rs deleted file mode 100644 index b1cfd36..0000000 --- a/showbits-common/src/widgets/image.rs +++ /dev/null @@ -1,161 +0,0 @@ -use image::{ - RgbaImage, - imageops::{self, FilterType}, -}; -use mark::dither::{AlgoFloydSteinberg, AlgoStucki, Algorithm, DiffEuclid, Palette}; -use palette::{IntoColor, LinSrgb, Srgba}; -use taffy::prelude::{AvailableSpace, Layout, Size}; - -use crate::Widget; - -#[derive(Clone, Copy, PartialEq, Eq)] -pub enum DitherAlgorithm { - FloydSteinberg, - Stucki, -} - -impl DitherAlgorithm { - fn dither(self, image: RgbaImage, palette: &Palette) -> RgbaImage { - match self { - Self::FloydSteinberg => { - >::run(image, palette) - } - Self::Stucki => >::run(image, palette), - } - } -} - -pub struct Image { - image: RgbaImage, - shrink: bool, - grow: bool, - scale: u32, - filter: FilterType, - - dither_palette: Option>, - dither_algorithm: DitherAlgorithm, -} - -impl Image { - pub fn new(image: RgbaImage) -> Self { - Self { - image, - shrink: true, - grow: false, - filter: FilterType::CatmullRom, - scale: 1, - dither_palette: None, - dither_algorithm: DitherAlgorithm::FloydSteinberg, - } - } - - pub fn with_shrink(mut self, shrink: bool) -> Self { - self.shrink = shrink; - self - } - - pub fn with_grow(mut self, grow: bool) -> Self { - self.grow = grow; - self - } - - pub fn with_scale(mut self, scale: u32) -> Self { - self.scale = scale.max(1); - self - } - - pub fn with_filter(mut self, filter: FilterType) -> Self { - self.filter = filter; - self - } - - pub fn with_dither_palette(mut self, palette: &[Srgba]) -> Self { - let palette = palette - .iter() - .map(|c| c.color.into_color()) - .collect::>(); - - self.dither_palette = Some(Palette::new(palette)); - self - } - - pub fn with_dither_algorithm(mut self, algorithm: DitherAlgorithm) -> Self { - self.dither_algorithm = algorithm; - self - } -} - -impl Widget for Image { - fn size( - &mut self, - _ctx: &mut C, - known: Size>, - available: Size, - ) -> Size { - if self.image.width() == 0 || self.image.height() == 0 { - // We don't want to divide by zero later on - return Size { - width: 0.0, - height: 0.0, - }; - } - - let size = Size { - width: (self.image.width() * self.scale) as f32, - height: (self.image.height() * self.scale) as f32, - }; - - let max_width = known.width.or(match available.width { - AvailableSpace::Definite(width) => Some(width), - AvailableSpace::MinContent => Some(0.0), - AvailableSpace::MaxContent => None, - }); - - let max_height = known.height.or(match available.height { - AvailableSpace::Definite(height) => Some(height), - AvailableSpace::MinContent => Some(0.0), - AvailableSpace::MaxContent => None, - }); - - let scale_factor = match (max_width, max_height) { - (None, None) => 1.0, - (None, Some(height)) => height / size.height, - (Some(width), None) => width / size.width, - (Some(width), Some(height)) => (width / size.width).min(height / size.height), - }; - - if (scale_factor < 1.0 && self.shrink) || (scale_factor > 1.0 && self.grow) { - Size { - width: size.width * scale_factor, - height: size.height * scale_factor, - } - } else { - size - } - } - - fn draw_below( - &mut self, - _ctx: &mut C, - view: &mut crate::View<'_>, - _layout: &Layout, - ) -> anyhow::Result<()> { - let (width, height) = view.size().to_u32(); - - let iwidth = width / self.scale; - let iheight = height / self.scale; - - let image = imageops::resize(&self.image, iwidth, iheight, self.filter); - - let image = if let Some(palette) = &self.dither_palette { - self.dither_algorithm.dither(image, palette) - } else { - image - }; - - let image = imageops::resize(&image, width, height, FilterType::Nearest); - - view.image(&image); - Ok(()) - } -} diff --git a/showbits-common/src/widgets/text.rs b/showbits-common/src/widgets/text.rs deleted file mode 100644 index 76b7f5f..0000000 --- a/showbits-common/src/widgets/text.rs +++ /dev/null @@ -1,210 +0,0 @@ -use cosmic_text::{Attrs, AttrsOwned, Buffer, Family, FontSystem, Metrics, Shaping, SwashCache}; -use palette::Srgba; -use showbits_assets::{UNIFONT, UNIFONT_JP, UNIFONT_NAME, UNIFONT_SIZE, UNIFONT_UPPER}; -use taffy::{ - Layout, - prelude::{AvailableSpace, Size}, -}; - -use crate::{Rect, Vec2, View, Widget, color}; - -// https://github.com/DioxusLabs/taffy/blob/main/examples/cosmic_text.rs - -pub struct FontStuff { - font_system: FontSystem, - swash_cache: SwashCache, -} - -impl FontStuff { - pub fn new() -> Self { - let mut font_system = FontSystem::new(); - let db = font_system.db_mut(); - db.load_font_data(UNIFONT.to_vec()); - db.load_font_data(UNIFONT_JP.to_vec()); - db.load_font_data(UNIFONT_UPPER.to_vec()); - db.set_monospace_family(UNIFONT_NAME); - - Self { - font_system, - swash_cache: SwashCache::new(), - } - } -} - -impl Default for FontStuff { - fn default() -> Self { - Self::new() - } -} - -pub trait HasFontStuff { - fn font_stuff(&mut self) -> &mut FontStuff; -} - -pub struct Text { - metrics: Metrics, - default_attrs: AttrsOwned, - chunks: Vec<(AttrsOwned, String)>, - shaping: Shaping, - color: Srgba, -} - -impl Text { - pub const fn default_metrics() -> Metrics { - Metrics::new(UNIFONT_SIZE, UNIFONT_SIZE) - } - - pub fn default_attrs<'a>() -> Attrs<'a> { - Attrs::new().family(Family::Monospace) - } - - pub fn new() -> Self { - Self { - metrics: Self::default_metrics(), - default_attrs: AttrsOwned::new(Self::default_attrs()), - chunks: vec![], - shaping: Shaping::Advanced, - color: color::BLACK, - } - } - - pub fn with_metrics(mut self, metrics: Metrics) -> Self { - self.metrics = metrics; - self - } - - pub fn with_font_size(mut self, size: f32) -> Self { - self.metrics.font_size = size; - self - } - - pub fn with_line_height(mut self, height: f32) -> Self { - self.metrics.line_height = height; - self - } - - pub fn with_default_attrs(mut self, attrs: Attrs<'_>) -> Self { - self.default_attrs = AttrsOwned::new(attrs); - self - } - - pub fn with_shaping(mut self, shaping: Shaping) -> Self { - self.shaping = shaping; - self - } - - pub fn with_color(mut self, color: Srgba) -> Self { - self.color = color; - self - } - - pub fn and_plain(mut self, text: S) -> Self { - let chunk = (self.default_attrs.clone(), text.to_string()); - self.chunks.push(chunk); - self - } - - pub fn and_rich(mut self, attrs: Attrs<'_>, text: S) -> Self { - let chunk = (AttrsOwned::new(attrs), text.to_string()); - self.chunks.push(chunk); - self - } - - pub fn and_chunks(mut self, chunks: I) -> Self - where - I: IntoIterator, - { - self.chunks.extend(chunks); - self - } - - pub fn widget(self, font_stuff: &mut FontStuff) -> impl Widget + use { - let fs = &mut font_stuff.font_system; - let mut buffer = Buffer::new_empty(self.metrics); - buffer.set_size(fs, None, None); - - let spans = self - .chunks - .iter() - .map(|(attrs, text)| (text as &str, attrs.as_attrs())); - buffer.set_rich_text(fs, spans, self.default_attrs.as_attrs(), self.shaping); - - TextWidget { - buffer, - color: self.color, - } - } -} - -impl Default for Text { - fn default() -> Self { - Self::new() - } -} - -struct TextWidget { - buffer: Buffer, - color: Srgba, -} - -impl Widget for TextWidget { - fn size( - &mut self, - ctx: &mut C, - known: Size>, - available: Size, - ) -> Size { - let width = known.width.unwrap_or(match available.width { - AvailableSpace::Definite(width) => width, - AvailableSpace::MinContent => 0.0, - AvailableSpace::MaxContent => f32::INFINITY, - }); - - let fs = &mut ctx.font_stuff().font_system; - self.buffer.set_size(fs, Some(width), None); - self.buffer.shape_until_scroll(fs, false); - - let runs = self.buffer.layout_runs().collect::>(); - let height = runs.len() as f32 * self.buffer.metrics().line_height; - let width = runs - .into_iter() - .map(|run| run.line_w) - .max_by(|a, b| a.partial_cmp(b).unwrap()) - .unwrap_or(0.0); - - // If we don't round up here, the layout rounding may round down our - // size slightly. This may lead to more line breaks, moving some words - // below our visible area. - let width = width.ceil(); - let height = height.ceil(); - - Size { width, height } - } - - fn draw_below( - &mut self, - ctx: &mut C, - view: &mut View<'_>, - _layout: &Layout, - ) -> anyhow::Result<()> { - let size = view.size(); - - let FontStuff { - font_system: fs, - swash_cache: sc, - } = ctx.font_stuff(); - - self.buffer - .set_size(fs, Some(size.x as f32), Some(size.y as f32)); - self.buffer.shape_until_scroll(fs, true); - - let color = color::to_text_color(self.color); - self.buffer.draw(fs, sc, color, |x, y, w, h, color| { - let color = color::from_text_color(color); - let area = Rect::from_nw(Vec2::new(x, y), Vec2::from_u32(w, h)); - view.rect(area, color); - }); - - Ok(()) - } -} diff --git a/showbits-common/src/widgets/typst.rs b/showbits-common/src/widgets/typst.rs deleted file mode 100644 index ad88d37..0000000 --- a/showbits-common/src/widgets/typst.rs +++ /dev/null @@ -1,217 +0,0 @@ -use std::{fs, path::PathBuf, sync::OnceLock}; - -use anyhow::anyhow; -use image::RgbaImage; -use taffy::{ - Layout, - prelude::{AvailableSpace, Size}, -}; -use typst::{ - Library, World, - diag::{FileError, FileResult}, - foundations::{Bytes, Datetime}, - layout::Abs, - syntax::{FileId, Source}, - text::{Font, FontBook}, - utils::LazyHash, - visualize::Color, -}; - -use crate::{View, Widget}; - -// The logic for detecting and loading fonts was ripped straight from: -// https://github.com/typst/typst/blob/69dcc89d84176838c293b2d59747cd65e28843ad/crates/typst-cli/src/fonts.rs -// https://github.com/typst/typst/blob/69dcc89d84176838c293b2d59747cd65e28843ad/crates/typst-cli/src/world.rs#L193-L195 - -struct FontSlot { - path: PathBuf, - index: u32, - font: OnceLock>, -} - -impl FontSlot { - pub fn get(&self) -> Option { - self.font - .get_or_init(|| { - let data = fs::read(&self.path).ok()?; - Font::new(Bytes::new(data), self.index) - }) - .clone() - } -} - -struct FontLoader { - book: FontBook, - fonts: Vec, -} - -impl FontLoader { - fn new() -> Self { - Self { - book: FontBook::new(), - fonts: vec![], - } - } - - fn load_embedded_fonts(&mut self) { - // https://github.com/typst/typst/blob/be12762d942e978ddf2e0ac5c34125264ab483b7/crates/typst-cli/src/fonts.rs#L107-L121 - for font_file in typst_assets::fonts() { - let font_data = Bytes::new(font_file); - for (i, font) in Font::iter(font_data).enumerate() { - self.book.push(font.info().clone()); - self.fonts.push(FontSlot { - path: PathBuf::new(), - index: i as u32, - font: OnceLock::from(Some(font)), - }); - } - } - } -} - -struct DummyWorld { - library: LazyHash, - book: LazyHash, - main: Source, - fonts: Vec, -} - -impl DummyWorld { - fn new(main: String) -> Self { - let mut loader = FontLoader::new(); - loader.load_embedded_fonts(); - - Self { - library: LazyHash::new(Library::builder().build()), - book: LazyHash::new(loader.book), - main: Source::detached(main), - fonts: loader.fonts, - } - } -} - -impl World for DummyWorld { - fn library(&self) -> &LazyHash { - &self.library - } - - fn book(&self) -> &LazyHash { - &self.book - } - - fn main(&self) -> FileId { - self.main.id() - } - - fn source(&self, id: FileId) -> FileResult { - if id == self.main.id() { - return Ok(self.main.clone()); - } - Err(FileError::AccessDenied) - } - - fn file(&self, _id: FileId) -> FileResult { - Err(FileError::AccessDenied) - } - - fn font(&self, index: usize) -> Option { - self.fonts[index].get() - } - - fn today(&self, _offset: Option) -> Option { - None - } -} - -const SCALE: f32 = 3.0; - -pub struct Typst { - code: String, -} - -impl Typst { - pub fn new(code: String) -> Self { - Self { code } - } - - fn render(&self, width: Option, height: Option) -> Result> { - let width = match width { - Some(width) => format!("{}pt", width / SCALE), - None => "auto".to_string(), - }; - - let height = match height { - Some(height) => format!("{}pt", height / SCALE), - None => "auto".to_string(), - }; - - let mut source = String::new(); - source.push_str(&format!("#set page(width: {width}, height: {height})\n")); - source.push_str("#set page(margin: (left: 0mm, right: 0mm, top: 1mm, bottom: 2mm))\n"); - source.push_str(&self.code); - - let world = DummyWorld::new(source); - - let document = typst::compile(&world).output.map_err(|errs| { - errs.into_iter() - .map(|sd| sd.message.to_string()) - .collect::>() - })?; - - let pixmap = typst_render::render_merged(&document, SCALE, Abs::zero(), Some(Color::WHITE)); - let buffer = RgbaImage::from_raw(pixmap.width(), pixmap.height(), pixmap.take()).unwrap(); - Ok(buffer) - } -} - -impl Widget for Typst { - fn size( - &mut self, - _ctx: &mut C, - known: Size>, - available: Size, - ) -> Size { - let width = known.width.or(match available.width { - AvailableSpace::Definite(width) => Some(width), - AvailableSpace::MinContent => None, // auto - AvailableSpace::MaxContent => Some(4096.0), - }); - - let height = known.height.or(match available.height { - AvailableSpace::Definite(width) => Some(width), - AvailableSpace::MinContent | AvailableSpace::MaxContent => None, // auto - }); - - let Ok(buffer) = self.render(width, height) else { - return Size { - width: width.unwrap_or(0.0), - height: height.unwrap_or(0.0), - }; - }; - - // Round up so we definitely have enough space. - // let width = (buffer.width() as f32 / SCALE).ceil() * SCALE + 1.0; - // let height = (buffer.height() as f32 / SCALE).ceil() * SCALE + 1.0; - let width = (buffer.width() as f32).ceil() + 1.0; - let height = (buffer.height() as f32).ceil() + 1.0; - - Size { width, height } - } - - fn draw_below( - &mut self, - _ctx: &mut C, - view: &mut View<'_>, - _layout: &Layout, - ) -> anyhow::Result<()> { - let size = view.size(); - - let buffer = self - .render(Some(size.x as f32), Some(size.y as f32)) - .map_err(|errs| anyhow!("{}", errs.join("\n")))?; - - view.image(&buffer); - - Ok(()) - } -} diff --git a/showbits-thermal-printer/Cargo.toml b/showbits-thermal-printer/Cargo.toml index b6d1a38..e8d7bf9 100644 --- a/showbits-thermal-printer/Cargo.toml +++ b/showbits-thermal-printer/Cargo.toml @@ -8,7 +8,7 @@ anyhow = { workspace = true } axum = { workspace = true, features = ["multipart"] } clap = { workspace = true } escpos = { workspace = true } -image = { workspace = true, default-features = true } +image = { workspace = true } jiff = { workspace = true } mime_guess = { workspace = true } palette = { workspace = true }