diff --git a/Cargo.lock b/Cargo.lock index 6b8a3f4..b6551fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,12 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-trait" version = "0.1.77" @@ -196,6 +202,58 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "20.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9d2790b5c08465d49f8dc05c8bcae9fea467855947db39b0f8145c091aaced5" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + [[package]] name = "crc32fast" version = "1.4.0" @@ -236,12 +294,43 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +[[package]] +name = "cstr" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68523903c8ae5aacfa32a0d9ae60cadeb764e1da14ee0d26b1f3089f13a54636" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "cursor-icon" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "dlib" version = "0.5.2" @@ -257,6 +346,18 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" +[[package]] +name = "dwrote" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" +dependencies = [ + "lazy_static", + "libc", + "winapi", + "wio", +] + [[package]] name = "either" version = "1.10.0" @@ -279,6 +380,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "euclid" +version = "0.22.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f253bc5c813ca05792837a0ff4b3a580336b224512d48f7eda1d7dd9210787" +dependencies = [ + "num-traits", +] + [[package]] name = "exr" version = "1.72.0" @@ -334,6 +444,12 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + [[package]] name = "flume" version = "0.10.14" @@ -356,12 +472,85 @@ dependencies = [ "spin", ] +[[package]] +name = "font-kit" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50ba02d3a19ab9012a00314ff4d105128cdc7ba223d69d48181f2d257244d51" +dependencies = [ + "bitflags 2.4.2", + "byteorder", + "core-foundation", + "core-graphics", + "core-text", + "dirs-next", + "dwrote", + "float-ord", + "freetype", + "lazy_static", + "libc", + "log", + "pathfinder_geometry", + "pathfinder_simd", + "walkdir", + "winapi", + "yeslogic-fontconfig-sys", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.49", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "fragile" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c2141d6d6c8512188a7891b4b01590a45f6dac67afb4f255c4124dbb86d4eaa" +[[package]] +name = "freetype" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a440748e063798e4893ceb877151e84acef9bea9a8c6800645cf3f1b3a7806e" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7edc5b9669349acfda99533e9e0bcf26a51862ab43b08ee7745c55d28eb134" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "futures" version = "0.3.30" @@ -799,6 +988,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -817,6 +1015,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "lebe" version = "0.5.2" @@ -845,6 +1049,16 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.4.2", + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -867,6 +1081,17 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "lyon_geom" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edecfb8d234a2b0be031ab02ebcdd9f3b9ee418fb35e265f7a540a48d197bff9" +dependencies = [ + "arrayvec", + "euclid", + "num-traits", +] + [[package]] name = "memchr" version = "2.7.1" @@ -926,6 +1151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -992,6 +1218,25 @@ dependencies = [ "system-deps", ] +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebf45976c56919841273f2a0fc684c28437e2f304e264557d9c72be5d5a718be" +dependencies = [ + "rustc_version", +] + [[package]] name = "pin-project" version = "1.1.4" @@ -1127,6 +1372,21 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "raqote" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3061d5dcf59093c811d645c517be6eb7c26a0110d146730418950139496f84" +dependencies = [ + "euclid", + "font-kit", + "lyon_geom", + "pathfinder_geometry", + "png", + "sw-composite", + "typed-arena", +] + [[package]] name = "rayon" version = "1.8.1" @@ -1147,6 +1407,17 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_users" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "relm4" version = "0.6.2" @@ -1242,6 +1513,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1347,6 +1627,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "sw-composite" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ac8fb7895b4afa060ad731a32860db8755da3449a47e796d5ecf758db2671d4" + [[package]] name = "syn" version = "1.0.109" @@ -1506,6 +1792,12 @@ dependencies = [ "once_cell", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -1524,6 +1816,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1685,7 +1987,9 @@ version = "0.1.0" dependencies = [ "anyhow", "image", + "itertools", "ocrs", + "raqote", "relm4", "rten", "rten-tensor", @@ -1715,6 +2019,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" +dependencies = [ + "windows-sys", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1805,6 +2118,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wio" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +dependencies = [ + "winapi", +] + [[package]] name = "xcursor" version = "0.3.5" @@ -1831,6 +2153,18 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "yeslogic-fontconfig-sys" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb6b23999a8b1a997bf47c7bb4d19ad4029c3327bb3386ebe0a5ff584b33c7a" +dependencies = [ + "cstr", + "dlib", + "once_cell", + "pkg-config", +] + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/Cargo.toml b/Cargo.toml index 2ee6cee..125acb3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,3 +14,5 @@ image = "0.24.9" anyhow = "1.0.81" smithay-client-toolkit = "0.18.1" wayland-client = "0.31.2" +itertools = "0.12.1" +raqote = "0.8.4" diff --git a/src/lib.rs b/src/lib.rs deleted file mode 100644 index 8b13789..0000000 --- a/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/wayland.rs b/src/wayland.rs index 1cbfab7..b67eb1c 100644 --- a/src/wayland.rs +++ b/src/wayland.rs @@ -31,8 +31,11 @@ use std::time::Duration; use anyhow::Result; mod ui; +mod pixel; +mod app; + +use ui::{ButtonBuilder, DrawAreaBuilder, TextAreaBuilder}; -use ui::{Pixel, PixelEncoding}; const WINDOW_HEIGHT: u32 = 256; const WINDOW_WIDTH: u32 = 512; @@ -51,7 +54,7 @@ pub(super) fn run() -> Result<()> { let compositor = CompositorState::bind(&globals, &qh)?; let xdg_shell = XdgShell::bind(&globals, &qh)?; let shm = Shm::bind(&globals, &qh)?; - let _xdg_activation = ActivationState::bind(&globals, &qh).ok(); + let xdg_activation = ActivationState::bind(&globals, &qh).ok(); let surface = compositor.create_surface(&qh); let window = xdg_shell.create_window(surface, WindowDecorations::RequestServer, &qh); @@ -63,6 +66,17 @@ pub(super) fn run() -> Result<()> { window.commit(); + if let Some(activation) = xdg_activation.as_ref() { + activation.request_token( + &qh, + RequestData { + seat_and_serial: None, + surface: Some(window.wl_surface().clone()), + app_id: Some(String::from("simmer.waywrite")), + }, + ) + } + let pool = SlotPool::new((WINDOW_WIDTH as usize) * (WINDOW_HEIGHT as usize) * 4, &shm)?; let mut simple_window = SimpleWindow { @@ -251,16 +265,44 @@ impl SimpleWindow { } }; - for pix in canvas.chunks_exact_mut(4) { + let draw_buffer: &mut [u32] = unsafe {canvas.align_to_mut::().1}; + + + let mut window = ui::Window::new( + draw_buffer, + self.width.try_into().unwrap(), + self.height.try_into().unwrap() + ); + + let mut button = ButtonBuilder::new(); + let mut draw_area = DrawAreaBuilder::new(); + + button.position(self.width as f32 - 100.0, 50.0); + button.width(100.0); + button.height(50.0); + button.color(0x33, 0x33, 0x33); + let button = button.finish(); + + draw_area.position(0.0, 50.0); + draw_area.width(self.width as f32 - 100.0); + draw_area.height(self.height as f32 - 50.0); + draw_area.color(0x15, 0x15, 0x15); + let draw_area = draw_area.finish(); + + + let mut text_area = TextAreaBuilder::new(); + text_area.position(0.0, 0.0); + text_area.width(self.width as f32); + text_area.height(50.0); + text_area.color(0x22, 0x22, 0x22); + let text_area = text_area.finish(); + + window.add_widget(button); + window.add_widget(draw_area); + window.add_widget(text_area); + + window.render(); - let pixel_color: &[u8; 4] = &Pixel::new(0.5, 0.5, 0.5, 1.0) - .as_u32(PixelEncoding::ARGB) - .to_le_bytes(); - - let pix: &mut [u8; 4] = pix.try_into().unwrap(); - - *pix = *pixel_color; - } self.window.wl_surface().damage_buffer(0, 0, self.width as i32, self.height as i32); self.window.wl_surface().frame(qh, self.window.wl_surface().clone()); diff --git a/src/wayland/pixel.rs b/src/wayland/pixel.rs deleted file mode 100644 index 77dfb33..0000000 --- a/src/wayland/pixel.rs +++ /dev/null @@ -1,113 +0,0 @@ - -pub(super) enum PixelEncoding { - ARGB, - RGBA, -} - -struct ColorValue { - value: u8, -} - -impl ColorValue { - fn new(value: u8) -> Self { - Self { value } - } -} - -impl From for ColorValue { - fn from(value: u8) -> Self { - Self { value } - } -} - -impl From for ColorValue { - fn from(value: f32) -> Self { - assert!(value <= 1.0); - Self { value: (value * 255.0) as u8 } - } -} - -impl From for ColorValue { - fn from(value: f64) -> Self { - assert!(value <= 1.0); - Self { value: (value * 255.0) as u8 } - } -} - -pub(super) struct Pixel { - r: ColorValue, - g: ColorValue, - b: ColorValue, - a: ColorValue, -} - -impl Pixel { - pub(super) fn new>(r: T, g: T, b: T, a: T) -> Self { - Self { - r: r.into(), - g: g.into(), - b: b.into(), - a: a.into(), - } - } - - pub(super) fn as_u32(&self, encoding: PixelEncoding) -> u32 { - match encoding { - PixelEncoding::ARGB => { - let color = [self.a.value, self.r.value, self.g.value, self.b.value]; - u32::from_be_bytes(color) - }, - PixelEncoding::RGBA => { - let color = [self.r.value, self.g.value, self.b.value, self.a.value]; - u32::from_be_bytes(color) - }, - } - } -} - -#[cfg(test)] -mod tests { - use super::{ColorValue, Pixel, PixelEncoding}; - - #[test] - fn test_max_u8() { - assert_eq!(ColorValue::from(1.0).value, 255); - } - - #[test] - fn test_min_u8() { - assert_eq!(ColorValue::from(0.0).value, 0); - } - - #[test] - fn test_argb_u32() { - let pixel = Pixel::new(0x00, 0x00, 0x00, 0x00); - assert_eq!(pixel.as_u32(PixelEncoding::ARGB), 0x00); - - let pixel = Pixel::new(0xff, 0xff, 0xff, 0xff); - assert_eq!(pixel.as_u32(PixelEncoding::ARGB), u32::MAX); - - let pixel = Pixel::new(0xaa, 0xaa, 0xaa, 0xaa); - assert_eq!(pixel.as_u32(PixelEncoding::ARGB), 0xaaaaaaaa); - - let pixel = Pixel::new(0xfd, 0xcb, 0xa9, 0x87); - println!("{:x?}", pixel.as_u32(PixelEncoding::ARGB)); - assert_eq!(pixel.as_u32(PixelEncoding::ARGB), 0x87fdcba9); - } - - #[test] - fn test_rgba_u32() { - let pixel = Pixel::new(0x00, 0x00, 0x00, 0x00); - assert_eq!(pixel.as_u32(PixelEncoding::RGBA), 0x00); - - let pixel = Pixel::new(0xff, 0xff, 0xff, 0xff); - assert_eq!(pixel.as_u32(PixelEncoding::RGBA), u32::MAX); - - let pixel = Pixel::new(0xaa, 0xaa, 0xaa, 0xaa); - assert_eq!(pixel.as_u32(PixelEncoding::RGBA), 0xaaaaaaaa); - - let pixel = Pixel::new(0xfd, 0xcb, 0xa9, 0x87); - println!("{:x?}", pixel.as_u32(PixelEncoding::RGBA)); - assert_eq!(pixel.as_u32(PixelEncoding::RGBA), 0xfdcba987); - } -} diff --git a/src/wayland/ui.rs b/src/wayland/ui.rs index e69de29..1a1b0e6 100644 --- a/src/wayland/ui.rs +++ b/src/wayland/ui.rs @@ -0,0 +1,335 @@ +use crate::wayland::pixel::{Pixel, Box, BoxMut, PixelEncoding}; +use raqote::{DrawTarget, PathBuilder, Source, StrokeStyle, DrawOptions, SolidSource, Color}; + +struct UIColor { + draw_area: Color, + button: Color, + button_accent: Color, + background: Color, + _text_area: Color, + _symbol: Color, + _text: Color, +} + +impl Default for UIColor { + fn default() -> Self { + Self { + draw_area: Color::new(0xff, 0x15, 0x15, 0x15), + button: Color::new(0xff, 0x22, 0x22, 0x22), + button_accent: Color::new(0xff, 0x33, 0x33, 0x33), + background: Color::new(0xff, 0x11, 0x11, 0x11), + _text_area: Color::new(0xff, 0x33, 0x33, 0x33), + _symbol: Color::new(0xff, 0xcc, 0xcc, 0xcc), + _text: Color::new(0xff, 0xdd, 0xdd, 0xdd), + } + } +} + +pub(super) struct Window<'a, 'b> { + pub(super) draw: DrawTarget<&'a mut [u32]>, + pub(super) options: DrawOptions, + pub(super) colors: UIColor, + pub(super) widgets: Vec>, + pub(super) width: usize, + pub(super) height: usize, + +} + +impl<'a, 'b> Window<'a, 'b> { + pub(super) fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self { + Self { + draw: DrawTarget::from_backing(width.try_into().unwrap(), height.try_into().unwrap(), buffer), + options: DrawOptions::new(), + colors: UIColor::default(), + widgets: Vec::new(), + width, + height, + } + } + + pub(super) fn add_widget(&mut self, widget: Widget<'b>) { + self.widgets.push(widget); + } + + pub(super) fn render(&mut self) { + + self.draw.fill_rect( + 0.0, + 0.0, + self.width as f32, + self.height as f32, + &Source::Solid(self.colors.background.into()), + &self.options + ); + + for widget in self.widgets.iter() { + match widget { + Widget::Button(button) => button.render(&mut self.draw), + Widget::DrawArea(draw_area) => draw_area.render(&mut self.draw), + Widget::TextArea(text_area) => text_area.render(&mut self.draw), + } + } + } +} + +pub(super) struct Position { + pub(super) x: f32, + pub(super) y: f32, +} + +impl Position { + pub(super) fn new(x: f32, y: f32) -> Self { + Position { x, y } + } +} + +pub(super) enum Widget<'a> { + Button(Button<'a>), + DrawArea(DrawArea<'a>), + TextArea(TextArea<'a>) +} + +pub(super) trait Draw { + fn render(&self, area: &mut DrawTarget<&mut [u32]>); +} + +pub(super) struct Button<'a> { + pub(super) width: f32, + pub(super) height: f32, + pub(super) position: Position, + pub(super) source: Source<'a>, + pub(super) options: DrawOptions, +} + +pub(super) struct ButtonBuilder<'a> { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) source: Option>, + pub(super) color: Option, + pub(super) options: Option, +} + +impl<'a> ButtonBuilder<'a> { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + source: None, + color: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + pub(super) fn source(&mut self, source: Source<'a>) { + self.source = Some(source); + } + + pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { + self.color = Some(Color::new(0xff, r, g, b)) + } + + pub(super) fn finish(self) -> Widget<'a> { + Widget::Button( + Button { + width: self.width.expect("Button must have width"), + height: self.height.expect("Button must have height"), + position: self.position.expect("Button must have a position"), + source: self.source.unwrap_or( + Source::Solid( + self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() + ) + ), + options: self.options.unwrap_or(DrawOptions::new()), + } + ) + } +} + +impl<'a> Draw for Button<'a> { + fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + + (*area).fill_rect( + self.position.x, + self.position.y, + self.width, + self.height, + &self.source, + &self.options, + ) + } +} + +pub(super) struct DrawArea<'a> { + pub(super) width: f32, + pub(super) height: f32, + pub(super) position: Position, + pub(super) source: Source<'a>, + pub(super) options: DrawOptions, +} + +pub(super) struct DrawAreaBuilder<'a> { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) source: Option>, + pub(super) color: Option, + pub(super) options: Option, +} + +impl<'a> DrawAreaBuilder<'a> { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + source: None, + color: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + pub(super) fn source(&mut self, source: Source<'a>) { + self.source = Some(source); + } + + pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { + self.color = Some(Color::new(0xff, r, g, b)) + } + + pub(super) fn finish(self) -> Widget<'a> { + Widget::DrawArea( + DrawArea { + width: self.width.expect("Draw area must have width"), + height: self.height.expect("Draw area must have height"), + position: self.position.expect("Draw area must have a position"), + source: self.source.unwrap_or( + Source::Solid( + self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() + ) + ), + options: self.options.unwrap_or(DrawOptions::new()), + } + ) + } +} + +impl<'a> Draw for DrawArea<'a> { + fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + area.fill_rect( + self.position.x, + self.position.y, + self.width, + self.height, + &self.source, + &self.options, + ) + } +} + + +pub(super) struct TextArea<'a> { + pub(super) width: f32, + pub(super) height: f32, + pub(super) position: Position, + pub(super) source: Source<'a>, + pub(super) options: DrawOptions, +} + +pub(super) struct TextAreaBuilder<'a> { + pub(super) width: Option, + pub(super) height: Option, + pub(super) position: Option, + pub(super) source: Option>, + pub(super) color: Option, + pub(super) options: Option, +} + +impl<'a> TextAreaBuilder<'a> { + pub(super) fn new() -> Self { + Self { + width: None, + height: None, + position: None, + source: None, + color: None, + options: None, + } + } + + pub(super) fn width(&mut self, width: f32) { + self.width = Some(width) + } + + pub(super) fn height(&mut self, height: f32) { + self.height = Some(height) + } + + pub(super) fn position(&mut self, x: f32, y: f32) { + self.position = Some(Position::new(x, y)) + } + + pub(super) fn source(&mut self, source: Source<'a>) { + self.source = Some(source); + } + + pub(super) fn color(&mut self, r: u8, g: u8, b: u8) { + self.color = Some(Color::new(0xff, r, g, b)) + } + + pub(super) fn finish(self) -> Widget<'a> { + Widget::TextArea( + TextArea { + width: self.width.expect("Button must have width"), + height: self.height.expect("Button must have height"), + position: self.position.expect("Button must have a position"), + source: self.source.unwrap_or( + Source::Solid( + self.color.unwrap_or(Color::new(0xff, 0x00, 0x00, 0x00)).into() + ) + ), + options: self.options.unwrap_or(DrawOptions::new()), + } + ) + } +} + +impl<'a> Draw for TextArea<'a> { + fn render(&self, area: &mut DrawTarget<&mut [u32]>) { + + (*area).fill_rect( + self.position.x, + self.position.y, + self.width, + self.height, + &self.source, + &self.options, + ) + } +} +