From 468dc30f8746267f155f1bfcf18a8e4307a9c15e Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 16 Apr 2023 01:09:08 +0200 Subject: [PATCH 01/49] I think I'm on to something there --- lokui/src/bin/playground.rs | 73 ++++++++++++++++++++++++++++++++++ lokui/src/components/button.rs | 53 ++++++++++++++++++++++++ lokui/src/components/mod.rs | 4 ++ lokui/src/components/number.rs | 18 +++++++++ lokui/src/components/pane.rs | 55 +++++++++++++++++++++++++ lokui/src/components/text.rs | 22 ++++++++++ lokui/src/lazy.rs | 47 ++++++++++++++++++++++ lokui/src/lib.rs | 17 ++------ lokui/src/widget.rs | 11 +++++ 9 files changed, 286 insertions(+), 14 deletions(-) create mode 100644 lokui/src/bin/playground.rs create mode 100644 lokui/src/components/button.rs create mode 100644 lokui/src/components/mod.rs create mode 100644 lokui/src/components/number.rs create mode 100644 lokui/src/components/pane.rs create mode 100644 lokui/src/components/text.rs create mode 100644 lokui/src/lazy.rs create mode 100644 lokui/src/widget.rs diff --git a/lokui/src/bin/playground.rs b/lokui/src/bin/playground.rs new file mode 100644 index 0000000..821dd8b --- /dev/null +++ b/lokui/src/bin/playground.rs @@ -0,0 +1,73 @@ +use std::ops::Deref; + +use lokui::components::button::button; +use lokui::components::number::number; +use lokui::components::pane::{pane, Pane}; +use lokui::components::text::text; +use lokui::lazy::{Laz, Lazy}; +use lokui::widget::{Event, Widget}; + +struct Counter { + value: Laz, + inner: Pane, +} + +impl Counter { + fn new() -> Self { + let value = Laz::new(0); + + let increment = { + let value = value.clone(); + move || value.set(value.get() + 1) + }; + + let decrement = { + let value = value.clone(); + move || value.set(value.get() - 1) + }; + + let inner = pane() + .child(text(Lazy::new("Count: "))) + .child(number(value.clone())) + .child( + pane() + .child(button("+1").on_click(increment)) + .child(button("-1").on_click(decrement)), + ); + + Counter { value, inner } + } + + fn value(&self) -> &Laz { + &self.value + } +} + +impl Deref for Counter { + type Target = Pane; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Widget for Counter { + fn draw(&self, indent: usize) { + self.inner.draw(indent); + } + + fn update(&mut self, event: Event) -> bool { + self.inner.update(event) + } +} + +fn main() { + let mut counter = Counter::new(); + + counter.draw(0); + println!("\nThe value of the counter is {}\n", counter.value().get()); + + counter.update(Event::Clicked); + counter.draw(0); + println!("\nThe value of the counter is {}\n", counter.value().get()); +} diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs new file mode 100644 index 0000000..cf2c9c0 --- /dev/null +++ b/lokui/src/components/button.rs @@ -0,0 +1,53 @@ +use crate::lazy::Laz; +use crate::widget::{Widget, Event}; + +pub struct Button { + text: String, + on_click: Option>, + enabled: Laz, + hovered: bool, +} + +impl Button { + pub fn on_click(mut self, on_click: impl FnMut() + 'static) -> Self { + self.on_click = Some(Box::new(on_click)); + self + } +} + +impl Widget for Button { + fn draw(&self, indent: usize) { + println!("{}", " ".repeat(indent), &self.text); + } + + fn update(&mut self, event: Event) -> bool { + if self.enabled.get() { + match event { + Event::Clicked => { + if let Some(on_click) = self.on_click.as_mut() { + (on_click)(); + } + } + Event::HoverStart => { + self.hovered = true; + } + Event::HoverEnd => { + self.hovered = false; + } + } + + true + } else { + false + } + } +} + +pub fn button(text: impl Into) -> Button { + Button { + text: text.into(), + on_click: None, + enabled: Laz::new(true), + hovered: false, + } +} diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs new file mode 100644 index 0000000..4c91479 --- /dev/null +++ b/lokui/src/components/mod.rs @@ -0,0 +1,4 @@ +pub mod button; +pub mod number; +pub mod pane; +pub mod text; diff --git a/lokui/src/components/number.rs b/lokui/src/components/number.rs new file mode 100644 index 0000000..c6a7408 --- /dev/null +++ b/lokui/src/components/number.rs @@ -0,0 +1,18 @@ +use crate::lazy::Laz; +use crate::widget::{Widget, Event}; + +pub struct Number(Laz); + +pub fn number(val: Laz) -> Number { + Number(val) +} + +impl Widget for Number { + fn draw(&self, indent: usize) { + println!("{}{}", " ".repeat(indent), self.0.get()); + } + + fn update(&mut self, _event: Event) -> bool { + false + } +} diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs new file mode 100644 index 0000000..ce1d456 --- /dev/null +++ b/lokui/src/components/pane.rs @@ -0,0 +1,55 @@ +use crate::widget::{Event, Widget}; + +#[derive(Default)] +pub struct Pane { + children: Vec>, +} + +impl Pane { + pub fn child(mut self, widget: impl Widget + 'static) -> Self { + self.add_child(widget); + self + } + + pub fn add_child(&mut self, widget: impl Widget + 'static) { + self.add_dyn_child(Box::new(widget)); + } + + pub fn add_dyn_child(&mut self, widget: Box) { + self.children.push(widget); + } + + pub fn pop_child(&mut self) -> Option> { + self.children.pop() + } +} + +pub fn pane() -> Pane { + Pane::default() +} + +impl Widget for Pane { + fn draw(&self, indent: usize) { + println!("{}", " ".repeat(indent)); + + for child in &self.children { + child.draw(indent + 1); + } + + println!("{}", " ".repeat(indent)); + } + + fn update(&mut self, event: Event) -> bool { + let mut handled = false; + + for child in &mut self.children { + handled |= child.update(event); + + if handled { + break; + } + } + + handled + } +} diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs new file mode 100644 index 0000000..c4d4c53 --- /dev/null +++ b/lokui/src/components/text.rs @@ -0,0 +1,22 @@ +use crate::lazy::Lazy; +use crate::widget::{Event, Widget}; + +pub fn text>(val: Lazy) -> Text { + Text(val) +} + +pub struct Text>(Lazy); + +impl> Widget for Text { + fn draw(&self, indent: usize) { + println!( + "{}{}", + " ".repeat(indent), + self.0.get().as_ref() + ); + } + + fn update(&mut self, _event: Event) -> bool { + false + } +} diff --git a/lokui/src/lazy.rs b/lokui/src/lazy.rs new file mode 100644 index 0000000..4966a61 --- /dev/null +++ b/lokui/src/lazy.rs @@ -0,0 +1,47 @@ +use std::cell::{Cell, Ref, RefCell}; +use std::ops::Deref; +use std::rc::Rc; + +pub struct Lazy(Rc>); + +impl Lazy { + pub fn new(val: T) -> Self { + Lazy(Rc::new(RefCell::new(val))) + } + + pub fn get(&self) -> Ref { + self.0.borrow() + } + + pub fn set(&self, val: T) { + *self.0.borrow_mut() = val; + } +} + +impl Clone for Lazy { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + +pub struct Laz(Rc>); + +impl Laz { + pub fn new(val: T) -> Self { + Laz(Rc::new(Cell::new(val))) + } +} + +impl Clone for Laz { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + +impl Deref for Laz { + type Target = Cell; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index 06d268d..9f5330f 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -1,14 +1,3 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod components; +pub mod lazy; +pub mod widget; diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs new file mode 100644 index 0000000..7bbbf74 --- /dev/null +++ b/lokui/src/widget.rs @@ -0,0 +1,11 @@ +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum Event { + Clicked, + HoverStart, + HoverEnd, +} + +pub trait Widget { + fn draw(&self, indent: usize); + fn update(&mut self, event: Event) -> bool; +} From 6054eef04d48cd745a840552248a411cee996c20 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 16 Apr 2023 18:42:57 +0200 Subject: [PATCH 02/49] =?UTF-8?q?I=20got=20one=20button=20inside=20a=20pan?= =?UTF-8?q?el=20with=20paddings=20=F0=9F=91=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 773 +++++++++++++++++++++++++++++++++ lokui/Cargo.toml | 2 + lokui/examples/playground.rs | 133 ++++++ lokui/src/bin/playground.rs | 73 ---- lokui/src/components/button.rs | 57 ++- lokui/src/components/mod.rs | 4 +- lokui/src/components/number.rs | 22 +- lokui/src/components/pane.rs | 142 +++++- lokui/src/components/text.rs | 24 +- lokui/src/layout/anchor.rs | 101 +++++ lokui/src/layout/mod.rs | 161 +++++++ lokui/src/layout/padding.rs | 154 +++++++ lokui/src/lib.rs | 3 + lokui/src/widget.rs | 42 +- 14 files changed, 1577 insertions(+), 114 deletions(-) create mode 100644 lokui/examples/playground.rs delete mode 100644 lokui/src/bin/playground.rs create mode 100644 lokui/src/layout/anchor.rs create mode 100644 lokui/src/layout/mod.rs create mode 100644 lokui/src/layout/padding.rs diff --git a/Cargo.lock b/Cargo.lock index 18af8a5..05275a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,21 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -14,12 +29,46 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64ct" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bindgen" +version = "0.64.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "block-buffer" version = "0.10.4" @@ -29,18 +78,50 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "cc" +version = "1.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clang-sys" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" +dependencies = [ + "glob", + "libc", + "libloading", +] + [[package]] name = "const-oid" version = "0.9.2" @@ -56,6 +137,15 @@ dependencies = [ "libc", ] +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -88,6 +178,43 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "filetime" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "form_urlencoded" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -109,6 +236,59 @@ dependencies = [ "wasi", ] +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "js-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -118,18 +298,43 @@ dependencies = [ "spin", ] +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.141" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libm" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" +[[package]] +name = "log" +version = "0.4.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +dependencies = [ + "cfg-if", +] + [[package]] name = "loki-client" version = "0.1.0" @@ -154,6 +359,68 @@ version = "0.1.0" [[package]] name = "lokui" version = "0.1.0" +dependencies = [ + "miniquad", + "skia-safe", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniquad" +version = "0.4.0-alpha.0" +source = "git+https://github.com/loki-chat/lokinit#2ec39bfacc0830538640ca68addd24da94766b1c" +dependencies = [ + "libc", + "ndk-sys", + "objc", + "skia-safe", + "winapi", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "ndk-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] [[package]] name = "num-bigint-dig" @@ -203,6 +470,27 @@ dependencies = [ "libm", ] +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", +] + +[[package]] +name = "once_cell" +version = "1.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + [[package]] name = "pem-rfc7468" version = "0.6.0" @@ -212,6 +500,12 @@ dependencies = [ "base64ct", ] +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + [[package]] name = "pkcs1" version = "0.4.1" @@ -240,6 +534,15 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "proc-macro2" +version = "1.0.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +dependencies = [ + "unicode-ident", +] + [[package]] name = "protolok" version = "0.1.0" @@ -250,6 +553,15 @@ dependencies = [ "sha2", ] +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rand" version = "0.8.5" @@ -280,6 +592,47 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", +] + [[package]] name = "rsa" version = "0.8.2" @@ -300,6 +653,66 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustls" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +dependencies = [ + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "serde" +version = "1.0.160" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" + +[[package]] +name = "serde_json" +version = "1.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_spanned" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +dependencies = [ + "serde", +] + [[package]] name = "sha2" version = "0.10.6" @@ -311,6 +724,12 @@ dependencies = [ "digest", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "signature" version = "2.1.0" @@ -321,6 +740,35 @@ dependencies = [ "rand_core", ] +[[package]] +name = "skia-bindings" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d166792c15b8ebd180b83e3b9ab38ef69c09468ed26c11692fb59c5af9bc1d" +dependencies = [ + "bindgen", + "cc", + "flate2", + "heck", + "lazy_static", + "regex", + "serde_json", + "tar", + "toml", + "ureq", +] + +[[package]] +name = "skia-safe" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fee050b67b2124a5745484f4216999951feeeb1577ef1515e9194ea6d9a9612" +dependencies = [ + "bitflags", + "lazy_static", + "skia-bindings", +] + [[package]] name = "smallvec" version = "1.10.0" @@ -349,12 +797,137 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tar" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "toml" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.19.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "ureq" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +dependencies = [ + "base64", + "flate2", + "log", + "once_cell", + "rustls", + "url", + "webpki", + "webpki-roots", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "version_check" version = "0.9.4" @@ -367,6 +940,206 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +dependencies = [ + "webpki", +] + +[[package]] +name = "which" +version = "4.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +dependencies = [ + "either", + "libc", + "once_cell", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + +[[package]] +name = "winnow" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +dependencies = [ + "memchr", +] + +[[package]] +name = "xattr" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" +dependencies = [ + "libc", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/lokui/Cargo.toml b/lokui/Cargo.toml index 959dfc1..87262cc 100644 --- a/lokui/Cargo.toml +++ b/lokui/Cargo.toml @@ -6,3 +6,5 @@ version = "0.1.0" edition = "2021" [dependencies] +skia-safe = { version = "0.60.0", features = ["gl"] } +miniquad = { git = "https://github.com/loki-chat/lokinit" } diff --git a/lokui/examples/playground.rs b/lokui/examples/playground.rs new file mode 100644 index 0000000..bfce420 --- /dev/null +++ b/lokui/examples/playground.rs @@ -0,0 +1,133 @@ +#![allow(clippy::unusual_byte_groupings)] + +use std::ops::Deref; + +use lokui::components::button::button; +// use lokui::components::number::number; +use lokui::components::pane::{pane, Pane}; +use lokui::layout::{Anchor, DimScalar, Layout, Padding, SolvedLayout}; +// use lokui::components::text::text; +use lokui::lazy::Laz; +use lokui::widget::{Event, Widget}; +use miniquad::skia::SkiaContext; +use miniquad::{conf, EventHandler}; +use skia_safe::Color; + +struct Counter { + value: Laz, + layout: Layout, + inner: Pane, +} + +impl Counter { + fn new() -> Self { + let value = Laz::new(0); + + let increment = { + let value = value.clone(); + move |_, _| value.set(value.get() + 1) + }; + + // let decrement = { + // let value = value.clone(); + // move || value.set(value.get() - 1) + // }; + + let inner = pane() + .with_padding(Padding::splat(10.)) + .with_layout( + Layout::new() + .with_anchor(Anchor::CENTER) + .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), + ) + // .child(text(Lazy::new("Count: "))) + // .child(number(value.clone())) + .child( + pane() + .with_padding(Padding::vh(5., 30.)) + .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) + .child(button("+1").on_click(increment)), + // .child(button("-1").on_click(decrement)), + ); + + Counter { + value, + inner, + layout: Layout::new(), + } + } + + fn value(&self) -> &Laz { + &self.value + } +} + +impl Deref for Counter { + type Target = Pane; + + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl Widget for Counter { + fn handle_event(&mut self, event: Event) -> bool { + self.inner.handle_event(event) + } + + fn layout(&self) -> &Layout { + &self.layout + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.inner.solve_layout(parent_layout) + } + + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { + self.inner.draw(skia_ctx, layout); + } +} + +struct Stage { + counter: Counter, + window_layout: SolvedLayout, + counter_layout: SolvedLayout, +} + +impl EventHandler for Stage { + fn update(&mut self, _skia_ctx: &mut SkiaContext) {} + + fn draw(&mut self, skia_ctx: &mut SkiaContext) { + let canvas = &mut skia_ctx.surface.canvas(); + canvas.clear(Color::from(0xff_161a1d)); + + self.counter.draw(skia_ctx, &self.counter_layout); + + skia_ctx.dctx.flush(None); + } +} + +fn main() { + let mut counter = Counter::new(); + let window_layout = + SolvedLayout::from_top_left(0., 0., 1280., 720.).padded(Padding::splat(20.)); + let counter_layout = counter.solve_layout(&window_layout); + + miniquad::start( + conf::Conf { + high_dpi: true, + window_width: 1280, + window_height: 720, + window_resizable: false, + window_title: "Lokui GUI Framework Prototype".to_owned(), + ..Default::default() + }, + move || { + Box::new(Stage { + counter, + window_layout, + counter_layout, + }) + }, + ); +} diff --git a/lokui/src/bin/playground.rs b/lokui/src/bin/playground.rs deleted file mode 100644 index 821dd8b..0000000 --- a/lokui/src/bin/playground.rs +++ /dev/null @@ -1,73 +0,0 @@ -use std::ops::Deref; - -use lokui::components::button::button; -use lokui::components::number::number; -use lokui::components::pane::{pane, Pane}; -use lokui::components::text::text; -use lokui::lazy::{Laz, Lazy}; -use lokui::widget::{Event, Widget}; - -struct Counter { - value: Laz, - inner: Pane, -} - -impl Counter { - fn new() -> Self { - let value = Laz::new(0); - - let increment = { - let value = value.clone(); - move || value.set(value.get() + 1) - }; - - let decrement = { - let value = value.clone(); - move || value.set(value.get() - 1) - }; - - let inner = pane() - .child(text(Lazy::new("Count: "))) - .child(number(value.clone())) - .child( - pane() - .child(button("+1").on_click(increment)) - .child(button("-1").on_click(decrement)), - ); - - Counter { value, inner } - } - - fn value(&self) -> &Laz { - &self.value - } -} - -impl Deref for Counter { - type Target = Pane; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl Widget for Counter { - fn draw(&self, indent: usize) { - self.inner.draw(indent); - } - - fn update(&mut self, event: Event) -> bool { - self.inner.update(event) - } -} - -fn main() { - let mut counter = Counter::new(); - - counter.draw(0); - println!("\nThe value of the counter is {}\n", counter.value().get()); - - counter.update(Event::Clicked); - counter.draw(0); - println!("\nThe value of the counter is {}\n", counter.value().get()); -} diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index cf2c9c0..d65511b 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,31 +1,71 @@ +use miniquad::skia::SkiaContext; +use skia_safe::{Color, Paint, Rect}; + +use crate::layout::{Layout, SolvedLayout}; use crate::lazy::Laz; -use crate::widget::{Widget, Event}; +use crate::widget::{Event, Widget}; pub struct Button { + layout: Layout, text: String, - on_click: Option>, + on_click: Option>, enabled: Laz, hovered: bool, } impl Button { - pub fn on_click(mut self, on_click: impl FnMut() + 'static) -> Self { + pub fn on_click(mut self, on_click: impl FnMut(f32, f32) + 'static) -> Self { self.on_click = Some(Box::new(on_click)); self } } impl Widget for Button { - fn draw(&self, indent: usize) { - println!("{}", " ".repeat(indent), &self.text); + fn layout(&self) -> &Layout { + &self.layout + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.default_solve_layout(parent_layout) + } + + fn min_width(&mut self) -> f32 { + self.text.len() as f32 * 10. + } + + fn min_height(&mut self) -> f32 { + 15. + } + + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { + let canvas = skia_ctx.surface.canvas(); + + let rect = Rect::from_xywh( + layout.x_start(), + layout.y_start(), + layout.width(), + layout.height(), + ); + + let mut paint = Paint::default(); + paint.set_anti_alias(true); + paint.set_stroke_width(2.); + + paint.set_stroke(false); + paint.set_color(Color::from(0x80_ff0051)); + canvas.draw_rect(rect, &paint); + + paint.set_stroke(true); + paint.set_color(Color::from(0xff_ff0051)); + canvas.draw_rect(rect, &paint); } - fn update(&mut self, event: Event) -> bool { + fn handle_event(&mut self, event: Event) -> bool { if self.enabled.get() { match event { - Event::Clicked => { + Event::Clicked(x, y) => { if let Some(on_click) = self.on_click.as_mut() { - (on_click)(); + (on_click)(x, y); } } Event::HoverStart => { @@ -45,6 +85,7 @@ impl Widget for Button { pub fn button(text: impl Into) -> Button { Button { + layout: Layout::new(), text: text.into(), on_click: None, enabled: Laz::new(true), diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index 4c91479..d907d8d 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,4 +1,4 @@ pub mod button; -pub mod number; +// pub mod number; pub mod pane; -pub mod text; +// pub mod text; diff --git a/lokui/src/components/number.rs b/lokui/src/components/number.rs index c6a7408..29c0995 100644 --- a/lokui/src/components/number.rs +++ b/lokui/src/components/number.rs @@ -1,15 +1,23 @@ +use crate::layout::{Layout, SolvedLayout}; use crate::lazy::Laz; -use crate::widget::{Widget, Event}; +use crate::widget::{Event, Widget}; -pub struct Number(Laz); - -pub fn number(val: Laz) -> Number { - Number(val) +pub struct Number { + layout: Layout, + number: Laz, } +// pub fn number(val: Laz) -> Number { +// Number(val) +// } + impl Widget for Number { - fn draw(&self, indent: usize) { - println!("{}{}", " ".repeat(indent), self.0.get()); + fn layout(&self) -> &Layout { + &self.layout + } + + fn draw(&self, layout: SolvedLayout) { + // TODO: draw number as text? } fn update(&mut self, _event: Event) -> bool { diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index ce1d456..6b1f146 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,11 +1,38 @@ +use miniquad::skia::SkiaContext; +use skia_safe::{Rect, Paint, Color}; + +use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use crate::widget::{Event, Widget}; +pub struct PaneChild { + solved_layout: SolvedLayout, + widget: Box, +} + #[derive(Default)] pub struct Pane { - children: Vec>, + layout: Layout, + padding: Padding, + flex_layout: Option, + children: Vec, } impl Pane { + pub fn with_layout(mut self, layout: Layout) -> Self { + self.layout = layout; + self + } + + pub fn with_padding(mut self, padding: Padding) -> Self { + self.padding = padding; + self + } + + pub fn with_flex_layout(mut self, flex_layout: FlexLayout) -> Self { + self.flex_layout = Some(flex_layout); + self + } + pub fn child(mut self, widget: impl Widget + 'static) -> Self { self.add_child(widget); self @@ -16,11 +43,14 @@ impl Pane { } pub fn add_dyn_child(&mut self, widget: Box) { - self.children.push(widget); + self.children.push(PaneChild { + solved_layout: SolvedLayout::default(), + widget, + }); } pub fn pop_child(&mut self) -> Option> { - self.children.pop() + self.children.pop().map(|child| child.widget) } } @@ -29,21 +59,113 @@ pub fn pane() -> Pane { } impl Widget for Pane { - fn draw(&self, indent: usize) { - println!("{}", " ".repeat(indent)); + fn layout(&self) -> &Layout { + &self.layout + } - for child in &self.children { - child.draw(indent + 1); + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + let layout = self.default_solve_layout(parent_layout); + + let inner_layout = layout.padded(self.padding); + if let Some(_auto_layout) = &self.flex_layout { + // With auto-layout, all children are placed next to each other, vertically or horizontally. + + todo!("no auto-layout for now"); + // for child in &mut self.children {} + } else { + // Without auto-layout, all children are superposed to each other. + + for child in &mut self.children { + child.solved_layout = child.widget.solve_layout(&inner_layout); + } } - println!("{}", " ".repeat(indent)); + layout + } + + fn min_width(&mut self) -> f32 { + let width_pad = self.padding.left + self.padding.right; + + if let Some(auto_layout) = self.flex_layout.as_ref() { + if auto_layout.direction == Direction::Horizontal { + let inner_min_width: f32 = (self.children.iter_mut()) + .map(|child| match child.widget.layout().width { + DimScalar::Fixed(w) => w, + _ => child.widget.min_width(), + }) + .sum(); + + return inner_min_width + width_pad; + } + } + + let inner_min_width = (self.children.iter_mut()) + .map(|child| child.widget.min_width()) + .max_by(|x, y| x.total_cmp(y)) + .unwrap_or_default(); + + inner_min_width + width_pad + } + + fn min_height(&mut self) -> f32 { + let height_pad = self.padding.top + self.padding.bottom; + + if let Some(auto_layout) = self.flex_layout.as_ref() { + if auto_layout.direction == Direction::Vertical { + let inner_min_height: f32 = (self.children.iter_mut()) + .map(|child| match child.widget.layout().height { + DimScalar::Fixed(h) => h, + _ => child.widget.min_height(), + }) + .sum(); + + return inner_min_height + height_pad; + } + } + + let inner_min_height = (self.children.iter_mut()) + .map(|child| child.widget.min_height()) + .max_by(|x, y| x.total_cmp(y)) + .unwrap_or_default(); + + inner_min_height + height_pad + } + + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { + let canvas = skia_ctx.surface.canvas(); + + let rect = Rect::from_xywh( + layout.x_start(), + layout.y_start(), + layout.width(), + layout.height(), + ); + + let mut paint = Paint::default(); + paint.set_anti_alias(true); + paint.set_stroke_width(2.); + + paint.set_stroke(false); + paint.set_color(Color::from(0x40_00cc51)); + canvas.draw_rect(rect, &paint); + + paint.set_stroke(true); + paint.set_color(Color::from(0xff_00cc51)); + canvas.draw_rect(rect, &paint); + + println!("["); + for child in &self.children { + println!(" drawing child with solved layout {:?}", &child.solved_layout); + child.widget.draw(skia_ctx, &child.solved_layout); + } + println!("]"); } - fn update(&mut self, event: Event) -> bool { + fn handle_event(&mut self, event: Event) -> bool { let mut handled = false; for child in &mut self.children { - handled |= child.update(event); + handled |= child.widget.handle_event(event); if handled { break; diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index c4d4c53..a680045 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -1,19 +1,23 @@ +use crate::layout::{SolvedLayout, Layout}; use crate::lazy::Lazy; use crate::widget::{Event, Widget}; -pub fn text>(val: Lazy) -> Text { - Text(val) -} +// pub fn text>(val: Lazy) -> Text { +// Text(val) +// } -pub struct Text>(Lazy); +pub struct Text> { + layout: Layout, + text: Lazy, +} impl> Widget for Text { - fn draw(&self, indent: usize) { - println!( - "{}{}", - " ".repeat(indent), - self.0.get().as_ref() - ); + fn layout(&self) -> &Layout { + &self.layout + } + + fn draw(&self, layout: SolvedLayout) { + // TODO: draw text? } fn update(&mut self, _event: Event) -> bool { diff --git a/lokui/src/layout/anchor.rs b/lokui/src/layout/anchor.rs new file mode 100644 index 0000000..19a82ca --- /dev/null +++ b/lokui/src/layout/anchor.rs @@ -0,0 +1,101 @@ +use super::SolvedLayout; + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] +pub enum HAnchor { + Left, + #[default] + Center, + Right, +} + +impl HAnchor { + pub fn calc_x_offset(&self, inner_width: f32, outer_width: f32) -> f32 { + match self { + HAnchor::Left => 0., + HAnchor::Center => (outer_width - inner_width) / 2., + HAnchor::Right => outer_width - inner_width, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] +pub enum VAnchor { + Top, + #[default] + Center, + Bottom, +} + +impl VAnchor { + pub fn calc_y_offset(&self, inner_height: f32, outer_height: f32) -> f32 { + match self { + VAnchor::Top => 0., + VAnchor::Center => (outer_height - inner_height) / 2., + VAnchor::Bottom => outer_height - inner_height, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub struct Anchor { + pub v: VAnchor, + pub h: HAnchor, +} + +impl Anchor { + pub const TOP_LEFT: Anchor = Anchor { + v: VAnchor::Top, + h: HAnchor::Left, + }; + pub const TOP_CENTER: Anchor = Anchor { + v: VAnchor::Top, + h: HAnchor::Center, + }; + pub const TOP_RIGHT: Anchor = Anchor { + v: VAnchor::Top, + h: HAnchor::Right, + }; + + pub const CENTER_LEFT: Anchor = Anchor { + v: VAnchor::Center, + h: HAnchor::Left, + }; + pub const CENTER: Anchor = Anchor { + v: VAnchor::Center, + h: HAnchor::Center, + }; + pub const CENTER_RIGHT: Anchor = Anchor { + v: VAnchor::Center, + h: HAnchor::Right, + }; + + pub const BOTTOM_LEFT: Anchor = Anchor { + v: VAnchor::Bottom, + h: HAnchor::Left, + }; + pub const BOTTOM_CENTER: Anchor = Anchor { + v: VAnchor::Bottom, + h: HAnchor::Center, + }; + pub const BOTTOM_RIGHT: Anchor = Anchor { + v: VAnchor::Bottom, + h: HAnchor::Right, + }; + + pub fn calc_child_abs_pos( + &self, + width: f32, + height: f32, + parent_layout: &SolvedLayout, + ) -> SolvedLayout { + let x = self.h.calc_x_offset(width, parent_layout.width()); + let y = self.v.calc_y_offset(height, parent_layout.height()); + + SolvedLayout { + x: parent_layout.x + x, + y: parent_layout.y + y, + width, + height, + } + } +} diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs new file mode 100644 index 0000000..1860078 --- /dev/null +++ b/lokui/src/layout/mod.rs @@ -0,0 +1,161 @@ +pub use anchor::*; +pub use padding::*; + +pub mod anchor; +pub mod padding; + +/// Scalar value used to represent static and dynamic widths and heights. +#[derive(Clone, Copy, Debug, Default)] +pub enum DimScalar { + /// The widget fills its parent on that dimension. + #[default] + Fill, + /// The widget hugs its internal content on that dimension. + Hug, + /// The dimension is fixed by that amount of pixels. + Fixed(f32), +} + +#[derive(Clone, Debug, Default)] +pub struct Layout { + /// Position offset in pixels on the `x` axis. + pub x: f32, + /// Position offset in pixels on the `y` axis. + pub y: f32, + /// Width of the widget box. + pub width: DimScalar, + /// Height of the widget box. + pub height: DimScalar, + /// Point where the widget is placed relative to its parent. + pub anchor: Anchor, + /// Point of origin of the widget relative to its own box. + pub origin: Anchor, +} + +impl Layout { + pub fn new() -> Self { + Self::default() + } + + pub fn with_pos(mut self, x: f32, y: f32) -> Self { + self.x = x; + self.y = y; + self + } + + pub fn with_dimension(mut self, width: DimScalar, height: DimScalar) -> Self { + self.width = width; + self.height = height; + self + } + + pub fn with_anchor(mut self, anchor: Anchor) -> Self { + self.anchor = anchor; + self + } + + pub fn with_origin(mut self, origin: Anchor) -> Self { + self.origin = origin; + self + } + + pub fn position(&self) -> (f32, f32) { + (self.x, self.y) + } + + pub fn dimension(&self) -> (DimScalar, DimScalar) { + (self.width, self.height) + } +} + +/// State returned by a widget that solved its layout geometry. +#[derive(Clone, Copy, Debug, Default)] +pub struct SolvedLayout { + /// Absolute X coordinate of the concrete widget box from the top-level corner. + x: f32, + /// Absolute Y coordinate of the concrete widget box from the top-level corner. + y: f32, + /// Width of the concrete widget box. + width: f32, + /// Height of the concrete widget box. + height: f32, +} + +impl SolvedLayout { + pub fn from_top_left(x: f32, y: f32, width: f32, height: f32) -> Self { + Self { + x, + y, + width, + height, + } + } + + pub fn from_2_points(xa: f32, ya: f32, xb: f32, yb: f32) -> Self { + Self { + x: xa.min(xb), + y: ya.min(yb), + width: (xb - xa).abs(), + height: (yb - ya).abs(), + } + } + + pub fn x_start(&self) -> f32 { + self.x + } + + pub fn y_start(&self) -> f32 { + self.y + } + + pub fn x_end(&self) -> f32 { + self.x + self.width + } + + pub fn y_end(&self) -> f32 { + self.y + self.height + } + + pub fn width(&self) -> f32 { + self.width + } + + pub fn height(&self) -> f32 { + self.height + } + + pub fn contains_x(&self, x: f32) -> bool { + x >= self.x && x <= self.x + self.width + } + + pub fn contains_y(&self, y: f32) -> bool { + y >= self.y && y <= self.y + self.height + } + + pub fn contains(&self, x: f32, y: f32) -> bool { + self.contains_x(x) && self.contains_y(y) + } + + pub fn padded(&self, padding: Padding) -> Self { + Self { + x: self.x + padding.left, + y: self.y + padding.top, + width: self.width - padding.left - padding.right, + height: self.height - padding.top - padding.bottom, + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +pub enum Direction { + #[default] + Horizontal, + Vertical, +} + +#[derive(Clone, Debug, Default)] +pub struct FlexLayout { + pub direction: Direction, + pub align: Anchor, + pub gap: f32, +} diff --git a/lokui/src/layout/padding.rs b/lokui/src/layout/padding.rs new file mode 100644 index 0000000..9fb6039 --- /dev/null +++ b/lokui/src/layout/padding.rs @@ -0,0 +1,154 @@ +use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign}; + +#[derive(Clone, Copy, Debug, Default)] +pub struct Padding { + pub top: f32, + pub right: f32, + pub bottom: f32, + pub left: f32, +} + +impl Padding { + pub fn trbl(top: f32, right: f32, bottom: f32, left: f32) -> Self { + Self { + top, + right, + bottom, + left, + } + } + + pub fn vh(vertical: f32, horizontal: f32) -> Self { + Self { + top: vertical, + right: horizontal, + bottom: vertical, + left: horizontal, + } + } + + pub fn splat(val: f32) -> Self { + Self { + top: val, + right: val, + bottom: val, + left: val, + } + } +} + +// add + +impl Add for Padding { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self += rhs; + self + } +} + +impl Add for Padding { + type Output = Self; + + fn add(mut self, rhs: f32) -> Self::Output { + self += rhs; + self + } +} + +impl AddAssign for Padding { + fn add_assign(&mut self, rhs: Self) { + self.top += rhs.top; + self.right += rhs.right; + self.bottom += rhs.bottom; + self.left += rhs.left; + } +} + +impl AddAssign for Padding { + fn add_assign(&mut self, rhs: f32) { + self.top += rhs; + self.right += rhs; + self.bottom += rhs; + self.left += rhs; + } +} + +// sub + +impl Sub for Padding { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self -= rhs; + self + } +} + +impl Sub for Padding { + type Output = Self; + + fn sub(mut self, rhs: f32) -> Self::Output { + self -= rhs; + self + } +} + +impl SubAssign for Padding { + fn sub_assign(&mut self, rhs: Self) { + self.top -= rhs.top; + self.right -= rhs.right; + self.bottom -= rhs.bottom; + self.left -= rhs.left; + } +} + +impl SubAssign for Padding { + fn sub_assign(&mut self, rhs: f32) { + self.top -= rhs; + self.right -= rhs; + self.bottom -= rhs; + self.left -= rhs; + } +} + +// mul + +impl Mul for Padding { + type Output = Self; + + fn mul(mut self, rhs: f32) -> Self::Output { + self *= rhs; + self + } +} + +impl MulAssign for Padding { + fn mul_assign(&mut self, rhs: f32) { + self.top *= rhs; + self.right *= rhs; + self.bottom *= rhs; + self.left *= rhs; + } +} + +// div + +impl Div for Padding { + type Output = Self; + + fn div(mut self, rhs: f32) -> Self::Output { + self /= rhs; + self + } +} + +impl DivAssign for Padding { + fn div_assign(&mut self, rhs: f32) { + self.top /= rhs; + self.right /= rhs; + self.bottom /= rhs; + self.left /= rhs; + } +} diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index 9f5330f..1aba7e4 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -1,3 +1,6 @@ +#![allow(clippy::unusual_byte_groupings)] + pub mod components; +pub mod layout; pub mod lazy; pub mod widget; diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 7bbbf74..2c5c568 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -1,11 +1,45 @@ -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +use miniquad::skia::SkiaContext; + +use crate::layout::{DimScalar, Layout, SolvedLayout}; + +#[derive(Clone, Copy, Debug)] pub enum Event { - Clicked, + Clicked(f32, f32), HoverStart, HoverEnd, } pub trait Widget { - fn draw(&self, indent: usize); - fn update(&mut self, event: Event) -> bool; + fn layout(&self) -> &Layout; + + fn default_solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + let width = match self.layout().width { + DimScalar::Fill => parent_layout.width(), + DimScalar::Hug => self.min_width(), + DimScalar::Fixed(w) => w, + }; + + let height = match self.layout().height { + DimScalar::Fill => parent_layout.height(), + DimScalar::Hug => self.min_height(), + DimScalar::Fixed(h) => h, + }; + + (self.layout().anchor).calc_child_abs_pos(width, height, parent_layout) + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout; + + /// Minimum possible width in case we choose DimScalar::Hug as the layout width. + fn min_width(&mut self) -> f32 { + 0. + } + + /// Minimum possible height in case we choose DimScalar::Hug as the layout height. + fn min_height(&mut self) -> f32 { + 0. + } + + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout); + fn handle_event(&mut self, event: Event) -> bool; } From cb469f6abc3531bf5e9802d4b04d4861464ec4f7 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 16 Apr 2023 20:24:15 +0200 Subject: [PATCH 03/49] Got events to work with the whole system too --- lokui/examples/playground.rs | 157 +++++++++++++++------------------ lokui/src/components/button.rs | 21 +++-- lokui/src/components/pane.rs | 27 +++--- lokui/src/widget.rs | 2 +- 4 files changed, 104 insertions(+), 103 deletions(-) diff --git a/lokui/examples/playground.rs b/lokui/examples/playground.rs index bfce420..fd10ee7 100644 --- a/lokui/examples/playground.rs +++ b/lokui/examples/playground.rs @@ -1,132 +1,115 @@ #![allow(clippy::unusual_byte_groupings)] -use std::ops::Deref; - use lokui::components::button::button; -// use lokui::components::number::number; use lokui::components::pane::{pane, Pane}; use lokui::layout::{Anchor, DimScalar, Layout, Padding, SolvedLayout}; -// use lokui::components::text::text; use lokui::lazy::Laz; use lokui::widget::{Event, Widget}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::Color; -struct Counter { - value: Laz, - layout: Layout, - inner: Pane, -} - -impl Counter { - fn new() -> Self { - let value = Laz::new(0); - - let increment = { - let value = value.clone(); - move |_, _| value.set(value.get() + 1) - }; - - // let decrement = { - // let value = value.clone(); - // move || value.set(value.get() - 1) - // }; - - let inner = pane() - .with_padding(Padding::splat(10.)) - .with_layout( - Layout::new() - .with_anchor(Anchor::CENTER) - .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), - ) - // .child(text(Lazy::new("Count: "))) - // .child(number(value.clone())) - .child( - pane() - .with_padding(Padding::vh(5., 30.)) - .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) - .child(button("+1").on_click(increment)), - // .child(button("-1").on_click(decrement)), - ); - - Counter { - value, - inner, - layout: Layout::new(), - } - } - - fn value(&self) -> &Laz { - &self.value - } -} - -impl Deref for Counter { - type Target = Pane; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl Widget for Counter { - fn handle_event(&mut self, event: Event) -> bool { - self.inner.handle_event(event) - } - - fn layout(&self) -> &Layout { - &self.layout - } - - fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - self.inner.solve_layout(parent_layout) - } - - fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { - self.inner.draw(skia_ctx, layout); - } +fn counter() -> Pane { + let value = Laz::new(0); + + let increment = { + let value = value.clone(); + move |_, _| value.set(value.get() + 1) + }; + + let decrement = { move |_, _| value.set(value.get() - 1) }; + + pane() + .with_padding(Padding::splat(10.)) + .with_layout( + Layout::new() + .with_anchor(Anchor::CENTER) + .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), + ) + .child( + pane() + .with_padding(Padding::vh(5., 30.)) + .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) + .child( + button("+1") + .with_layout( + Layout::new() + .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_anchor(Anchor::TOP_RIGHT), + ) + .on_click(increment), + ) + .child( + button("-1") + .with_layout( + Layout::new() + .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_anchor(Anchor::BOTTOM_RIGHT), + ) + .on_click(decrement), + ), + ) } struct Stage { - counter: Counter, + root_pane: Pane, + root_layout: SolvedLayout, window_layout: SolvedLayout, - counter_layout: SolvedLayout, } impl EventHandler for Stage { fn update(&mut self, _skia_ctx: &mut SkiaContext) {} + fn mouse_button_up_event( + &mut self, + _skia_ctx: &mut SkiaContext, + _button: miniquad::MouseButton, + x: f32, + y: f32, + ) { + self.root_pane + .handle_event(Event::Clicked(x, y), &self.root_layout); + } + + fn resize_event(&mut self, skia_ctx: &mut SkiaContext, width: f32, height: f32) { + self.window_layout = SolvedLayout::from_top_left(0., 0., width, height); + self.root_layout = self.root_pane.solve_layout(&self.window_layout); + skia_ctx.recreate_surface(width as i32, height as i32); + } + fn draw(&mut self, skia_ctx: &mut SkiaContext) { + println!("drawing from window {:?} [", &self.window_layout); + let canvas = &mut skia_ctx.surface.canvas(); canvas.clear(Color::from(0xff_161a1d)); - self.counter.draw(skia_ctx, &self.counter_layout); + self.root_pane.draw(skia_ctx, &self.root_layout); skia_ctx.dctx.flush(None); + + println!("]"); } } fn main() { - let mut counter = Counter::new(); - let window_layout = - SolvedLayout::from_top_left(0., 0., 1280., 720.).padded(Padding::splat(20.)); - let counter_layout = counter.solve_layout(&window_layout); + let mut root_pane = counter(); + let window_layout = SolvedLayout::from_top_left(0., 0., 1280., 720.); + let root_layout = root_pane.solve_layout(&window_layout); miniquad::start( conf::Conf { high_dpi: true, window_width: 1280, window_height: 720, - window_resizable: false, + // window_resizable: false, window_title: "Lokui GUI Framework Prototype".to_owned(), ..Default::default() }, move || { Box::new(Stage { - counter, + root_pane, + root_layout, window_layout, - counter_layout, }) }, ); diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index d65511b..1b3967e 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -14,6 +14,11 @@ pub struct Button { } impl Button { + pub fn with_layout(mut self, layout: Layout) -> Self { + self.layout = layout; + self + } + pub fn on_click(mut self, on_click: impl FnMut(f32, f32) + 'static) -> Self { self.on_click = Some(Box::new(on_click)); self @@ -60,23 +65,29 @@ impl Widget for Button { canvas.draw_rect(rect, &paint); } - fn handle_event(&mut self, event: Event) -> bool { + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { if self.enabled.get() { match event { Event::Clicked(x, y) => { - if let Some(on_click) = self.on_click.as_mut() { - (on_click)(x, y); + if layout.contains(x, y) { + if let Some(on_click) = self.on_click.as_mut() { + println!("clicked button {:?}", &self.text); + (on_click)(x, y); + } + true + } else { + false } } Event::HoverStart => { self.hovered = true; + true } Event::HoverEnd => { self.hovered = false; + false } } - - true } else { false } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 6b1f146..46edd37 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,5 +1,5 @@ use miniquad::skia::SkiaContext; -use skia_safe::{Rect, Paint, Color}; +use skia_safe::{Color, Paint, Rect}; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use crate::widget::{Event, Widget}; @@ -70,13 +70,14 @@ impl Widget for Pane { if let Some(_auto_layout) = &self.flex_layout { // With auto-layout, all children are placed next to each other, vertically or horizontally. + // for child in &mut self.children {} todo!("no auto-layout for now"); - // for child in &mut self.children {} } else { // Without auto-layout, all children are superposed to each other. for child in &mut self.children { child.solved_layout = child.widget.solve_layout(&inner_layout); + println!("solved child layout: {:?}", &child.solved_layout); } } @@ -153,22 +154,28 @@ impl Widget for Pane { paint.set_color(Color::from(0xff_00cc51)); canvas.draw_rect(rect, &paint); - println!("["); for child in &self.children { - println!(" drawing child with solved layout {:?}", &child.solved_layout); + println!(" drawing child with {:?}", &child.solved_layout); child.widget.draw(skia_ctx, &child.solved_layout); } - println!("]"); } - fn handle_event(&mut self, event: Event) -> bool { + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { let mut handled = false; - for child in &mut self.children { - handled |= child.widget.handle_event(event); + let should_handle = match event { + Event::Clicked(x, y) => layout.contains(x, y), + _ => true, + }; - if handled { - break; + if should_handle { + println!("we do handle the event"); + for child in &mut self.children { + handled |= child.widget.handle_event(event, &child.solved_layout); + + if handled { + break; + } } } diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 2c5c568..45f7b9d 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -41,5 +41,5 @@ pub trait Widget { } fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout); - fn handle_event(&mut self, event: Event) -> bool; + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool; } From 3a73fb41e272ef773e948fd37cbe0124fd42366a Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 09:54:41 +0200 Subject: [PATCH 04/49] Turn playground into counter example --- lokui/examples/{playground.rs => counter.rs} | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) rename lokui/examples/{playground.rs => counter.rs} (92%) diff --git a/lokui/examples/playground.rs b/lokui/examples/counter.rs similarity index 92% rename from lokui/examples/playground.rs rename to lokui/examples/counter.rs index fd10ee7..5e9bac0 100644 --- a/lokui/examples/playground.rs +++ b/lokui/examples/counter.rs @@ -14,10 +14,18 @@ fn counter() -> Pane { let increment = { let value = value.clone(); - move |_, _| value.set(value.get() + 1) + move |_, _| { + value.set(value.get() + 1); + println!("+1! Counter = {}", value.get()); + } }; - let decrement = { move |_, _| value.set(value.get() - 1) }; + let decrement = { + move |_, _| { + value.set(value.get() - 1); + println!("-1! Counter = {}", value.get()); + } + }; pane() .with_padding(Padding::splat(10.)) From 432a955fad20265f16acbab7ee47c209e7222e6d Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 11:15:53 +0200 Subject: [PATCH 05/49] Do not print while drawing --- lokui/examples/counter.rs | 4 ---- lokui/src/components/pane.rs | 1 - 2 files changed, 5 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 5e9bac0..c88181d 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -86,16 +86,12 @@ impl EventHandler for Stage { } fn draw(&mut self, skia_ctx: &mut SkiaContext) { - println!("drawing from window {:?} [", &self.window_layout); - let canvas = &mut skia_ctx.surface.canvas(); canvas.clear(Color::from(0xff_161a1d)); self.root_pane.draw(skia_ctx, &self.root_layout); skia_ctx.dctx.flush(None); - - println!("]"); } } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 46edd37..8c28894 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -155,7 +155,6 @@ impl Widget for Pane { canvas.draw_rect(rect, &paint); for child in &self.children { - println!(" drawing child with {:?}", &child.solved_layout); child.widget.draw(skia_ctx, &child.solved_layout); } } From acd189a7c5d52dc226ebdb989cfa9081712376fa Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 11:57:49 +0200 Subject: [PATCH 06/49] Stop printing while solving layout --- lokui/src/components/pane.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 8c28894..694c45a 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -77,7 +77,6 @@ impl Widget for Pane { for child in &mut self.children { child.solved_layout = child.widget.solve_layout(&inner_layout); - println!("solved child layout: {:?}", &child.solved_layout); } } From 502494e61fbdf7ddb0bfcd2bedcccf424b33b1e4 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 11:58:37 +0200 Subject: [PATCH 07/49] Add `debug` function to `Widget` trait --- lokui/examples/counter.rs | 6 ++++++ lokui/src/components/button.rs | 7 +++++++ lokui/src/components/pane.rs | 11 +++++++++++ lokui/src/lib.rs | 4 ++++ lokui/src/widget.rs | 6 ++++++ 5 files changed, 34 insertions(+) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index c88181d..6c305b3 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -1,5 +1,7 @@ #![allow(clippy::unusual_byte_groupings)] +use std::io::{BufWriter, stdout, Write}; + use lokui::components::button::button; use lokui::components::pane::{pane, Pane}; use lokui::layout::{Anchor, DimScalar, Layout, Padding, SolvedLayout}; @@ -100,6 +102,10 @@ fn main() { let window_layout = SolvedLayout::from_top_left(0., 0., 1280., 720.); let root_layout = root_pane.solve_layout(&window_layout); + let mut writer = BufWriter::new(stdout()); + root_pane.debug(&mut writer, 0).unwrap(); + writer.flush().unwrap(); + miniquad::start( conf::Conf { high_dpi: true, diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 1b3967e..b7b7238 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,6 +1,9 @@ +use std::io; + use miniquad::skia::SkiaContext; use skia_safe::{Color, Paint, Rect}; +use crate::indentation; use crate::layout::{Layout, SolvedLayout}; use crate::lazy::Laz; use crate::widget::{Event, Widget}; @@ -42,6 +45,10 @@ impl Widget for Button { 15. } + fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { + writeln!(w, "{}", indentation(deepness), &self.text) + } + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { let canvas = skia_ctx.surface.canvas(); diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 694c45a..3ba8e01 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,6 +1,9 @@ +use std::io; + use miniquad::skia::SkiaContext; use skia_safe::{Color, Paint, Rect}; +use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use crate::widget::{Event, Widget}; @@ -131,6 +134,14 @@ impl Widget for Pane { inner_min_height + height_pad } + fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { + writeln!(w, "{}", indentation(deepness))?; + for child in &self.children { + child.widget.debug(w, deepness + 1)?; + } + writeln!(w, "{}", indentation(deepness)) + } + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { let canvas = skia_ctx.surface.canvas(); diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index 1aba7e4..c911e7e 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -4,3 +4,7 @@ pub mod components; pub mod layout; pub mod lazy; pub mod widget; + +pub fn indentation(n: usize) -> String { + " ".repeat(n) +} diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 45f7b9d..3c792f6 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -1,3 +1,5 @@ +use std::io; + use miniquad::skia::SkiaContext; use crate::layout::{DimScalar, Layout, SolvedLayout}; @@ -40,6 +42,10 @@ pub trait Widget { 0. } + fn debug(&self, _w: &mut dyn io::Write, _deepness: usize) -> io::Result<()> { + Ok(()) + } + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout); fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool; } From 6ebb16a713a84bc33e6d7e5e1144c3661f9d7377 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 13:21:43 +0200 Subject: [PATCH 08/49] Simplify `Anchor` --- lokui/src/layout/anchor.rs | 99 ++++++++------------------------------ 1 file changed, 19 insertions(+), 80 deletions(-) diff --git a/lokui/src/layout/anchor.rs b/lokui/src/layout/anchor.rs index 19a82ca..c37ed50 100644 --- a/lokui/src/layout/anchor.rs +++ b/lokui/src/layout/anchor.rs @@ -1,86 +1,25 @@ use super::SolvedLayout; -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] -pub enum HAnchor { - Left, - #[default] - Center, - Right, -} - -impl HAnchor { - pub fn calc_x_offset(&self, inner_width: f32, outer_width: f32) -> f32 { - match self { - HAnchor::Left => 0., - HAnchor::Center => (outer_width - inner_width) / 2., - HAnchor::Right => outer_width - inner_width, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] -pub enum VAnchor { - Top, - #[default] - Center, - Bottom, -} - -impl VAnchor { - pub fn calc_y_offset(&self, inner_height: f32, outer_height: f32) -> f32 { - match self { - VAnchor::Top => 0., - VAnchor::Center => (outer_height - inner_height) / 2., - VAnchor::Bottom => outer_height - inner_height, - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] +#[derive(Clone, Copy, Debug, Default)] pub struct Anchor { - pub v: VAnchor, - pub h: HAnchor, + pub x: f32, + pub y: f32, } impl Anchor { - pub const TOP_LEFT: Anchor = Anchor { - v: VAnchor::Top, - h: HAnchor::Left, - }; - pub const TOP_CENTER: Anchor = Anchor { - v: VAnchor::Top, - h: HAnchor::Center, - }; - pub const TOP_RIGHT: Anchor = Anchor { - v: VAnchor::Top, - h: HAnchor::Right, - }; - - pub const CENTER_LEFT: Anchor = Anchor { - v: VAnchor::Center, - h: HAnchor::Left, - }; - pub const CENTER: Anchor = Anchor { - v: VAnchor::Center, - h: HAnchor::Center, - }; - pub const CENTER_RIGHT: Anchor = Anchor { - v: VAnchor::Center, - h: HAnchor::Right, - }; + pub const fn new(x: f32, y: f32) -> Self { + Self { x, y } + } - pub const BOTTOM_LEFT: Anchor = Anchor { - v: VAnchor::Bottom, - h: HAnchor::Left, - }; - pub const BOTTOM_CENTER: Anchor = Anchor { - v: VAnchor::Bottom, - h: HAnchor::Center, - }; - pub const BOTTOM_RIGHT: Anchor = Anchor { - v: VAnchor::Bottom, - h: HAnchor::Right, - }; + pub const TOP_LEFT: Anchor = Anchor::new(0.0, 0.0); + pub const TOP_CENTER: Anchor = Anchor::new(0.5, 0.0); + pub const TOP_RIGHT: Anchor = Anchor::new(1.0, 0.0); + pub const CENTER_LEFT: Anchor = Anchor::new(0.0, 0.5); + pub const CENTER: Anchor = Anchor::new(0.5, 0.5); + pub const CENTER_RIGHT: Anchor = Anchor::new(1.0, 0.5); + pub const BOTTOM_LEFT: Anchor = Anchor::new(0.0, 1.0); + pub const BOTTOM_CENTER: Anchor = Anchor::new(0.5, 1.0); + pub const BOTTOM_RIGHT: Anchor = Anchor::new(1.0, 1.0); pub fn calc_child_abs_pos( &self, @@ -88,12 +27,12 @@ impl Anchor { height: f32, parent_layout: &SolvedLayout, ) -> SolvedLayout { - let x = self.h.calc_x_offset(width, parent_layout.width()); - let y = self.v.calc_y_offset(height, parent_layout.height()); + let x_offset = (parent_layout.width() - width) * self.x; + let y_offset = (parent_layout.height() - height) * self.y; SolvedLayout { - x: parent_layout.x + x, - y: parent_layout.y + y, + x: parent_layout.x + x_offset, + y: parent_layout.y + y_offset, width, height, } From 38d06cad3e704a63926c94762e3ce190e51aef65 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 13:47:08 +0200 Subject: [PATCH 09/49] flex --- lokui/src/components/pane.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 3ba8e01..597cc1b 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -70,13 +70,13 @@ impl Widget for Pane { let layout = self.default_solve_layout(parent_layout); let inner_layout = layout.padded(self.padding); - if let Some(_auto_layout) = &self.flex_layout { - // With auto-layout, all children are placed next to each other, vertically or horizontally. + if let Some(_flex_layout) = &self.flex_layout { + // With flex-layout, all children are placed next to each other, vertically or horizontally. // for child in &mut self.children {} - todo!("no auto-layout for now"); + todo!("no flex-layout for now"); } else { - // Without auto-layout, all children are superposed to each other. + // Without flex-layout, all children are superposed to each other. for child in &mut self.children { child.solved_layout = child.widget.solve_layout(&inner_layout); @@ -89,8 +89,8 @@ impl Widget for Pane { fn min_width(&mut self) -> f32 { let width_pad = self.padding.left + self.padding.right; - if let Some(auto_layout) = self.flex_layout.as_ref() { - if auto_layout.direction == Direction::Horizontal { + if let Some(flex_layout) = self.flex_layout.as_ref() { + if flex_layout.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter_mut()) .map(|child| match child.widget.layout().width { DimScalar::Fixed(w) => w, @@ -113,8 +113,8 @@ impl Widget for Pane { fn min_height(&mut self) -> f32 { let height_pad = self.padding.top + self.padding.bottom; - if let Some(auto_layout) = self.flex_layout.as_ref() { - if auto_layout.direction == Direction::Vertical { + if let Some(flex_layout) = self.flex_layout.as_ref() { + if flex_layout.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter_mut()) .map(|child| match child.widget.layout().height { DimScalar::Fixed(h) => h, From ed737bbf13fde843cbd3cb4a59f9d9f876dfcb1a Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 14:17:36 +0200 Subject: [PATCH 10/49] Remove other useless prints --- lokui/src/components/button.rs | 1 - lokui/src/components/pane.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index b7b7238..bc503e0 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -78,7 +78,6 @@ impl Widget for Button { Event::Clicked(x, y) => { if layout.contains(x, y) { if let Some(on_click) = self.on_click.as_mut() { - println!("clicked button {:?}", &self.text); (on_click)(x, y); } true diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 597cc1b..585f23b 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -178,7 +178,6 @@ impl Widget for Pane { }; if should_handle { - println!("we do handle the event"); for child in &mut self.children { handled |= child.widget.handle_event(event, &child.solved_layout); From e9dc989bb1ba50b3c9eb27b1966004e0cf59904b Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 14:18:31 +0200 Subject: [PATCH 11/49] Get anchor-origin system right --- lokui/examples/counter.rs | 4 +++- lokui/src/layout/anchor.rs | 8 +++++++- lokui/src/layout/mod.rs | 35 ++++++++++++++++++++++++++++------- lokui/src/widget.rs | 3 ++- 4 files changed, 40 insertions(+), 10 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 6c305b3..2114160 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -1,6 +1,6 @@ #![allow(clippy::unusual_byte_groupings)] -use std::io::{BufWriter, stdout, Write}; +use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; use lokui::components::pane::{pane, Pane}; @@ -45,6 +45,7 @@ fn counter() -> Pane { .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_origin(Anchor::TOP_RIGHT) .with_anchor(Anchor::TOP_RIGHT), ) .on_click(increment), @@ -54,6 +55,7 @@ fn counter() -> Pane { .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_origin(Anchor::BOTTOM_RIGHT) .with_anchor(Anchor::BOTTOM_RIGHT), ) .on_click(decrement), diff --git a/lokui/src/layout/anchor.rs b/lokui/src/layout/anchor.rs index c37ed50..5f3379d 100644 --- a/lokui/src/layout/anchor.rs +++ b/lokui/src/layout/anchor.rs @@ -1,6 +1,6 @@ use super::SolvedLayout; -#[derive(Clone, Copy, Debug, Default)] +#[derive(Clone, Copy, Debug)] pub struct Anchor { pub x: f32, pub y: f32, @@ -38,3 +38,9 @@ impl Anchor { } } } + +impl Default for Anchor { + fn default() -> Self { + Self::CENTER + } +} diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs index 1860078..49cc9cb 100644 --- a/lokui/src/layout/mod.rs +++ b/lokui/src/layout/mod.rs @@ -91,6 +91,10 @@ impl SolvedLayout { } } + pub fn from_origin(origin: Anchor, x: f32, y: f32, width: f32, height: f32) -> Self { + Self::from_top_left(x, y, width, height).anchored(origin) + } + pub fn from_2_points(xa: f32, ya: f32, xb: f32, yb: f32) -> Self { Self { x: xa.min(xb), @@ -100,6 +104,18 @@ impl SolvedLayout { } } + pub fn x_at_anchor(&self, anchor: Anchor) -> f32 { + self.x + self.width * anchor.x + } + + pub fn y_at_anchor(&self, anchor: Anchor) -> f32 { + self.y + self.height * anchor.y + } + + pub fn point_at_anchor(&self, anchor: Anchor) -> (f32, f32) { + (self.x_at_anchor(anchor), self.y_at_anchor(anchor)) + } + pub fn x_start(&self) -> f32 { self.x } @@ -136,13 +152,18 @@ impl SolvedLayout { self.contains_x(x) && self.contains_y(y) } - pub fn padded(&self, padding: Padding) -> Self { - Self { - x: self.x + padding.left, - y: self.y + padding.top, - width: self.width - padding.left - padding.right, - height: self.height - padding.top - padding.bottom, - } + pub fn padded(mut self, padding: Padding) -> Self { + self.x += padding.left; + self.y += padding.top; + self.width -= padding.left + padding.right; + self.height -= padding.top + padding.bottom; + self + } + + pub fn anchored(mut self, anchor: Anchor) -> Self { + self.x -= self.width * anchor.x; + self.y -= self.height * anchor.y; + self } } diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 3c792f6..6989b60 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -27,7 +27,8 @@ pub trait Widget { DimScalar::Fixed(h) => h, }; - (self.layout().anchor).calc_child_abs_pos(width, height, parent_layout) + let (x, y) = parent_layout.point_at_anchor(self.layout().anchor); + SolvedLayout::from_origin(self.layout().origin, x, y, width, height) } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout; From b88dd1ef7281addacd89e4facd8efe21e10b0fe8 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 14:25:34 +0200 Subject: [PATCH 12/49] Consistency in placement of constructor functions --- lokui/src/components/button.rs | 20 ++++++++++---------- lokui/src/components/pane.rs | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index bc503e0..2cd0ce3 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -16,6 +16,16 @@ pub struct Button { hovered: bool, } +pub fn button(text: impl Into) -> Button { + Button { + layout: Layout::new(), + text: text.into(), + on_click: None, + enabled: Laz::new(true), + hovered: false, + } +} + impl Button { pub fn with_layout(mut self, layout: Layout) -> Self { self.layout = layout; @@ -99,13 +109,3 @@ impl Widget for Button { } } } - -pub fn button(text: impl Into) -> Button { - Button { - layout: Layout::new(), - text: text.into(), - on_click: None, - enabled: Laz::new(true), - hovered: false, - } -} diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 585f23b..b3ef493 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -20,6 +20,10 @@ pub struct Pane { children: Vec, } +pub fn pane() -> Pane { + Pane::default() +} + impl Pane { pub fn with_layout(mut self, layout: Layout) -> Self { self.layout = layout; @@ -57,10 +61,6 @@ impl Pane { } } -pub fn pane() -> Pane { - Pane::default() -} - impl Widget for Pane { fn layout(&self) -> &Layout { &self.layout From 25fce4f724e04e1f9090e2df13baea6fdb96e4a3 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 16:55:23 +0200 Subject: [PATCH 13/49] Implement text rendering Counter is now fully functional! :D --- lokui/examples/counter.rs | 22 +++++++--- lokui/src/components/button.rs | 38 +++++++++++------ lokui/src/components/mod.rs | 3 +- lokui/src/components/number.rs | 26 ------------ lokui/src/components/text.rs | 74 +++++++++++++++++++++++++++++----- lokui/src/layout/mod.rs | 4 ++ lokui/src/lazy.rs | 15 +++++++ 7 files changed, 126 insertions(+), 56 deletions(-) delete mode 100644 lokui/src/components/number.rs diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 2114160..5923c48 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -4,15 +4,16 @@ use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; use lokui::components::pane::{pane, Pane}; +use lokui::components::text::text; use lokui::layout::{Anchor, DimScalar, Layout, Padding, SolvedLayout}; -use lokui::lazy::Laz; +use lokui::lazy::{laz, lazy}; use lokui::widget::{Event, Widget}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; -use skia_safe::Color; +use skia_safe::{Color, Font, FontStyle, Typeface}; fn counter() -> Pane { - let value = Laz::new(0); + let value = laz(0); let increment = { let value = value.clone(); @@ -23,12 +24,16 @@ fn counter() -> Pane { }; let decrement = { + let value = value.clone(); move |_, _| { value.set(value.get() - 1); println!("-1! Counter = {}", value.get()); } }; + let typeface = Typeface::new("Torus Pro", FontStyle::normal()).unwrap(); + let font = lazy(Font::new(typeface, Some(20.))); + pane() .with_padding(Padding::splat(10.)) .with_layout( @@ -41,7 +46,14 @@ fn counter() -> Pane { .with_padding(Padding::vh(5., 30.)) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) .child( - button("+1") + text(value, font.clone()).with_layout( + Layout::hug() + .with_origin(Anchor::CENTER_LEFT) + .with_anchor(Anchor::CENTER_LEFT), + ), + ) + .child( + button(text("+1", font.clone())) .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) @@ -51,7 +63,7 @@ fn counter() -> Pane { .on_click(increment), ) .child( - button("-1") + button(text("-1", font)) .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 2cd0ce3..b85327d 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,32 +1,37 @@ +use std::fmt::Display; use std::io; use miniquad::skia::SkiaContext; use skia_safe::{Color, Paint, Rect}; use crate::indentation; -use crate::layout::{Layout, SolvedLayout}; +use crate::layout::{Layout, Padding, SolvedLayout}; use crate::lazy::Laz; use crate::widget::{Event, Widget}; -pub struct Button { +use super::text::Text; + +pub struct Button { layout: Layout, - text: String, + text_layout: SolvedLayout, + text: Text, on_click: Option>, enabled: Laz, hovered: bool, } -pub fn button(text: impl Into) -> Button { +pub fn button(text: Text) -> Button { Button { - layout: Layout::new(), - text: text.into(), + layout: Layout::hug(), + text_layout: SolvedLayout::default(), + text, on_click: None, enabled: Laz::new(true), hovered: false, } } -impl Button { +impl Button { pub fn with_layout(mut self, layout: Layout) -> Self { self.layout = layout; self @@ -38,25 +43,32 @@ impl Button { } } -impl Widget for Button { +impl Widget for Button { fn layout(&self) -> &Layout { &self.layout } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - self.default_solve_layout(parent_layout) + let layout = self.default_solve_layout(parent_layout); + self.text_layout = (self.text).solve_layout(&layout.padded(Padding::vh(5., 10.))); + layout } fn min_width(&mut self) -> f32 { - self.text.len() as f32 * 10. + self.text.min_width() + 10. } fn min_height(&mut self) -> f32 { - 15. + self.text.min_height() + 5. } fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { - writeln!(w, "{}", indentation(deepness), &self.text) + writeln!( + w, + "{}", + indentation(deepness), + self.text.text(), + ) } fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { @@ -80,6 +92,8 @@ impl Widget for Button { paint.set_stroke(true); paint.set_color(Color::from(0xff_ff0051)); canvas.draw_rect(rect, &paint); + + self.text.draw(skia_ctx, &self.text_layout); } fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index d907d8d..d3ada0d 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,4 +1,3 @@ pub mod button; -// pub mod number; pub mod pane; -// pub mod text; +pub mod text; diff --git a/lokui/src/components/number.rs b/lokui/src/components/number.rs deleted file mode 100644 index 29c0995..0000000 --- a/lokui/src/components/number.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::layout::{Layout, SolvedLayout}; -use crate::lazy::Laz; -use crate::widget::{Event, Widget}; - -pub struct Number { - layout: Layout, - number: Laz, -} - -// pub fn number(val: Laz) -> Number { -// Number(val) -// } - -impl Widget for Number { - fn layout(&self) -> &Layout { - &self.layout - } - - fn draw(&self, layout: SolvedLayout) { - // TODO: draw number as text? - } - - fn update(&mut self, _event: Event) -> bool { - false - } -} diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index a680045..8958283 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -1,26 +1,78 @@ -use crate::layout::{SolvedLayout, Layout}; +use std::fmt::Display; + +use miniquad::skia::SkiaContext; +use skia_safe::{Color, Font, Paint, Rect}; + +use crate::layout::{Anchor, Layout, SolvedLayout}; use crate::lazy::Lazy; use crate::widget::{Event, Widget}; -// pub fn text>(val: Lazy) -> Text { -// Text(val) -// } - -pub struct Text> { +pub struct Text { layout: Layout, - text: Lazy, + font: Lazy, + text: T, + min_bounds: Rect, +} + +pub fn text(text: T, font: Lazy) -> Text { + Text { + layout: Layout::hug(), + font, + text, + min_bounds: Rect::default(), + } +} + +impl Text { + pub fn with_layout(mut self, layout: Layout) -> Self { + self.layout = layout; + self + } + + pub fn text(&self) -> &T { + &self.text + } } -impl> Widget for Text { +impl Widget for Text { fn layout(&self) -> &Layout { &self.layout } - fn draw(&self, layout: SolvedLayout) { - // TODO: draw text? + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.min_bounds = (self.font.get()) + .measure_str(format!("{}", &self.text), None) + .1; + + self.default_solve_layout(parent_layout) + } + + fn min_width(&mut self) -> f32 { + self.min_bounds.width() + } + + fn min_height(&mut self) -> f32 { + self.min_bounds.height() + } + + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { + let canvas = skia_ctx.surface.canvas(); + + let mut paint = Paint::default(); + paint.set_anti_alias(true); + paint.set_color(Color::from(0xff_ffffff)); + + let (x, y) = layout.point_at_anchor(Anchor::TOP_LEFT); + + canvas.draw_str( + format!("{}", &self.text), + (x, y + self.min_bounds.height()), + self.font.get().as_ref(), + &paint, + ); } - fn update(&mut self, _event: Event) -> bool { + fn handle_event(&mut self, _event: Event, _layout: &SolvedLayout) -> bool { false } } diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs index 49cc9cb..97e5980 100644 --- a/lokui/src/layout/mod.rs +++ b/lokui/src/layout/mod.rs @@ -37,6 +37,10 @@ impl Layout { Self::default() } + pub fn hug() -> Self { + Self::new().with_dimension(DimScalar::Hug, DimScalar::Hug) + } + pub fn with_pos(mut self, x: f32, y: f32) -> Self { self.x = x; self.y = y; diff --git a/lokui/src/lazy.rs b/lokui/src/lazy.rs index 4966a61..55ca1fe 100644 --- a/lokui/src/lazy.rs +++ b/lokui/src/lazy.rs @@ -1,9 +1,14 @@ use std::cell::{Cell, Ref, RefCell}; +use std::fmt; use std::ops::Deref; use std::rc::Rc; pub struct Lazy(Rc>); +pub fn lazy(val: T) -> Lazy { + Lazy::new(val) +} + impl Lazy { pub fn new(val: T) -> Self { Lazy(Rc::new(RefCell::new(val))) @@ -26,6 +31,10 @@ impl Clone for Lazy { pub struct Laz(Rc>); +pub fn laz(val: T) -> Laz { + Laz::new(val) +} + impl Laz { pub fn new(val: T) -> Self { Laz(Rc::new(Cell::new(val))) @@ -45,3 +54,9 @@ impl Deref for Laz { self.0.as_ref() } } + +impl fmt::Display for Laz { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} From ad39404b057422e31e6ff973fa5078e991535cde Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 17 Apr 2023 17:10:49 +0200 Subject: [PATCH 14/49] Implement `Display` for `Lazy` --- lokui/src/lazy.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lokui/src/lazy.rs b/lokui/src/lazy.rs index 55ca1fe..c5fa41a 100644 --- a/lokui/src/lazy.rs +++ b/lokui/src/lazy.rs @@ -29,6 +29,12 @@ impl Clone for Lazy { } } +impl fmt::Display for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + pub struct Laz(Rc>); pub fn laz(val: T) -> Laz { From bd6c2b9987787bf2aa82480c687c80fae60a2cce Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 00:28:49 +0200 Subject: [PATCH 15/49] Implement basics of flex layout --- lokui/examples/counter.rs | 30 +++++++----- lokui/src/components/button.rs | 4 +- lokui/src/components/pane.rs | 86 ++++++++++++++++++++++++++++++---- lokui/src/components/text.rs | 24 +++++----- lokui/src/layout/mod.rs | 38 +++++++++++++++ lokui/src/widget.rs | 4 +- 6 files changed, 151 insertions(+), 35 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 5923c48..800c0b0 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -5,7 +5,7 @@ use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; use lokui::components::pane::{pane, Pane}; use lokui::components::text::text; -use lokui::layout::{Anchor, DimScalar, Layout, Padding, SolvedLayout}; +use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use lokui::lazy::{laz, lazy}; use lokui::widget::{Event, Widget}; use miniquad::skia::SkiaContext; @@ -43,22 +43,30 @@ fn counter() -> Pane { ) .child( pane() - .with_padding(Padding::vh(5., 30.)) + .with_padding(Padding::vh(5., 10.)) + .with_flex_layout(FlexLayout { + direction: Direction::Horizontal, + align: Anchor::CENTER_LEFT, + gap: 0., + }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) .child( - text(value, font.clone()).with_layout( - Layout::hug() - .with_origin(Anchor::CENTER_LEFT) - .with_anchor(Anchor::CENTER_LEFT), - ), + pane() + .with_layout( + Layout::new() + .with_dimension(DimScalar::Fixed(120.), DimScalar::Fixed(50.)) + .with_origin(Anchor::CENTER) + .with_anchor(Anchor::CENTER), + ) + .child(text(value, font.clone()).with_layout(Layout::hug())), ) .child( button(text("+1", font.clone())) .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) - .with_origin(Anchor::TOP_RIGHT) - .with_anchor(Anchor::TOP_RIGHT), + .with_origin(Anchor::CENTER_LEFT) + .with_anchor(Anchor::CENTER_LEFT), ) .on_click(increment), ) @@ -67,8 +75,8 @@ fn counter() -> Pane { .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) - .with_origin(Anchor::BOTTOM_RIGHT) - .with_anchor(Anchor::BOTTOM_RIGHT), + .with_origin(Anchor::CENTER_LEFT) + .with_anchor(Anchor::CENTER_LEFT), ) .on_click(decrement), ), diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index b85327d..944d636 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -54,11 +54,11 @@ impl Widget for Button { layout } - fn min_width(&mut self) -> f32 { + fn min_width(&self) -> f32 { self.text.min_width() + 10. } - fn min_height(&mut self) -> f32 { + fn min_height(&self) -> f32 { self.text.min_height() + 5. } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index b3ef493..44d53ae 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -70,11 +70,79 @@ impl Widget for Pane { let layout = self.default_solve_layout(parent_layout); let inner_layout = layout.padded(self.padding); - if let Some(_flex_layout) = &self.flex_layout { + if let Some(flex_layout) = &self.flex_layout { // With flex-layout, all children are placed next to each other, vertically or horizontally. - // for child in &mut self.children {} - todo!("no flex-layout for now"); + match flex_layout.direction { + Direction::Horizontal => { + let fills_count = (self.children.iter()) + .filter(|child| child.widget.layout().width.is_fill()) + .count(); + + let filling_width = if fills_count == 0 { + 0. + } else { + let fixed_width: f32 = (self.children.iter()) + .filter_map(|child| match child.widget.layout().width { + DimScalar::Fill => None, + DimScalar::Hug => Some(child.widget.min_width()), + DimScalar::Fixed(w) => Some(w), + }) + .sum(); + + let leftover_width = (inner_layout.width() - fixed_width).max(0.); + leftover_width / fills_count as f32 + }; + + let mut x = inner_layout.x_start(); + for child in &mut self.children { + let child_width = match child.widget.layout().width { + DimScalar::Fill => filling_width, + DimScalar::Hug => child.widget.min_width(), + DimScalar::Fixed(w) => w, + }; + + let inner_layout = inner_layout.with_width(child_width).with_x(x); + child.solved_layout = child.widget.solve_layout(&inner_layout); + + x += child_width; + } + } + Direction::Vertical => { + let fills_count = (self.children.iter()) + .filter(|child| child.widget.layout().height.is_fill()) + .count(); + + let filling_height = if fills_count == 0 { + 0. + } else { + let fixed_height: f32 = (self.children.iter()) + .filter_map(|child| match child.widget.layout().height { + DimScalar::Fill => None, + DimScalar::Hug => Some(child.widget.min_height()), + DimScalar::Fixed(w) => Some(w), + }) + .sum(); + + let leftover_height = (inner_layout.height() - fixed_height).max(0.); + leftover_height / fills_count as f32 + }; + + let mut y = inner_layout.y_start(); + for child in &mut self.children { + let child_height = match child.widget.layout().height { + DimScalar::Fill => filling_height, + DimScalar::Hug => child.widget.min_height(), + DimScalar::Fixed(w) => w, + }; + + let inner_layout = inner_layout.with_height(child_height).with_y(y); + child.solved_layout = child.widget.solve_layout(&inner_layout); + + y += child_height; + } + } + } } else { // Without flex-layout, all children are superposed to each other. @@ -86,12 +154,12 @@ impl Widget for Pane { layout } - fn min_width(&mut self) -> f32 { + fn min_width(&self) -> f32 { let width_pad = self.padding.left + self.padding.right; if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Horizontal { - let inner_min_width: f32 = (self.children.iter_mut()) + let inner_min_width: f32 = (self.children.iter()) .map(|child| match child.widget.layout().width { DimScalar::Fixed(w) => w, _ => child.widget.min_width(), @@ -102,7 +170,7 @@ impl Widget for Pane { } } - let inner_min_width = (self.children.iter_mut()) + let inner_min_width = (self.children.iter()) .map(|child| child.widget.min_width()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); @@ -110,12 +178,12 @@ impl Widget for Pane { inner_min_width + width_pad } - fn min_height(&mut self) -> f32 { + fn min_height(&self) -> f32 { let height_pad = self.padding.top + self.padding.bottom; if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Vertical { - let inner_min_height: f32 = (self.children.iter_mut()) + let inner_min_height: f32 = (self.children.iter()) .map(|child| match child.widget.layout().height { DimScalar::Fixed(h) => h, _ => child.widget.min_height(), @@ -126,7 +194,7 @@ impl Widget for Pane { } } - let inner_min_height = (self.children.iter_mut()) + let inner_min_height = (self.children.iter()) .map(|child| child.widget.min_height()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 8958283..b89615c 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -11,7 +11,7 @@ pub struct Text { layout: Layout, font: Lazy, text: T, - min_bounds: Rect, + // min_bounds: Rect, } pub fn text(text: T, font: Lazy) -> Text { @@ -19,7 +19,7 @@ pub fn text(text: T, font: Lazy) -> Text { layout: Layout::hug(), font, text, - min_bounds: Rect::default(), + // min_bounds: Rect::default(), } } @@ -32,6 +32,12 @@ impl Text { pub fn text(&self) -> &T { &self.text } + + pub fn min_bounds(&self) -> Rect { + (self.font.get()) + .measure_str(format!("{}", &self.text), None) + .1 + } } impl Widget for Text { @@ -40,19 +46,15 @@ impl Widget for Text { } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - self.min_bounds = (self.font.get()) - .measure_str(format!("{}", &self.text), None) - .1; - self.default_solve_layout(parent_layout) } - fn min_width(&mut self) -> f32 { - self.min_bounds.width() + fn min_width(&self) -> f32 { + self.min_bounds().width() } - fn min_height(&mut self) -> f32 { - self.min_bounds.height() + fn min_height(&self) -> f32 { + self.min_bounds().height() } fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { @@ -66,7 +68,7 @@ impl Widget for Text { canvas.draw_str( format!("{}", &self.text), - (x, y + self.min_bounds.height()), + (x, y + self.min_bounds().height()), self.font.get().as_ref(), &paint, ); diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs index 97e5980..18d2e31 100644 --- a/lokui/src/layout/mod.rs +++ b/lokui/src/layout/mod.rs @@ -16,6 +16,18 @@ pub enum DimScalar { Fixed(f32), } +impl DimScalar { + pub fn is_fill(&self) -> bool { + matches!(self, Self::Fill) + } + pub fn is_hug(&self) -> bool { + matches!(self, Self::Hug) + } + pub fn is_fixed(&self) -> bool { + matches!(self, Self::Fixed(_)) + } +} + #[derive(Clone, Debug, Default)] pub struct Layout { /// Position offset in pixels on the `x` axis. @@ -169,6 +181,32 @@ impl SolvedLayout { self.y -= self.height * anchor.y; self } + + pub fn with_dimensions(mut self, width: f32, height: f32) -> Self { + self.width = width; + self.height = height; + self + } + + pub fn with_width(mut self, width: f32) -> Self { + self.width = width; + self + } + + pub fn with_height(mut self, height: f32) -> Self { + self.height = height; + self + } + + pub fn with_x(mut self, x: f32) -> Self { + self.x = x; + self + } + + pub fn with_y(mut self, y: f32) -> Self { + self.y = y; + self + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 6989b60..a16a033 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -34,12 +34,12 @@ pub trait Widget { fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout; /// Minimum possible width in case we choose DimScalar::Hug as the layout width. - fn min_width(&mut self) -> f32 { + fn min_width(&self) -> f32 { 0. } /// Minimum possible height in case we choose DimScalar::Hug as the layout height. - fn min_height(&mut self) -> f32 { + fn min_height(&self) -> f32 { 0. } From 29e6b544a48d9a866c0fe8ac482f2ae54da0750a Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 00:44:27 +0200 Subject: [PATCH 16/49] Add a few comments --- lokui/examples/counter.rs | 2 +- lokui/src/components/pane.rs | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 800c0b0..410ebc9 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -54,7 +54,7 @@ fn counter() -> Pane { pane() .with_layout( Layout::new() - .with_dimension(DimScalar::Fixed(120.), DimScalar::Fixed(50.)) + .with_dimension(DimScalar::Fill, DimScalar::Fixed(50.)) .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 44d53ae..a0f7a1e 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -96,6 +96,8 @@ impl Widget for Pane { let mut x = inner_layout.x_start(); for child in &mut self.children { + // Each child is given a slice of the inner layout. + let child_width = match child.widget.layout().width { DimScalar::Fill => filling_width, DimScalar::Hug => child.widget.min_width(), @@ -109,6 +111,9 @@ impl Widget for Pane { } } Direction::Vertical => { + // maybe put this into a function since it's + // copy-pasted from the Horizontal case but for height? + let fills_count = (self.children.iter()) .filter(|child| child.widget.layout().height.is_fill()) .count(); From 4bd9b0e5a7972f0e2e1315486bd8240bbebe4a5b Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 00:47:19 +0200 Subject: [PATCH 17/49] Add `debug` for `Text` component --- lokui/src/components/text.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index b89615c..9bed8e5 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -1,8 +1,10 @@ use std::fmt::Display; +use std::io; use miniquad::skia::SkiaContext; use skia_safe::{Color, Font, Paint, Rect}; +use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; use crate::lazy::Lazy; use crate::widget::{Event, Widget}; @@ -57,6 +59,15 @@ impl Widget for Text { self.min_bounds().height() } + fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { + writeln!( + w, + "{}{}", + indentation(deepness), + &self.text, + ) + } + fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { let canvas = skia_ctx.surface.canvas(); From 46d59e4692a7583e48b6f4c625cd6e967cffe3a0 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 00:55:26 +0200 Subject: [PATCH 18/49] Make `Widget::draw` take a Skia canvas Taking lokinit's SkiaContext was stupid since we only need the canvas anyway. :) --- lokui/examples/counter.rs | 6 ++---- lokui/src/components/button.rs | 9 +++------ lokui/src/components/pane.rs | 9 +++------ lokui/src/components/text.rs | 7 ++----- lokui/src/widget.rs | 3 ++- 5 files changed, 12 insertions(+), 22 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 410ebc9..f33ceb9 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -110,11 +110,9 @@ impl EventHandler for Stage { } fn draw(&mut self, skia_ctx: &mut SkiaContext) { - let canvas = &mut skia_ctx.surface.canvas(); + let canvas = skia_ctx.surface.canvas(); canvas.clear(Color::from(0xff_161a1d)); - - self.root_pane.draw(skia_ctx, &self.root_layout); - + self.root_pane.draw(canvas, &self.root_layout); skia_ctx.dctx.flush(None); } } diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 944d636..c1a0d47 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,8 +1,7 @@ use std::fmt::Display; use std::io; -use miniquad::skia::SkiaContext; -use skia_safe::{Color, Paint, Rect}; +use skia_safe::{Color, Paint, Rect, Canvas}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; @@ -71,9 +70,7 @@ impl Widget for Button { ) } - fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { - let canvas = skia_ctx.surface.canvas(); - + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { let rect = Rect::from_xywh( layout.x_start(), layout.y_start(), @@ -93,7 +90,7 @@ impl Widget for Button { paint.set_color(Color::from(0xff_ff0051)); canvas.draw_rect(rect, &paint); - self.text.draw(skia_ctx, &self.text_layout); + self.text.draw(canvas, &self.text_layout); } fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index a0f7a1e..5594c07 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,7 +1,6 @@ use std::io; -use miniquad::skia::SkiaContext; -use skia_safe::{Color, Paint, Rect}; +use skia_safe::{Color, Paint, Rect, Canvas}; use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; @@ -215,9 +214,7 @@ impl Widget for Pane { writeln!(w, "{}", indentation(deepness)) } - fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { - let canvas = skia_ctx.surface.canvas(); - + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { let rect = Rect::from_xywh( layout.x_start(), layout.y_start(), @@ -238,7 +235,7 @@ impl Widget for Pane { canvas.draw_rect(rect, &paint); for child in &self.children { - child.widget.draw(skia_ctx, &child.solved_layout); + child.widget.draw(canvas, &child.solved_layout); } } diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 9bed8e5..7d3f1d4 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -1,8 +1,7 @@ use std::fmt::Display; use std::io; -use miniquad::skia::SkiaContext; -use skia_safe::{Color, Font, Paint, Rect}; +use skia_safe::{Color, Font, Paint, Rect, Canvas}; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; @@ -68,9 +67,7 @@ impl Widget for Text { ) } - fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout) { - let canvas = skia_ctx.surface.canvas(); - + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { let mut paint = Paint::default(); paint.set_anti_alias(true); paint.set_color(Color::from(0xff_ffffff)); diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index a16a033..ea2a7b5 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -1,6 +1,7 @@ use std::io; use miniquad::skia::SkiaContext; +use skia_safe::Canvas; use crate::layout::{DimScalar, Layout, SolvedLayout}; @@ -47,6 +48,6 @@ pub trait Widget { Ok(()) } - fn draw(&self, skia_ctx: &mut SkiaContext, layout: &SolvedLayout); + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout); fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool; } From 008451f9cd8b8a642944c595490ed520a214cfed Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 00:59:55 +0200 Subject: [PATCH 19/49] small format and clean --- lokui/examples/counter.rs | 1 - lokui/src/components/button.rs | 2 +- lokui/src/components/pane.rs | 2 +- lokui/src/components/text.rs | 11 ++--------- lokui/src/widget.rs | 1 - 5 files changed, 4 insertions(+), 13 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index f33ceb9..a4aacb9 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -131,7 +131,6 @@ fn main() { high_dpi: true, window_width: 1280, window_height: 720, - // window_resizable: false, window_title: "Lokui GUI Framework Prototype".to_owned(), ..Default::default() }, diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index c1a0d47..379627b 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use std::io; -use skia_safe::{Color, Paint, Rect, Canvas}; +use skia_safe::{Canvas, Color, Paint, Rect}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 5594c07..60fb054 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,6 +1,6 @@ use std::io; -use skia_safe::{Color, Paint, Rect, Canvas}; +use skia_safe::{Canvas, Color, Paint, Rect}; use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 7d3f1d4..06a7d67 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use std::io; -use skia_safe::{Color, Font, Paint, Rect, Canvas}; +use skia_safe::{Canvas, Color, Font, Paint, Rect}; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; @@ -12,7 +12,6 @@ pub struct Text { layout: Layout, font: Lazy, text: T, - // min_bounds: Rect, } pub fn text(text: T, font: Lazy) -> Text { @@ -20,7 +19,6 @@ pub fn text(text: T, font: Lazy) -> Text { layout: Layout::hug(), font, text, - // min_bounds: Rect::default(), } } @@ -59,12 +57,7 @@ impl Widget for Text { } fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { - writeln!( - w, - "{}{}", - indentation(deepness), - &self.text, - ) + writeln!(w, "{}{}", indentation(deepness), &self.text) } fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index ea2a7b5..ce06bd7 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -1,6 +1,5 @@ use std::io; -use miniquad::skia::SkiaContext; use skia_safe::Canvas; use crate::layout::{DimScalar, Layout, SolvedLayout}; From 8726f8bb838ee4a62a5af3051f93ed83709958c2 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 01:29:32 +0200 Subject: [PATCH 20/49] That wasn't needed --- lokui/examples/counter.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index a4aacb9..f107045 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -58,7 +58,7 @@ fn counter() -> Pane { .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) - .child(text(value, font.clone()).with_layout(Layout::hug())), + .child(text(value, font.clone())), ) .child( button(text("+1", font.clone())) From 73d659edf389e7995d969b395a4ecc48e13218f5 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 18 Apr 2023 02:02:54 +0200 Subject: [PATCH 21/49] Make lokinit a dev dependency --- lokui/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lokui/Cargo.toml b/lokui/Cargo.toml index 87262cc..62688d5 100644 --- a/lokui/Cargo.toml +++ b/lokui/Cargo.toml @@ -7,4 +7,6 @@ edition = "2021" [dependencies] skia-safe = { version = "0.60.0", features = ["gl"] } + +[dev-dependencies] miniquad = { git = "https://github.com/loki-chat/lokinit" } From f5a05dc8d477d4afb6a9a78bdfa8e43388568801 Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 01:36:26 +0200 Subject: [PATCH 22/49] Put buttons on true center Useless, until the flex layout changes directions and the buttons stay centered, probably as intended --- lokui/examples/counter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index f107045..202f846 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -65,8 +65,8 @@ fn counter() -> Pane { .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) - .with_origin(Anchor::CENTER_LEFT) - .with_anchor(Anchor::CENTER_LEFT), + .with_origin(Anchor::CENTER) + .with_anchor(Anchor::CENTER), ) .on_click(increment), ) @@ -75,8 +75,8 @@ fn counter() -> Pane { .with_layout( Layout::new() .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) - .with_origin(Anchor::CENTER_LEFT) - .with_anchor(Anchor::CENTER_LEFT), + .with_origin(Anchor::CENTER) + .with_anchor(Anchor::CENTER), ) .on_click(decrement), ), From a1350ff71d9b73dbe395e724764f8566406a5714 Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 01:37:12 +0200 Subject: [PATCH 23/49] `FlexLayout`'s `align` property has no effect --- lokui/examples/counter.rs | 1 - lokui/src/layout/mod.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 202f846..c5293ed 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -46,7 +46,6 @@ fn counter() -> Pane { .with_padding(Padding::vh(5., 10.)) .with_flex_layout(FlexLayout { direction: Direction::Horizontal, - align: Anchor::CENTER_LEFT, gap: 0., }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs index 18d2e31..26afb4c 100644 --- a/lokui/src/layout/mod.rs +++ b/lokui/src/layout/mod.rs @@ -219,6 +219,5 @@ pub enum Direction { #[derive(Clone, Debug, Default)] pub struct FlexLayout { pub direction: Direction, - pub align: Anchor, pub gap: f32, } From d1174bad9872645e803004b00adc7a1a1e7f94ee Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 02:06:23 +0200 Subject: [PATCH 24/49] Remove `PaneChild` struct A tuple turns out to be more convenient, I think. --- lokui/src/components/pane.rs | 70 ++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 39 deletions(-) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 60fb054..7b71703 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -6,17 +6,12 @@ use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use crate::widget::{Event, Widget}; -pub struct PaneChild { - solved_layout: SolvedLayout, - widget: Box, -} - #[derive(Default)] pub struct Pane { layout: Layout, padding: Padding, flex_layout: Option, - children: Vec, + children: Vec<(Box, SolvedLayout)>, } pub fn pane() -> Pane { @@ -49,14 +44,11 @@ impl Pane { } pub fn add_dyn_child(&mut self, widget: Box) { - self.children.push(PaneChild { - solved_layout: SolvedLayout::default(), - widget, - }); + self.children.push((widget, SolvedLayout::default())); } pub fn pop_child(&mut self) -> Option> { - self.children.pop().map(|child| child.widget) + self.children.pop().map(|(widget, _)| widget) } } @@ -75,16 +67,16 @@ impl Widget for Pane { match flex_layout.direction { Direction::Horizontal => { let fills_count = (self.children.iter()) - .filter(|child| child.widget.layout().width.is_fill()) + .filter(|(widget, _)| widget.layout().width.is_fill()) .count(); let filling_width = if fills_count == 0 { 0. } else { let fixed_width: f32 = (self.children.iter()) - .filter_map(|child| match child.widget.layout().width { + .filter_map(|(widget, _)| match widget.layout().width { DimScalar::Fill => None, - DimScalar::Hug => Some(child.widget.min_width()), + DimScalar::Hug => Some(widget.min_width()), DimScalar::Fixed(w) => Some(w), }) .sum(); @@ -94,17 +86,17 @@ impl Widget for Pane { }; let mut x = inner_layout.x_start(); - for child in &mut self.children { + for (widget, solved_layout) in &mut self.children { // Each child is given a slice of the inner layout. - let child_width = match child.widget.layout().width { + let child_width = match widget.layout().width { DimScalar::Fill => filling_width, - DimScalar::Hug => child.widget.min_width(), + DimScalar::Hug => widget.min_width(), DimScalar::Fixed(w) => w, }; let inner_layout = inner_layout.with_width(child_width).with_x(x); - child.solved_layout = child.widget.solve_layout(&inner_layout); + *solved_layout = widget.solve_layout(&inner_layout); x += child_width; } @@ -114,16 +106,16 @@ impl Widget for Pane { // copy-pasted from the Horizontal case but for height? let fills_count = (self.children.iter()) - .filter(|child| child.widget.layout().height.is_fill()) + .filter(|(widget, _)| widget.layout().height.is_fill()) .count(); let filling_height = if fills_count == 0 { 0. } else { let fixed_height: f32 = (self.children.iter()) - .filter_map(|child| match child.widget.layout().height { + .filter_map(|(widget, _)| match widget.layout().height { DimScalar::Fill => None, - DimScalar::Hug => Some(child.widget.min_height()), + DimScalar::Hug => Some(widget.min_height()), DimScalar::Fixed(w) => Some(w), }) .sum(); @@ -133,15 +125,15 @@ impl Widget for Pane { }; let mut y = inner_layout.y_start(); - for child in &mut self.children { - let child_height = match child.widget.layout().height { + for (widget, solved_layout) in &mut self.children { + let child_height = match widget.layout().height { DimScalar::Fill => filling_height, - DimScalar::Hug => child.widget.min_height(), + DimScalar::Hug => widget.min_height(), DimScalar::Fixed(w) => w, }; let inner_layout = inner_layout.with_height(child_height).with_y(y); - child.solved_layout = child.widget.solve_layout(&inner_layout); + *solved_layout = widget.solve_layout(&inner_layout); y += child_height; } @@ -150,8 +142,8 @@ impl Widget for Pane { } else { // Without flex-layout, all children are superposed to each other. - for child in &mut self.children { - child.solved_layout = child.widget.solve_layout(&inner_layout); + for (widget, solved_layout) in &mut self.children { + *solved_layout = widget.solve_layout(&inner_layout); } } @@ -164,9 +156,9 @@ impl Widget for Pane { if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter()) - .map(|child| match child.widget.layout().width { + .map(|(widget, _)| match widget.layout().width { DimScalar::Fixed(w) => w, - _ => child.widget.min_width(), + _ => widget.min_width(), }) .sum(); @@ -175,7 +167,7 @@ impl Widget for Pane { } let inner_min_width = (self.children.iter()) - .map(|child| child.widget.min_width()) + .map(|(widget, _)| widget.min_width()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); @@ -188,9 +180,9 @@ impl Widget for Pane { if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter()) - .map(|child| match child.widget.layout().height { + .map(|(widget, _)| match widget.layout().height { DimScalar::Fixed(h) => h, - _ => child.widget.min_height(), + _ => widget.min_height(), }) .sum(); @@ -199,7 +191,7 @@ impl Widget for Pane { } let inner_min_height = (self.children.iter()) - .map(|child| child.widget.min_height()) + .map(|(widget, _)| widget.min_height()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); @@ -208,8 +200,8 @@ impl Widget for Pane { fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { writeln!(w, "{}", indentation(deepness))?; - for child in &self.children { - child.widget.debug(w, deepness + 1)?; + for (widget, _) in &self.children { + widget.debug(w, deepness + 1)?; } writeln!(w, "{}", indentation(deepness)) } @@ -234,8 +226,8 @@ impl Widget for Pane { paint.set_color(Color::from(0xff_00cc51)); canvas.draw_rect(rect, &paint); - for child in &self.children { - child.widget.draw(canvas, &child.solved_layout); + for (widget, solved_layout) in &self.children { + widget.draw(canvas, solved_layout); } } @@ -248,8 +240,8 @@ impl Widget for Pane { }; if should_handle { - for child in &mut self.children { - handled |= child.widget.handle_event(event, &child.solved_layout); + for (widget, solved_layout) in &mut self.children { + handled |= widget.handle_event(event, solved_layout); if handled { break; From fee22f33f64bc006560925363d7c72767ae85857 Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 02:32:57 +0200 Subject: [PATCH 25/49] Put flex layout solving into a single function --- lokui/src/components/pane.rs | 163 ++++++++++++++++++----------------- 1 file changed, 86 insertions(+), 77 deletions(-) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 7b71703..f79581c 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -62,83 +62,7 @@ impl Widget for Pane { let inner_layout = layout.padded(self.padding); if let Some(flex_layout) = &self.flex_layout { - // With flex-layout, all children are placed next to each other, vertically or horizontally. - - match flex_layout.direction { - Direction::Horizontal => { - let fills_count = (self.children.iter()) - .filter(|(widget, _)| widget.layout().width.is_fill()) - .count(); - - let filling_width = if fills_count == 0 { - 0. - } else { - let fixed_width: f32 = (self.children.iter()) - .filter_map(|(widget, _)| match widget.layout().width { - DimScalar::Fill => None, - DimScalar::Hug => Some(widget.min_width()), - DimScalar::Fixed(w) => Some(w), - }) - .sum(); - - let leftover_width = (inner_layout.width() - fixed_width).max(0.); - leftover_width / fills_count as f32 - }; - - let mut x = inner_layout.x_start(); - for (widget, solved_layout) in &mut self.children { - // Each child is given a slice of the inner layout. - - let child_width = match widget.layout().width { - DimScalar::Fill => filling_width, - DimScalar::Hug => widget.min_width(), - DimScalar::Fixed(w) => w, - }; - - let inner_layout = inner_layout.with_width(child_width).with_x(x); - *solved_layout = widget.solve_layout(&inner_layout); - - x += child_width; - } - } - Direction::Vertical => { - // maybe put this into a function since it's - // copy-pasted from the Horizontal case but for height? - - let fills_count = (self.children.iter()) - .filter(|(widget, _)| widget.layout().height.is_fill()) - .count(); - - let filling_height = if fills_count == 0 { - 0. - } else { - let fixed_height: f32 = (self.children.iter()) - .filter_map(|(widget, _)| match widget.layout().height { - DimScalar::Fill => None, - DimScalar::Hug => Some(widget.min_height()), - DimScalar::Fixed(w) => Some(w), - }) - .sum(); - - let leftover_height = (inner_layout.height() - fixed_height).max(0.); - leftover_height / fills_count as f32 - }; - - let mut y = inner_layout.y_start(); - for (widget, solved_layout) in &mut self.children { - let child_height = match widget.layout().height { - DimScalar::Fill => filling_height, - DimScalar::Hug => widget.min_height(), - DimScalar::Fixed(w) => w, - }; - - let inner_layout = inner_layout.with_height(child_height).with_y(y); - *solved_layout = widget.solve_layout(&inner_layout); - - y += child_height; - } - } - } + solve_flex_layout(flex_layout, &mut self.children, inner_layout); } else { // Without flex-layout, all children are superposed to each other. @@ -252,3 +176,88 @@ impl Widget for Pane { handled } } + +/// Solves a pane's children's solved layouts with a flex layout. +/// +/// With a flex layout, all children are placed next to each other, vertically or horizontally. +fn solve_flex_layout( + flex_layout: &FlexLayout, + children: &mut [(Box, SolvedLayout)], + inner_layout: SolvedLayout, +) { + match flex_layout.direction { + Direction::Horizontal => { + let fills_count = (children.iter()) + .filter(|(widget, _)| widget.layout().width.is_fill()) + .count(); + + let filling_width = if fills_count == 0 { + 0. + } else { + let fixed_width: f32 = (children.iter()) + .filter_map(|(widget, _)| match widget.layout().width { + DimScalar::Fill => None, + DimScalar::Hug => Some(widget.min_width()), + DimScalar::Fixed(w) => Some(w), + }) + .sum(); + + let leftover_width = (inner_layout.width() - fixed_width).max(0.); + leftover_width / fills_count as f32 + }; + + let mut x = inner_layout.x_start(); + for (widget, solved_layout) in children.iter_mut() { + // Each child is given a slice of the inner layout. + + let child_width = match widget.layout().width { + DimScalar::Fill => filling_width, + DimScalar::Hug => widget.min_width(), + DimScalar::Fixed(w) => w, + }; + + let inner_layout = inner_layout.with_width(child_width).with_x(x); + *solved_layout = widget.solve_layout(&inner_layout); + + x += child_width; + } + } + Direction::Vertical => { + // maybe put this into a function since it's + // copy-pasted from the Horizontal case but for height? + + let fills_count = (children.iter()) + .filter(|(widget, _)| widget.layout().height.is_fill()) + .count(); + + let filling_height = if fills_count == 0 { + 0. + } else { + let fixed_height: f32 = (children.iter()) + .filter_map(|(widget, _)| match widget.layout().height { + DimScalar::Fill => None, + DimScalar::Hug => Some(widget.min_height()), + DimScalar::Fixed(w) => Some(w), + }) + .sum(); + + let leftover_height = (inner_layout.height() - fixed_height).max(0.); + leftover_height / fills_count as f32 + }; + + let mut y = inner_layout.y_start(); + for (widget, solved_layout) in children.iter_mut() { + let child_height = match widget.layout().height { + DimScalar::Fill => filling_height, + DimScalar::Hug => widget.min_height(), + DimScalar::Fixed(w) => w, + }; + + let inner_layout = inner_layout.with_height(child_height).with_y(y); + *solved_layout = widget.solve_layout(&inner_layout); + + y += child_height; + } + } + } +} From 045e040b29bc117ef2f8ce080020775523a2046c Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 02:41:27 +0200 Subject: [PATCH 26/49] Add flex layout gap logic --- lokui/examples/counter.rs | 2 +- lokui/src/components/pane.rs | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index c5293ed..bc60e7d 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -46,7 +46,7 @@ fn counter() -> Pane { .with_padding(Padding::vh(5., 10.)) .with_flex_layout(FlexLayout { direction: Direction::Horizontal, - gap: 0., + gap: 5., }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) .child( diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index f79581c..1cb95ce 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -202,7 +202,8 @@ fn solve_flex_layout( }) .sum(); - let leftover_width = (inner_layout.width() - fixed_width).max(0.); + let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; + let leftover_width = (inner_layout.width() - fixed_width - gap_width).max(0.); leftover_width / fills_count as f32 }; @@ -219,7 +220,7 @@ fn solve_flex_layout( let inner_layout = inner_layout.with_width(child_width).with_x(x); *solved_layout = widget.solve_layout(&inner_layout); - x += child_width; + x += child_width + flex_layout.gap; } } Direction::Vertical => { @@ -241,7 +242,8 @@ fn solve_flex_layout( }) .sum(); - let leftover_height = (inner_layout.height() - fixed_height).max(0.); + let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; + let leftover_height = (inner_layout.height() - fixed_height - gap_width).max(0.); leftover_height / fills_count as f32 }; @@ -256,7 +258,7 @@ fn solve_flex_layout( let inner_layout = inner_layout.with_height(child_height).with_y(y); *solved_layout = widget.solve_layout(&inner_layout); - y += child_height; + y += child_height + flex_layout.gap; } } } From cc6e3b6f8f43b7ff1d7705e5af10083b5f59c93a Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 13:59:32 +0200 Subject: [PATCH 27/49] refactor: functions to solve width and height This small pattern was repeated several times. Additionally, `default_solve_layout` is now out of the `Widget` trait to prevent it from being overridden. --- lokui/src/components/button.rs | 4 +-- lokui/src/components/pane.rs | 30 ++++------------- lokui/src/components/text.rs | 4 +-- lokui/src/widget.rs | 60 +++++++++++++++++++++------------- 4 files changed, 47 insertions(+), 51 deletions(-) diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 379627b..84b06c5 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -6,7 +6,7 @@ use skia_safe::{Canvas, Color, Paint, Rect}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; use crate::lazy::Laz; -use crate::widget::{Event, Widget}; +use crate::widget::{default_solve_layout, Event, Widget}; use super::text::Text; @@ -48,7 +48,7 @@ impl Widget for Button { } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - let layout = self.default_solve_layout(parent_layout); + let layout = default_solve_layout(self, parent_layout); self.text_layout = (self.text).solve_layout(&layout.padded(Padding::vh(5., 10.))); layout } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 1cb95ce..c89e250 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -4,7 +4,7 @@ use skia_safe::{Canvas, Color, Paint, Rect}; use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use crate::widget::{Event, Widget}; +use crate::widget::{default_solve_layout, solve_height, solve_width, Event, Widget}; #[derive(Default)] pub struct Pane { @@ -58,7 +58,7 @@ impl Widget for Pane { } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - let layout = self.default_solve_layout(parent_layout); + let layout = default_solve_layout(self, parent_layout); let inner_layout = layout.padded(self.padding); if let Some(flex_layout) = &self.flex_layout { @@ -195,11 +195,7 @@ fn solve_flex_layout( 0. } else { let fixed_width: f32 = (children.iter()) - .filter_map(|(widget, _)| match widget.layout().width { - DimScalar::Fill => None, - DimScalar::Hug => Some(widget.min_width()), - DimScalar::Fixed(w) => Some(w), - }) + .filter_map(|(widget, _)| solve_width(widget.as_ref())) .sum(); let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; @@ -211,12 +207,7 @@ fn solve_flex_layout( for (widget, solved_layout) in children.iter_mut() { // Each child is given a slice of the inner layout. - let child_width = match widget.layout().width { - DimScalar::Fill => filling_width, - DimScalar::Hug => widget.min_width(), - DimScalar::Fixed(w) => w, - }; - + let child_width = solve_width(widget.as_ref()).unwrap_or(filling_width); let inner_layout = inner_layout.with_width(child_width).with_x(x); *solved_layout = widget.solve_layout(&inner_layout); @@ -235,11 +226,7 @@ fn solve_flex_layout( 0. } else { let fixed_height: f32 = (children.iter()) - .filter_map(|(widget, _)| match widget.layout().height { - DimScalar::Fill => None, - DimScalar::Hug => Some(widget.min_height()), - DimScalar::Fixed(w) => Some(w), - }) + .filter_map(|(widget, _)| solve_height(widget.as_ref())) .sum(); let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; @@ -249,12 +236,7 @@ fn solve_flex_layout( let mut y = inner_layout.y_start(); for (widget, solved_layout) in children.iter_mut() { - let child_height = match widget.layout().height { - DimScalar::Fill => filling_height, - DimScalar::Hug => widget.min_height(), - DimScalar::Fixed(w) => w, - }; - + let child_height = solve_height(widget.as_ref()).unwrap_or(filling_height); let inner_layout = inner_layout.with_height(child_height).with_y(y); *solved_layout = widget.solve_layout(&inner_layout); diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 06a7d67..73ef284 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -6,7 +6,7 @@ use skia_safe::{Canvas, Color, Font, Paint, Rect}; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; use crate::lazy::Lazy; -use crate::widget::{Event, Widget}; +use crate::widget::{Event, Widget, default_solve_layout}; pub struct Text { layout: Layout, @@ -45,7 +45,7 @@ impl Widget for Text { } fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - self.default_solve_layout(parent_layout) + default_solve_layout(self, parent_layout) } fn min_width(&self) -> f32 { diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index ce06bd7..0d5d2fd 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -14,34 +14,13 @@ pub enum Event { pub trait Widget { fn layout(&self) -> &Layout; - fn default_solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - let width = match self.layout().width { - DimScalar::Fill => parent_layout.width(), - DimScalar::Hug => self.min_width(), - DimScalar::Fixed(w) => w, - }; - - let height = match self.layout().height { - DimScalar::Fill => parent_layout.height(), - DimScalar::Hug => self.min_height(), - DimScalar::Fixed(h) => h, - }; - - let (x, y) = parent_layout.point_at_anchor(self.layout().anchor); - SolvedLayout::from_origin(self.layout().origin, x, y, width, height) - } - fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout; /// Minimum possible width in case we choose DimScalar::Hug as the layout width. - fn min_width(&self) -> f32 { - 0. - } + fn min_width(&self) -> f32; /// Minimum possible height in case we choose DimScalar::Hug as the layout height. - fn min_height(&self) -> f32 { - 0. - } + fn min_height(&self) -> f32; fn debug(&self, _w: &mut dyn io::Write, _deepness: usize) -> io::Result<()> { Ok(()) @@ -50,3 +29,38 @@ pub trait Widget { fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout); fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool; } + +/// Partially resolve this widget's width. +/// +/// - If the width is `Fill`, it returns `None`. +/// - If the width is `Hug`, it returns this widget's minimum width. +/// - If the width is `Fixed`, it returns that fixed width value. +pub fn solve_width(widget: &dyn Widget) -> Option { + match widget.layout().width { + DimScalar::Fill => None, + DimScalar::Hug => Some(widget.min_width()), + DimScalar::Fixed(w) => Some(w), + } +} + +/// Partially resolve this widget's height. +/// +/// - If the height is `Fill`, it returns `None`. +/// - If the height is `Hug`, it returns this widget's minimum height. +/// - If the height is `Fixed`, it returns that fixed height value. +pub fn solve_height(widget: &dyn Widget) -> Option { + match widget.layout().height { + DimScalar::Fill => None, + DimScalar::Hug => Some(widget.min_height()), + DimScalar::Fixed(w) => Some(w), + } +} + +/// Default function to solve a widget's layout, based on the parent's solved layout. +pub fn default_solve_layout(widget: &mut impl Widget, parent_layout: &SolvedLayout) -> SolvedLayout { + let width = solve_width(widget).unwrap_or_else(|| parent_layout.width()); + let height = solve_height(widget).unwrap_or_else(|| parent_layout.height()); + + let (x, y) = parent_layout.point_at_anchor(widget.layout().anchor); + SolvedLayout::from_origin(widget.layout().origin, x, y, width, height) +} From e8fa294067f6fc878b2e64967aab2bdc55d687af Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 17:34:59 +0200 Subject: [PATCH 28/49] fix: cargo fmt --- lokui/src/components/text.rs | 2 +- lokui/src/widget.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 73ef284..3c62a84 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -6,7 +6,7 @@ use skia_safe::{Canvas, Color, Font, Paint, Rect}; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; use crate::lazy::Lazy; -use crate::widget::{Event, Widget, default_solve_layout}; +use crate::widget::{default_solve_layout, Event, Widget}; pub struct Text { layout: Layout, diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 0d5d2fd..fe4c211 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -57,7 +57,10 @@ pub fn solve_height(widget: &dyn Widget) -> Option { } /// Default function to solve a widget's layout, based on the parent's solved layout. -pub fn default_solve_layout(widget: &mut impl Widget, parent_layout: &SolvedLayout) -> SolvedLayout { +pub fn default_solve_layout( + widget: &mut impl Widget, + parent_layout: &SolvedLayout, +) -> SolvedLayout { let width = solve_width(widget).unwrap_or_else(|| parent_layout.width()); let height = solve_height(widget).unwrap_or_else(|| parent_layout.height()); From 6b34435b46b356317f59695dce1688a69118ad17 Mon Sep 17 00:00:00 2001 From: Speykious Date: Wed, 19 Apr 2023 20:13:13 +0200 Subject: [PATCH 29/49] Handle hover events --- lokui/examples/counter.rs | 23 ++++++- lokui/src/components/button.rs | 35 ++++++++--- lokui/src/components/pane.rs | 109 ++++++++++++++++++++------------- lokui/src/components/text.rs | 3 +- lokui/src/events.rs | 14 +++++ lokui/src/lib.rs | 1 + lokui/src/widget.rs | 8 +-- 7 files changed, 132 insertions(+), 61 deletions(-) create mode 100644 lokui/src/events.rs diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index bc60e7d..cd86e21 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -5,9 +5,10 @@ use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; use lokui::components::pane::{pane, Pane}; use lokui::components::text::text; +use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use lokui::lazy::{laz, lazy}; -use lokui::widget::{Event, Widget}; +use lokui::widget::Widget; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::{Color, Font, FontStyle, Typeface}; @@ -91,6 +92,17 @@ struct Stage { impl EventHandler for Stage { fn update(&mut self, _skia_ctx: &mut SkiaContext) {} + fn mouse_button_down_event( + &mut self, + _skia_ctx: &mut SkiaContext, + _button: miniquad::MouseButton, + x: f32, + y: f32, + ) { + let event = Event::MouseDown(MousePosition { x, y }); + self.root_pane.handle_event(event, &self.root_layout); + } + fn mouse_button_up_event( &mut self, _skia_ctx: &mut SkiaContext, @@ -98,8 +110,13 @@ impl EventHandler for Stage { x: f32, y: f32, ) { - self.root_pane - .handle_event(Event::Clicked(x, y), &self.root_layout); + let event = Event::MouseUp(MousePosition { x, y }); + self.root_pane.handle_event(event, &self.root_layout); + } + + fn mouse_motion_event(&mut self, _skia_ctx: &mut SkiaContext, x: f32, y: f32) { + let event = Event::MouseMove(MousePosition { x, y }); + self.root_pane.handle_event(event, &self.root_layout); } fn resize_event(&mut self, skia_ctx: &mut SkiaContext, width: f32, height: f32) { diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 84b06c5..7acf33c 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -3,10 +3,11 @@ use std::io; use skia_safe::{Canvas, Color, Paint, Rect}; +use crate::events::{Event, MousePosition}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; use crate::lazy::Laz; -use crate::widget::{default_solve_layout, Event, Widget}; +use crate::widget::{default_solve_layout, Widget}; use super::text::Text; @@ -16,6 +17,7 @@ pub struct Button { text: Text, on_click: Option>, enabled: Laz, + is_mouse_down: bool, hovered: bool, } @@ -26,6 +28,7 @@ pub fn button(text: Text) -> Button { text, on_click: None, enabled: Laz::new(true), + is_mouse_down: false, hovered: false, } } @@ -82,22 +85,34 @@ impl Widget for Button { paint.set_anti_alias(true); paint.set_stroke_width(2.); + let color = match (self.enabled.get(), self.is_mouse_down) { + (true, true) => 0xa70038, + (true, false) if self.hovered => 0xff415a, + (true, false) if !self.hovered => 0xff0051, + _ => 0x7a4553, + }; + paint.set_stroke(false); - paint.set_color(Color::from(0x80_ff0051)); + paint.set_color(Color::from(0x80_000000 | color)); canvas.draw_rect(rect, &paint); paint.set_stroke(true); - paint.set_color(Color::from(0xff_ff0051)); + paint.set_color(Color::from(0xff_000000 | color)); canvas.draw_rect(rect, &paint); self.text.draw(canvas, &self.text_layout); } - fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { + fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { if self.enabled.get() { match event { - Event::Clicked(x, y) => { - if layout.contains(x, y) { + Event::MouseDown(_) => { + self.is_mouse_down = true; + true + } + Event::MouseUp(MousePosition { x, y }) => { + if self.is_mouse_down { + self.is_mouse_down = false; if let Some(on_click) = self.on_click.as_mut() { (on_click)(x, y); } @@ -106,14 +121,16 @@ impl Widget for Button { false } } - Event::HoverStart => { + Event::MouseIn => { self.hovered = true; true } - Event::HoverEnd => { + Event::MouseOut => { self.hovered = false; - false + self.is_mouse_down = false; + true } + _ => false, } } else { false diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index c89e250..a8db8e7 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -2,16 +2,33 @@ use std::io; use skia_safe::{Canvas, Color, Paint, Rect}; +use crate::events::{Event, MousePosition}; use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use crate::widget::{default_solve_layout, solve_height, solve_width, Event, Widget}; +use crate::widget::{default_solve_layout, solve_height, solve_width, Widget}; + +struct PaneChild { + widget: Box, + solved_layout: SolvedLayout, + is_hovered: bool, +} + +impl PaneChild { + fn new(widget: Box) -> Self { + Self { + widget, + solved_layout: SolvedLayout::default(), + is_hovered: false, + } + } +} #[derive(Default)] pub struct Pane { layout: Layout, padding: Padding, flex_layout: Option, - children: Vec<(Box, SolvedLayout)>, + children: Vec, } pub fn pane() -> Pane { @@ -44,11 +61,11 @@ impl Pane { } pub fn add_dyn_child(&mut self, widget: Box) { - self.children.push((widget, SolvedLayout::default())); + self.children.push(PaneChild::new(widget)); } pub fn pop_child(&mut self) -> Option> { - self.children.pop().map(|(widget, _)| widget) + self.children.pop().map(|child| child.widget) } } @@ -66,8 +83,8 @@ impl Widget for Pane { } else { // Without flex-layout, all children are superposed to each other. - for (widget, solved_layout) in &mut self.children { - *solved_layout = widget.solve_layout(&inner_layout); + for child in &mut self.children { + child.solved_layout = child.widget.solve_layout(&inner_layout); } } @@ -80,9 +97,9 @@ impl Widget for Pane { if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter()) - .map(|(widget, _)| match widget.layout().width { + .map(|child| match child.widget.layout().width { DimScalar::Fixed(w) => w, - _ => widget.min_width(), + _ => child.widget.min_width(), }) .sum(); @@ -91,7 +108,7 @@ impl Widget for Pane { } let inner_min_width = (self.children.iter()) - .map(|(widget, _)| widget.min_width()) + .map(|child| child.widget.min_width()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); @@ -104,9 +121,9 @@ impl Widget for Pane { if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter()) - .map(|(widget, _)| match widget.layout().height { + .map(|child| match child.widget.layout().height { DimScalar::Fixed(h) => h, - _ => widget.min_height(), + _ => child.widget.min_height(), }) .sum(); @@ -115,7 +132,7 @@ impl Widget for Pane { } let inner_min_height = (self.children.iter()) - .map(|(widget, _)| widget.min_height()) + .map(|child| child.widget.min_height()) .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); @@ -124,8 +141,8 @@ impl Widget for Pane { fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { writeln!(w, "{}", indentation(deepness))?; - for (widget, _) in &self.children { - widget.debug(w, deepness + 1)?; + for child in &self.children { + child.widget.debug(w, deepness + 1)?; } writeln!(w, "{}", indentation(deepness)) } @@ -150,30 +167,40 @@ impl Widget for Pane { paint.set_color(Color::from(0xff_00cc51)); canvas.draw_rect(rect, &paint); - for (widget, solved_layout) in &self.children { - widget.draw(canvas, solved_layout); + for child in &self.children { + child.widget.draw(canvas, &child.solved_layout); } } - fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { - let mut handled = false; - - let should_handle = match event { - Event::Clicked(x, y) => layout.contains(x, y), - _ => true, - }; - - if should_handle { - for (widget, solved_layout) in &mut self.children { - handled |= widget.handle_event(event, solved_layout); + fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { + match event { + Event::MouseDown(MousePosition { x, y }) | Event::MouseUp(MousePosition { x, y }) => { + for child in &mut self.children { + if !child.solved_layout.contains(x, y) { + continue; + } - if handled { - break; + child.widget.handle_event(event, &child.solved_layout); + } + } + Event::MouseMove(MousePosition { x, y }) => { + for child in &mut self.children { + if child.solved_layout.contains(x, y) { + if !child.is_hovered { + child.is_hovered = true; + (child.widget).handle_event(Event::MouseIn, &child.solved_layout); + } + child.widget.handle_event(event, &child.solved_layout); + } else if child.is_hovered { + child.is_hovered = false; + (child.widget).handle_event(Event::MouseOut, &child.solved_layout); + } } } + _ => (), } - handled + true } } @@ -182,20 +209,20 @@ impl Widget for Pane { /// With a flex layout, all children are placed next to each other, vertically or horizontally. fn solve_flex_layout( flex_layout: &FlexLayout, - children: &mut [(Box, SolvedLayout)], + children: &mut [PaneChild], inner_layout: SolvedLayout, ) { match flex_layout.direction { Direction::Horizontal => { let fills_count = (children.iter()) - .filter(|(widget, _)| widget.layout().width.is_fill()) + .filter(|child| child.widget.layout().width.is_fill()) .count(); let filling_width = if fills_count == 0 { 0. } else { let fixed_width: f32 = (children.iter()) - .filter_map(|(widget, _)| solve_width(widget.as_ref())) + .filter_map(|child| solve_width(child.widget.as_ref())) .sum(); let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; @@ -204,12 +231,12 @@ fn solve_flex_layout( }; let mut x = inner_layout.x_start(); - for (widget, solved_layout) in children.iter_mut() { + for child in children.iter_mut() { // Each child is given a slice of the inner layout. - let child_width = solve_width(widget.as_ref()).unwrap_or(filling_width); + let child_width = solve_width(child.widget.as_ref()).unwrap_or(filling_width); let inner_layout = inner_layout.with_width(child_width).with_x(x); - *solved_layout = widget.solve_layout(&inner_layout); + child.solved_layout = child.widget.solve_layout(&inner_layout); x += child_width + flex_layout.gap; } @@ -219,14 +246,14 @@ fn solve_flex_layout( // copy-pasted from the Horizontal case but for height? let fills_count = (children.iter()) - .filter(|(widget, _)| widget.layout().height.is_fill()) + .filter(|child| child.widget.layout().height.is_fill()) .count(); let filling_height = if fills_count == 0 { 0. } else { let fixed_height: f32 = (children.iter()) - .filter_map(|(widget, _)| solve_height(widget.as_ref())) + .filter_map(|child| solve_height(child.widget.as_ref())) .sum(); let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; @@ -235,10 +262,10 @@ fn solve_flex_layout( }; let mut y = inner_layout.y_start(); - for (widget, solved_layout) in children.iter_mut() { - let child_height = solve_height(widget.as_ref()).unwrap_or(filling_height); + for child in children.iter_mut() { + let child_height = solve_height(child.widget.as_ref()).unwrap_or(filling_height); let inner_layout = inner_layout.with_height(child_height).with_y(y); - *solved_layout = widget.solve_layout(&inner_layout); + child.solved_layout = child.widget.solve_layout(&inner_layout); y += child_height + flex_layout.gap; } diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 3c62a84..384c321 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -3,10 +3,11 @@ use std::io; use skia_safe::{Canvas, Color, Font, Paint, Rect}; +use crate::events::Event; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; use crate::lazy::Lazy; -use crate::widget::{default_solve_layout, Event, Widget}; +use crate::widget::{default_solve_layout, Widget}; pub struct Text { layout: Layout, diff --git a/lokui/src/events.rs b/lokui/src/events.rs new file mode 100644 index 0000000..f39fe67 --- /dev/null +++ b/lokui/src/events.rs @@ -0,0 +1,14 @@ +#[derive(Clone, Copy, Debug)] +pub struct MousePosition { + pub x: f32, + pub y: f32, +} + +#[derive(Clone, Copy, Debug)] +pub enum Event { + MouseDown(MousePosition), + MouseUp(MousePosition), + MouseMove(MousePosition), + MouseIn, + MouseOut, +} diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index c911e7e..bba5a0a 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -1,6 +1,7 @@ #![allow(clippy::unusual_byte_groupings)] pub mod components; +pub mod events; pub mod layout; pub mod lazy; pub mod widget; diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index fe4c211..461a1c6 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -2,15 +2,9 @@ use std::io; use skia_safe::Canvas; +use crate::events::Event; use crate::layout::{DimScalar, Layout, SolvedLayout}; -#[derive(Clone, Copy, Debug)] -pub enum Event { - Clicked(f32, f32), - HoverStart, - HoverEnd, -} - pub trait Widget { fn layout(&self) -> &Layout; From 547580c83fcc995649514012104f6d7c0836c752 Mon Sep 17 00:00:00 2001 From: Speykious Date: Fri, 21 Apr 2023 19:42:55 +0200 Subject: [PATCH 30/49] Basics of animation system --- Cargo.lock | 1 - lokui/Cargo.toml | 3 +- lokui/examples/counter.rs | 2 +- lokui/src/anim/ease.rs | 164 +++++++++++++++++++++++++++++++++ lokui/src/anim/mod.rs | 111 ++++++++++++++++++++++ lokui/src/components/button.rs | 37 +++++--- lokui/src/components/text.rs | 2 +- lokui/src/lazy.rs | 68 -------------- lokui/src/lib.rs | 3 +- lokui/src/state.rs | 158 +++++++++++++++++++++++++++++++ 10 files changed, 463 insertions(+), 86 deletions(-) create mode 100644 lokui/src/anim/ease.rs create mode 100644 lokui/src/anim/mod.rs delete mode 100644 lokui/src/lazy.rs create mode 100644 lokui/src/state.rs diff --git a/Cargo.lock b/Cargo.lock index 05275a0..f36a6f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,7 +388,6 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniquad" version = "0.4.0-alpha.0" -source = "git+https://github.com/loki-chat/lokinit#2ec39bfacc0830538640ca68addd24da94766b1c" dependencies = [ "libc", "ndk-sys", diff --git a/lokui/Cargo.toml b/lokui/Cargo.toml index 62688d5..9444e52 100644 --- a/lokui/Cargo.toml +++ b/lokui/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" skia-safe = { version = "0.60.0", features = ["gl"] } [dev-dependencies] -miniquad = { git = "https://github.com/loki-chat/lokinit" } +# miniquad = { git = "https://github.com/loki-chat/lokinit" } +miniquad = { path = "../../lokinit" } diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index cd86e21..a4749a7 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -7,7 +7,7 @@ use lokui::components::pane::{pane, Pane}; use lokui::components::text::text; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use lokui::lazy::{laz, lazy}; +use lokui::state::{laz, lazy}; use lokui::widget::Widget; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; diff --git a/lokui/src/anim/ease.rs b/lokui/src/anim/ease.rs new file mode 100644 index 0000000..78d8098 --- /dev/null +++ b/lokui/src/anim/ease.rs @@ -0,0 +1,164 @@ +use std::f32::consts::PI; + +pub fn linear(t: f32) -> f32 { + t +} + +// sine + +pub fn in_sine(t: f32) -> f32 { + 1. - (t * PI / 2.).cos() +} + +pub fn out_sine(t: f32) -> f32 { + ((t * PI) / 2.).sin() +} + +pub fn in_out_sine(t: f32) -> f32 { + -((PI * t).cos() - 1.) / 2. +} + +// quad + +pub fn in_quad(t: f32) -> f32 { + t.powi(2) +} + +pub fn out_quad(t: f32) -> f32 { + 1. - (1. - t).powi(2) +} + +pub fn in_out_quad(t: f32) -> f32 { + if t < 0.5 { + 2. * t.powi(2) + } else { + 1. - (-2. * t + 2.).powi(2) / 2. + } +} + +// cubic + +pub fn in_cubic(t: f32) -> f32 { + t.powi(3) +} + +pub fn out_cubic(t: f32) -> f32 { + 1. - (1. - t).powi(3) +} + +pub fn in_out_cubic(t: f32) -> f32 { + if t < 0.5 { + 4. * t.powi(3) + } else { + 1. - (-2. * t + 2.).powi(3) / 2. + } +} + +// quart + +pub fn in_quart(t: f32) -> f32 { + t.powi(4) +} + +pub fn out_quart(t: f32) -> f32 { + 1. - (1. - t).powi(4) +} + +pub fn in_out_quart(t: f32) -> f32 { + if t < 0.5 { + 8. * t.powi(4) + } else { + 1. - (-2. * t + 2.).powi(4) / 2. + } +} + +// quint + +pub fn in_quint(t: f32) -> f32 { + t.powi(5) +} + +pub fn out_quint(t: f32) -> f32 { + 1. - (1. - t).powi(5) +} + +pub fn in_out_quint(t: f32) -> f32 { + if t < 0.5 { + 16. * t.powi(5) + } else { + 1. - (-2. * t + 2.).powi(5) / 2. + } +} + +// expo + +pub fn in_expo(t: f32) -> f32 { + if t == 0. { + 0. + } else { + 2_f32.powf(10. * t - 10.) + } +} + +pub fn out_expo(t: f32) -> f32 { + if t == 1. { + 1. + } else { + 1. - 2_f32.powf(-10. * t) + } +} + +pub fn in_out_expo(t: f32) -> f32 { + if t == 0. { + 0. + } else if t == 1. { + 1. + } else if t < 0.5 { + 2_f32.powf(20. * t - 10.) / 2. + } else { + (2. - 2_f32.powf(-20. * t + 10.)) / 2. + } +} + +// circ + +pub fn in_circ(t: f32) -> f32 { + 1. - (1. - t.powi(2)).sqrt() +} + +pub fn out_circ(t: f32) -> f32 { + (1. - (t - 1.).powi(2)).sqrt() +} + +pub fn in_out_circ(t: f32) -> f32 { + if t < 0.5 { + (1. - (1. - (2. * t).powi(2)).sqrt()) / 2. + } else { + ((1. - (-2. * t + 2.).powi(2)).sqrt() + 1.) / 2. + } +} + +// back + +pub fn in_back(t: f32) -> f32 { + let c1 = 1.70158; + let c3 = c1 + 1.; + c3 * t.powi(3) - c1 * t.powi(2) +} + +pub fn out_back(t: f32) -> f32 { + let c1 = 1.70158; + let c3 = c1 + 1.; + 1. + c3 * (t - 1.).powi(3) + c1 * (t - 1.).powi(2) +} + +pub fn in_out_back(x: f32) -> f32 { + let c1 = 1.70158; + let c2 = c1 * 1.525; + + if x < 0.5 { + ((2. * x).powi(2) * ((c2 + 1.) * 2. * x - c2)) / 2. + } else { + ((2. * x - 2.).powi(2) * ((c2 + 1.) * (x * 2. - 2.) + c2) + 2.) / 2. + } +} diff --git a/lokui/src/anim/mod.rs b/lokui/src/anim/mod.rs new file mode 100644 index 0000000..403410c --- /dev/null +++ b/lokui/src/anim/mod.rs @@ -0,0 +1,111 @@ +use std::fmt::Debug; +use std::ops::{Add, Mul, Sub}; +use std::time::{Duration, Instant}; + +pub mod ease; + +impl + Sub + Mul + Copy> Interpolate for I {} +pub trait Interpolate: + Add + Sub + Mul + Copy +{ +} + +pub fn lerp(t: f32, start: I, end: I) -> I { + start + (end - start) * t +} + +pub fn interp(ease_fn: fn(f32) -> f32, t: f32, start: I, end: I) -> I { + lerp(ease_fn(t), start, end) +} + +#[derive(Clone)] +pub enum Property { + Static { + value: T, + }, + Anim { + prev: T, + next: T, + start: Instant, + ease_fn: fn(f32) -> f32, + duration: Duration, + }, +} + +impl Property { + pub fn new(value: T) -> Self { + Self::Static { value } + } + + pub fn go_to(&mut self, next: T, ease_fn: fn(f32) -> f32, duration: Duration) { + let prev = self.current(); + let start = Instant::now(); + *self = Self::Anim { + prev, + next, + start, + ease_fn, + duration, + }; + } + + pub fn set(&mut self, value: T) { + *self = Self::Static { value }; + } + + pub fn current(&mut self) -> T { + match self { + Self::Static { value } => *value, + Self::Anim { + prev, + next, + start, + ease_fn, + duration, + } => { + let t = (start.elapsed().as_secs_f32() / duration.as_secs_f32()).clamp(0., 1.); + let value = interp(*ease_fn, t, *prev, *next); + if t == 1. { + // make static + self.set(value); + } + value + } + } + } + + pub fn freeze(&mut self) { + if self.is_anim() { + let value = self.current(); + self.set(value); + } + } + + pub fn is_static(&self) -> bool { + matches!(self, Self::Static { .. }) + } + + pub fn is_anim(&self) -> bool { + matches!(self, Self::Anim { .. }) + } +} + +impl Debug for Property { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Static { value } => write!(f, "static ({value:?})"), + Self::Anim { + prev, + next, + duration, + .. + } => { + write!( + f, + "anim [{}ms] ({prev:?} -> {next:?})", + duration.as_millis() + ) + } + } + } +} diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index 7acf33c..f1ba84a 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -1,12 +1,14 @@ use std::fmt::Display; use std::io; +use std::time::Duration; -use skia_safe::{Canvas, Color, Paint, Rect}; +use skia_safe::{Canvas, Paint, Rect}; +use crate::anim::{ease, Property}; use crate::events::{Event, MousePosition}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; -use crate::lazy::Laz; +use crate::state::{lazy, Color, Laz, Lazy}; use crate::widget::{default_solve_layout, Widget}; use super::text::Text; @@ -15,6 +17,7 @@ pub struct Button { layout: Layout, text_layout: SolvedLayout, text: Text, + color: Lazy>, on_click: Option>, enabled: Laz, is_mouse_down: bool, @@ -26,6 +29,7 @@ pub fn button(text: Text) -> Button { layout: Layout::hug(), text_layout: SolvedLayout::default(), text, + color: lazy(Property::new(Color::from_hex(0xff0051))), on_click: None, enabled: Laz::new(true), is_mouse_down: false, @@ -85,19 +89,14 @@ impl Widget for Button { paint.set_anti_alias(true); paint.set_stroke_width(2.); - let color = match (self.enabled.get(), self.is_mouse_down) { - (true, true) => 0xa70038, - (true, false) if self.hovered => 0xff415a, - (true, false) if !self.hovered => 0xff0051, - _ => 0x7a4553, - }; + let color = self.color.get_mut().current().into_skia(); paint.set_stroke(false); - paint.set_color(Color::from(0x80_000000 | color)); + paint.set_color(color.with_a(0x80)); canvas.draw_rect(rect, &paint); paint.set_stroke(true); - paint.set_color(Color::from(0xff_000000 | color)); + paint.set_color(color.with_a(0xff)); canvas.draw_rect(rect, &paint); self.text.draw(canvas, &self.text_layout); @@ -105,7 +104,7 @@ impl Widget for Button { fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { if self.enabled.get() { - match event { + let handled = match event { Event::MouseDown(_) => { self.is_mouse_down = true; true @@ -130,8 +129,20 @@ impl Widget for Button { self.is_mouse_down = false; true } - _ => false, - } + _ => return false, + }; + + let color = match (self.enabled.get(), self.is_mouse_down) { + (true, true) => 0xa70038, + (true, false) if self.hovered => 0x51ffff, + (true, false) => 0xff0051, + _ => 0x7a4553, + }; + + let color = Color::from_hex(color); + (self.color.get_mut()).go_to(color, ease::out_quint, Duration::from_millis(500)); + + handled } else { false } diff --git a/lokui/src/components/text.rs b/lokui/src/components/text.rs index 384c321..b3bac7b 100644 --- a/lokui/src/components/text.rs +++ b/lokui/src/components/text.rs @@ -6,7 +6,7 @@ use skia_safe::{Canvas, Color, Font, Paint, Rect}; use crate::events::Event; use crate::indentation; use crate::layout::{Anchor, Layout, SolvedLayout}; -use crate::lazy::Lazy; +use crate::state::Lazy; use crate::widget::{default_solve_layout, Widget}; pub struct Text { diff --git a/lokui/src/lazy.rs b/lokui/src/lazy.rs deleted file mode 100644 index c5fa41a..0000000 --- a/lokui/src/lazy.rs +++ /dev/null @@ -1,68 +0,0 @@ -use std::cell::{Cell, Ref, RefCell}; -use std::fmt; -use std::ops::Deref; -use std::rc::Rc; - -pub struct Lazy(Rc>); - -pub fn lazy(val: T) -> Lazy { - Lazy::new(val) -} - -impl Lazy { - pub fn new(val: T) -> Self { - Lazy(Rc::new(RefCell::new(val))) - } - - pub fn get(&self) -> Ref { - self.0.borrow() - } - - pub fn set(&self, val: T) { - *self.0.borrow_mut() = val; - } -} - -impl Clone for Lazy { - fn clone(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl fmt::Display for Lazy { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - -pub struct Laz(Rc>); - -pub fn laz(val: T) -> Laz { - Laz::new(val) -} - -impl Laz { - pub fn new(val: T) -> Self { - Laz(Rc::new(Cell::new(val))) - } -} - -impl Clone for Laz { - fn clone(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl Deref for Laz { - type Target = Cell; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl fmt::Display for Laz { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index bba5a0a..1959dad 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -1,9 +1,10 @@ #![allow(clippy::unusual_byte_groupings)] +pub mod anim; pub mod components; pub mod events; pub mod layout; -pub mod lazy; +pub mod state; pub mod widget; pub fn indentation(n: usize) -> String { diff --git a/lokui/src/state.rs b/lokui/src/state.rs new file mode 100644 index 0000000..6217678 --- /dev/null +++ b/lokui/src/state.rs @@ -0,0 +1,158 @@ +use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::fmt::{self, Debug}; +use std::ops::{Add, Deref, Mul, Sub}; +use std::rc::Rc; + +pub struct Lazy(Rc>); + +pub fn lazy(val: T) -> Lazy { + Lazy::new(val) +} + +impl Lazy { + pub fn new(val: T) -> Self { + Lazy(Rc::new(RefCell::new(val))) + } + + pub fn get(&self) -> Ref { + (*self.0).borrow() + } + + pub fn get_mut(&self) -> RefMut { + (*self.0).borrow_mut() + } + + pub fn set(&self, val: T) { + *self.0.borrow_mut() = val; + } +} + +impl Clone for Lazy { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + +impl fmt::Display for Lazy { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +pub struct Laz(Rc>); + +pub fn laz(val: T) -> Laz { + Laz::new(val) +} + +impl Laz { + pub fn new(val: T) -> Self { + Laz(Rc::new(Cell::new(val))) + } +} + +impl Clone for Laz { + fn clone(&self) -> Self { + Self(Rc::clone(&self.0)) + } +} + +impl Deref for Laz { + type Target = Cell; + + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} + +impl fmt::Display for Laz { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.get().fmt(f) + } +} + +#[derive(Clone, Copy)] +pub struct Color { + r: f32, + g: f32, + b: f32, +} + +impl Color { + pub fn from_hex(hex: u32) -> Self { + let r = ((hex >> 16) & 0xff) as u8; + let g = ((hex >> 8) & 0xff) as u8; + let b = (hex & 0xff) as u8; + Self::from_rgb(r, g, b) + } + + pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { + Self { + r: r as f32 / 255., + g: g as f32 / 255., + b: b as f32 / 255., + } + } + + pub fn rgb_f32(&self) -> (f32, f32, f32) { + ( + (self.r * 255.).clamp(0., 255.).round(), + (self.g * 255.).clamp(0., 255.).round(), + (self.b * 255.).clamp(0., 255.).round(), + ) + } + + pub fn rgb(&self) -> (u8, u8, u8) { + let (r, g, b) = self.rgb_f32(); + (r as u8, g as u8, b as u8) + } + + pub fn rgb_i32(&self) -> (i32, i32, i32) { + let (r, g, b) = self.rgb_f32(); + (r as i32, g as i32, b as i32) + } + + pub fn into_skia(self) -> skia_safe::Color { + let (r, g, b) = self.rgb(); + skia_safe::Color::from_rgb(r, g, b) + } +} + +impl Debug for Color { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.rgb_i32().fmt(f) + } +} + +impl Add for Color { + type Output = Self; + + fn add(mut self, rhs: Self) -> Self::Output { + self.r += rhs.r; + self.g += rhs.g; + self.b += rhs.b; + self + } +} + +impl Sub for Color { + type Output = Self; + + fn sub(mut self, rhs: Self) -> Self::Output { + self.r -= rhs.r; + self.g -= rhs.g; + self.b -= rhs.b; + self + } +} + +impl Mul for Color { + type Output = Self; + + fn mul(mut self, rhs: f32) -> Self::Output { + self.r *= rhs; + self.g *= rhs; + self.b *= rhs; + self + } +} From 241aad84f7f80526d4822593d302677ad58ba0e9 Mon Sep 17 00:00:00 2001 From: Speykious Date: Fri, 21 Apr 2023 19:44:02 +0200 Subject: [PATCH 31/49] Woops, didn't mean to change that --- Cargo.lock | 1 + lokui/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f36a6f6..05275a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -388,6 +388,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniquad" version = "0.4.0-alpha.0" +source = "git+https://github.com/loki-chat/lokinit#2ec39bfacc0830538640ca68addd24da94766b1c" dependencies = [ "libc", "ndk-sys", diff --git a/lokui/Cargo.toml b/lokui/Cargo.toml index 9444e52..6236309 100644 --- a/lokui/Cargo.toml +++ b/lokui/Cargo.toml @@ -9,5 +9,5 @@ edition = "2021" skia-safe = { version = "0.60.0", features = ["gl"] } [dev-dependencies] -# miniquad = { git = "https://github.com/loki-chat/lokinit" } -miniquad = { path = "../../lokinit" } +miniquad = { git = "https://github.com/loki-chat/lokinit" } +# miniquad = { path = "../../lokinit" } From 1b36d4f178f428f9dd8da315513c4de9bc629d24 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sat, 22 Apr 2023 22:30:58 +0200 Subject: [PATCH 32/49] Add `WithBackground` widget wrapper --- lokui/examples/counter.rs | 49 ++++++++---- lokui/src/components/mod.rs | 24 ++++++ lokui/src/components/pane.rs | 38 ++++----- lokui/src/components/wrappers.rs | 127 +++++++++++++++++++++++++++++++ lokui/src/state.rs | 29 ++++--- 5 files changed, 221 insertions(+), 46 deletions(-) create mode 100644 lokui/src/components/wrappers.rs diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index a4749a7..6a190c0 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -3,17 +3,19 @@ use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; -use lokui::components::pane::{pane, Pane}; +use lokui::components::pane::pane; use lokui::components::text::text; +use lokui::components::wrappers::BackgroundState; +use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use lokui::state::{laz, lazy}; +use lokui::state::{laz, lazy, Color}; use lokui::widget::Widget; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; -use skia_safe::{Color, Font, FontStyle, Typeface}; +use skia_safe::{Font, FontStyle, Typeface}; -fn counter() -> Pane { +fn counter() -> impl Widget { let value = laz(0); let increment = { @@ -58,7 +60,12 @@ fn counter() -> Pane { .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) - .child(text(value, font.clone())), + .child(text(value, font.clone())) + .bg(lazy(BackgroundState::new( + Color::from_hex(0xff_33aa55), + 5., + None, + ))), ) .child( button(text("+1", font.clone())) @@ -79,17 +86,27 @@ fn counter() -> Pane { .with_anchor(Anchor::CENTER), ) .on_click(decrement), - ), + ) + .bg(lazy(BackgroundState::new( + Color::from_hex(0x80_657cb1), + 5., + None, + ))), ) + .bg(lazy(BackgroundState::new( + Color::from_hex(0xff_2e428c), + 10., + None, + ))) } -struct Stage { - root_pane: Pane, +struct Stage { + root_widget: W, root_layout: SolvedLayout, window_layout: SolvedLayout, } -impl EventHandler for Stage { +impl EventHandler for Stage { fn update(&mut self, _skia_ctx: &mut SkiaContext) {} fn mouse_button_down_event( @@ -100,7 +117,7 @@ impl EventHandler for Stage { y: f32, ) { let event = Event::MouseDown(MousePosition { x, y }); - self.root_pane.handle_event(event, &self.root_layout); + self.root_widget.handle_event(event, &self.root_layout); } fn mouse_button_up_event( @@ -111,24 +128,24 @@ impl EventHandler for Stage { y: f32, ) { let event = Event::MouseUp(MousePosition { x, y }); - self.root_pane.handle_event(event, &self.root_layout); + self.root_widget.handle_event(event, &self.root_layout); } fn mouse_motion_event(&mut self, _skia_ctx: &mut SkiaContext, x: f32, y: f32) { let event = Event::MouseMove(MousePosition { x, y }); - self.root_pane.handle_event(event, &self.root_layout); + self.root_widget.handle_event(event, &self.root_layout); } fn resize_event(&mut self, skia_ctx: &mut SkiaContext, width: f32, height: f32) { self.window_layout = SolvedLayout::from_top_left(0., 0., width, height); - self.root_layout = self.root_pane.solve_layout(&self.window_layout); + self.root_layout = self.root_widget.solve_layout(&self.window_layout); skia_ctx.recreate_surface(width as i32, height as i32); } fn draw(&mut self, skia_ctx: &mut SkiaContext) { let canvas = skia_ctx.surface.canvas(); - canvas.clear(Color::from(0xff_161a1d)); - self.root_pane.draw(canvas, &self.root_layout); + canvas.clear(skia_safe::Color::from(0xff_161a1d)); + self.root_widget.draw(canvas, &self.root_layout); skia_ctx.dctx.flush(None); } } @@ -152,7 +169,7 @@ fn main() { }, move || { Box::new(Stage { - root_pane, + root_widget: root_pane, root_layout, window_layout, }) diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index d3ada0d..ff74acb 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,3 +1,27 @@ +use crate::state::Lazy; +use crate::widget::Widget; + +use self::wrappers::{BackgroundState, WithBackground}; + pub mod button; pub mod pane; pub mod text; +pub mod wrappers; + +pub trait WidgetExt: Widget { + fn bg(self, state: Lazy) -> WithBackground + where + Self: Sized; +} + +impl WidgetExt for T { + fn bg(self, state: Lazy) -> WithBackground + where + Self: Sized, + { + WithBackground { + widget: self, + state, + } + } +} diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index a8db8e7..54f17c0 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -147,25 +147,25 @@ impl Widget for Pane { writeln!(w, "{}", indentation(deepness)) } - fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { - let rect = Rect::from_xywh( - layout.x_start(), - layout.y_start(), - layout.width(), - layout.height(), - ); - - let mut paint = Paint::default(); - paint.set_anti_alias(true); - paint.set_stroke_width(2.); - - paint.set_stroke(false); - paint.set_color(Color::from(0x40_00cc51)); - canvas.draw_rect(rect, &paint); - - paint.set_stroke(true); - paint.set_color(Color::from(0xff_00cc51)); - canvas.draw_rect(rect, &paint); + fn draw(&self, canvas: &mut Canvas, _layout: &SolvedLayout) { + // let rect = Rect::from_xywh( + // layout.x_start(), + // layout.y_start(), + // layout.width(), + // layout.height(), + // ); + + // let mut paint = Paint::default(); + // paint.set_anti_alias(true); + // paint.set_stroke_width(2.); + + // paint.set_stroke(false); + // paint.set_color(Color::from(0x40_00cc51)); + // canvas.draw_rect(rect, &paint); + + // paint.set_stroke(true); + // paint.set_color(Color::from(0xff_00cc51)); + // canvas.draw_rect(rect, &paint); for child in &self.children { child.widget.draw(canvas, &child.solved_layout); diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs new file mode 100644 index 0000000..6636a11 --- /dev/null +++ b/lokui/src/components/wrappers.rs @@ -0,0 +1,127 @@ +use std::ops::{Deref, DerefMut}; + +use skia_safe::{Canvas, Paint, RRect, Rect}; + +use crate::anim::Property; +use crate::events::Event; +use crate::layout::{Layout, Padding, SolvedLayout}; +use crate::state::{Color, Lazy}; +use crate::widget::Widget; + +pub struct WithPadding { + widget: W, + padding: Padding, +} + +impl Widget for WithPadding { + fn layout(&self) -> &Layout { + self.widget.layout() + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.widget.solve_layout(parent_layout) + } + + fn min_width(&self) -> f32 { + self.widget.min_width() + self.padding.left + self.padding.right + } + + fn min_height(&self) -> f32 { + self.widget.min_height() + self.padding.top + self.padding.bottom + } + + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { + self.widget.draw(canvas, layout); + } + + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { + self.widget.handle_event(event, layout) + } +} + +pub struct BackgroundState { + pub color: Property, + pub border_radius: Property, + pub stroke: Option<(Property, Property)>, +} + +impl BackgroundState { + pub fn new(color: Color, border_radius: f32, stroke: Option<(Color, f32)>) -> Self { + Self { + color: Property::new(color), + border_radius: Property::new(border_radius), + stroke: stroke.map(|(c, w)| (Property::new(c), Property::new(w))), + } + } +} + +pub struct WithBackground { + pub(crate) widget: W, + pub(crate) state: Lazy, +} + +impl Deref for WithBackground { + type Target = W; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl DerefMut for WithBackground { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + +impl Widget for WithBackground { + fn layout(&self) -> &Layout { + self.widget.layout() + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.widget.solve_layout(parent_layout) + } + + fn min_width(&self) -> f32 { + self.widget.min_width() + } + + fn min_height(&self) -> f32 { + self.widget.min_height() + } + + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { + let rect = Rect::from_xywh( + layout.x_start(), + layout.y_start(), + layout.width(), + layout.height(), + ); + + let mut state = self.state.get_mut(); + + let radius = state.border_radius.current(); + let rect = RRect::new_rect_xy(rect, radius, radius); + + let mut paint = Paint::default(); + paint.set_anti_alias(true); + + paint.set_stroke(false); + paint.set_color(state.color.current().into_skia()); + canvas.draw_rrect(rect, &paint); + + if let Some((color, width)) = &mut state.stroke { + paint.set_stroke(true); + paint.set_stroke_width(width.current()); + paint.set_color(color.current().into_skia()); + canvas.draw_rrect(rect, &paint); + } + + self.widget.draw(canvas, layout); + } + + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { + self.widget.handle_event(event, layout) + } +} diff --git a/lokui/src/state.rs b/lokui/src/state.rs index 6217678..3317e5d 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -73,6 +73,7 @@ impl fmt::Display for Laz { #[derive(Clone, Copy)] pub struct Color { + a: f32, r: f32, g: f32, b: f32, @@ -80,41 +81,44 @@ pub struct Color { impl Color { pub fn from_hex(hex: u32) -> Self { + let a = ((hex >> 24) & 0xff) as u8; let r = ((hex >> 16) & 0xff) as u8; let g = ((hex >> 8) & 0xff) as u8; let b = (hex & 0xff) as u8; - Self::from_rgb(r, g, b) + Self::from_argb(a, r, g, b) } - pub fn from_rgb(r: u8, g: u8, b: u8) -> Self { + pub fn from_argb(a: u8, r: u8, g: u8, b: u8) -> Self { Self { + a: a as f32 / 255., r: r as f32 / 255., g: g as f32 / 255., b: b as f32 / 255., } } - pub fn rgb_f32(&self) -> (f32, f32, f32) { + pub fn argb_f32(&self) -> (f32, f32, f32, f32) { ( + (self.a * 255.).clamp(0., 255.).round(), (self.r * 255.).clamp(0., 255.).round(), (self.g * 255.).clamp(0., 255.).round(), (self.b * 255.).clamp(0., 255.).round(), ) } - pub fn rgb(&self) -> (u8, u8, u8) { - let (r, g, b) = self.rgb_f32(); - (r as u8, g as u8, b as u8) + pub fn argb(&self) -> (u8, u8, u8, u8) { + let (a, r, g, b) = self.argb_f32(); + (a as u8, r as u8, g as u8, b as u8) } - pub fn rgb_i32(&self) -> (i32, i32, i32) { - let (r, g, b) = self.rgb_f32(); - (r as i32, g as i32, b as i32) + pub fn rgb_i32(&self) -> (i32, i32, i32, i32) { + let (a, r, g, b) = self.argb_f32(); + (a as i32, r as i32, g as i32, b as i32) } pub fn into_skia(self) -> skia_safe::Color { - let (r, g, b) = self.rgb(); - skia_safe::Color::from_rgb(r, g, b) + let (a, r, g, b) = self.argb(); + skia_safe::Color::from_argb(a, r, g, b) } } @@ -128,6 +132,7 @@ impl Add for Color { type Output = Self; fn add(mut self, rhs: Self) -> Self::Output { + self.a += rhs.a; self.r += rhs.r; self.g += rhs.g; self.b += rhs.b; @@ -139,6 +144,7 @@ impl Sub for Color { type Output = Self; fn sub(mut self, rhs: Self) -> Self::Output { + self.a -= rhs.a; self.r -= rhs.r; self.g -= rhs.g; self.b -= rhs.b; @@ -150,6 +156,7 @@ impl Mul for Color { type Output = Self; fn mul(mut self, rhs: f32) -> Self::Output { + self.a *= rhs; self.r *= rhs; self.g *= rhs; self.b *= rhs; From 7edfde920e949e2986e6ac28816ac4542be6bdc9 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 24 Apr 2023 22:45:42 +0200 Subject: [PATCH 33/49] Add a `WidgetContainer` trait This solves the problem of not being able to add children after wrapping a pane with another component. --- lokui/examples/counter.rs | 24 ++++++++++---------- lokui/src/components/pane.rs | 38 +++++--------------------------- lokui/src/components/wrappers.rs | 14 ++++++++++++ lokui/src/widget.rs | 29 ++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 44 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 6a190c0..a758177 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -10,7 +10,7 @@ use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; use lokui::state::{laz, lazy, Color}; -use lokui::widget::Widget; +use lokui::widget::{Widget, WidgetContainer}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; @@ -44,6 +44,11 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER) .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), ) + .bg(lazy(BackgroundState::new( + Color::from_hex(0xff_2e428c), + 10., + None, + ))) .child( pane() .with_padding(Padding::vh(5., 10.)) @@ -52,6 +57,11 @@ fn counter() -> impl Widget { gap: 5., }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) + .bg(lazy(BackgroundState::new( + Color::from_hex(0x80_657cb1), + 5., + None, + ))) .child( pane() .with_layout( @@ -86,18 +96,8 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER), ) .on_click(decrement), - ) - .bg(lazy(BackgroundState::new( - Color::from_hex(0x80_657cb1), - 5., - None, - ))), + ), ) - .bg(lazy(BackgroundState::new( - Color::from_hex(0xff_2e428c), - 10., - None, - ))) } struct Stage { diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 54f17c0..ee7e137 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -1,11 +1,11 @@ use std::io; -use skia_safe::{Canvas, Color, Paint, Rect}; +use skia_safe::Canvas; use crate::events::{Event, MousePosition}; use crate::indentation; use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use crate::widget::{default_solve_layout, solve_height, solve_width, Widget}; +use crate::widget::{default_solve_layout, solve_height, solve_width, Widget, WidgetContainer}; struct PaneChild { widget: Box, @@ -50,21 +50,14 @@ impl Pane { self.flex_layout = Some(flex_layout); self } +} - pub fn child(mut self, widget: impl Widget + 'static) -> Self { - self.add_child(widget); - self - } - - pub fn add_child(&mut self, widget: impl Widget + 'static) { - self.add_dyn_child(Box::new(widget)); - } - - pub fn add_dyn_child(&mut self, widget: Box) { +impl WidgetContainer for Pane { + fn add_dyn_child(&mut self, widget: Box) { self.children.push(PaneChild::new(widget)); } - pub fn pop_child(&mut self) -> Option> { + fn pop_child(&mut self) -> Option> { self.children.pop().map(|child| child.widget) } } @@ -148,25 +141,6 @@ impl Widget for Pane { } fn draw(&self, canvas: &mut Canvas, _layout: &SolvedLayout) { - // let rect = Rect::from_xywh( - // layout.x_start(), - // layout.y_start(), - // layout.width(), - // layout.height(), - // ); - - // let mut paint = Paint::default(); - // paint.set_anti_alias(true); - // paint.set_stroke_width(2.); - - // paint.set_stroke(false); - // paint.set_color(Color::from(0x40_00cc51)); - // canvas.draw_rect(rect, &paint); - - // paint.set_stroke(true); - // paint.set_color(Color::from(0xff_00cc51)); - // canvas.draw_rect(rect, &paint); - for child in &self.children { child.widget.draw(canvas, &child.solved_layout); } diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs index 6636a11..de43ff8 100644 --- a/lokui/src/components/wrappers.rs +++ b/lokui/src/components/wrappers.rs @@ -13,6 +13,20 @@ pub struct WithPadding { padding: Padding, } +impl Deref for WithPadding { + type Target = W; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl DerefMut for WithPadding { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + impl Widget for WithPadding { fn layout(&self) -> &Layout { self.widget.layout() diff --git a/lokui/src/widget.rs b/lokui/src/widget.rs index 461a1c6..c1aaa5e 100644 --- a/lokui/src/widget.rs +++ b/lokui/src/widget.rs @@ -1,4 +1,5 @@ use std::io; +use std::ops::DerefMut; use skia_safe::Canvas; @@ -61,3 +62,31 @@ pub fn default_solve_layout( let (x, y) = parent_layout.point_at_anchor(widget.layout().anchor); SolvedLayout::from_origin(widget.layout().origin, x, y, width, height) } + +pub trait WidgetContainer { + fn pop_child(&mut self) -> Option>; + fn add_dyn_child(&mut self, widget: Box); + + fn add_child(&mut self, widget: impl Widget + 'static) { + self.add_dyn_child(Box::new(widget)); + } + + fn child(mut self, widget: impl Widget + 'static) -> Self + where + Self: Sized, + { + self.add_child(widget); + self + } +} + +/// Anything that mut-derefs to a WidgetContainer is also a WidgetContainer. +impl> WidgetContainer for W { + fn add_dyn_child(&mut self, widget: Box) { + self.deref_mut().add_dyn_child(widget); + } + + fn pop_child(&mut self) -> Option> { + self.deref_mut().pop_child() + } +} From a927665e860913a90b6cce04462a26312f28eb8d Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 24 Apr 2023 22:55:23 +0200 Subject: [PATCH 34/49] Remove `Laz` It wasn't really useful --- lokui/examples/counter.rs | 8 ++++---- lokui/src/components/button.rs | 10 +++++----- lokui/src/state.rs | 32 -------------------------------- 3 files changed, 9 insertions(+), 41 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index a758177..7eba2d9 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -9,19 +9,19 @@ use lokui::components::wrappers::BackgroundState; use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use lokui::state::{laz, lazy, Color}; +use lokui::state::{lazy, Color}; use lokui::widget::{Widget, WidgetContainer}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; fn counter() -> impl Widget { - let value = laz(0); + let value = lazy(0); let increment = { let value = value.clone(); move |_, _| { - value.set(value.get() + 1); + value.set(*value.get() + 1); println!("+1! Counter = {}", value.get()); } }; @@ -29,7 +29,7 @@ fn counter() -> impl Widget { let decrement = { let value = value.clone(); move |_, _| { - value.set(value.get() - 1); + value.set(*value.get() - 1); println!("-1! Counter = {}", value.get()); } }; diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs index f1ba84a..eae7796 100644 --- a/lokui/src/components/button.rs +++ b/lokui/src/components/button.rs @@ -8,7 +8,7 @@ use crate::anim::{ease, Property}; use crate::events::{Event, MousePosition}; use crate::indentation; use crate::layout::{Layout, Padding, SolvedLayout}; -use crate::state::{lazy, Color, Laz, Lazy}; +use crate::state::{lazy, Color, Lazy}; use crate::widget::{default_solve_layout, Widget}; use super::text::Text; @@ -19,7 +19,7 @@ pub struct Button { text: Text, color: Lazy>, on_click: Option>, - enabled: Laz, + enabled: Lazy, is_mouse_down: bool, hovered: bool, } @@ -31,7 +31,7 @@ pub fn button(text: Text) -> Button { text, color: lazy(Property::new(Color::from_hex(0xff0051))), on_click: None, - enabled: Laz::new(true), + enabled: Lazy::new(true), is_mouse_down: false, hovered: false, } @@ -103,7 +103,7 @@ impl Widget for Button { } fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { - if self.enabled.get() { + if *self.enabled.get() { let handled = match event { Event::MouseDown(_) => { self.is_mouse_down = true; @@ -132,7 +132,7 @@ impl Widget for Button { _ => return false, }; - let color = match (self.enabled.get(), self.is_mouse_down) { + let color = match (*self.enabled.get(), self.is_mouse_down) { (true, true) => 0xa70038, (true, false) if self.hovered => 0x51ffff, (true, false) => 0xff0051, diff --git a/lokui/src/state.rs b/lokui/src/state.rs index 3317e5d..aa1242c 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -39,38 +39,6 @@ impl fmt::Display for Lazy { } } -pub struct Laz(Rc>); - -pub fn laz(val: T) -> Laz { - Laz::new(val) -} - -impl Laz { - pub fn new(val: T) -> Self { - Laz(Rc::new(Cell::new(val))) - } -} - -impl Clone for Laz { - fn clone(&self) -> Self { - Self(Rc::clone(&self.0)) - } -} - -impl Deref for Laz { - type Target = Cell; - - fn deref(&self) -> &Self::Target { - self.0.as_ref() - } -} - -impl fmt::Display for Laz { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.get().fmt(f) - } -} - #[derive(Clone, Copy)] pub struct Color { a: f32, From 289f75237964c9a2fa3db558af3907b2eff34985 Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 24 Apr 2023 22:57:05 +0200 Subject: [PATCH 35/49] Remove unused imports --- lokui/src/state.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lokui/src/state.rs b/lokui/src/state.rs index aa1242c..b5a09d7 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -1,6 +1,6 @@ -use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::cell::{Ref, RefCell, RefMut}; use std::fmt::{self, Debug}; -use std::ops::{Add, Deref, Mul, Sub}; +use std::ops::{Add, Mul, Sub}; use std::rc::Rc; pub struct Lazy(Rc>); From 6393c0de476f7f14c14d1358224371437b4fd1aa Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 24 Apr 2023 23:06:17 +0200 Subject: [PATCH 36/49] Use `get_mut()` in example --- lokui/examples/counter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 7eba2d9..8f736ae 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -21,7 +21,7 @@ fn counter() -> impl Widget { let increment = { let value = value.clone(); move |_, _| { - value.set(*value.get() + 1); + *value.get_mut() += 1; println!("+1! Counter = {}", value.get()); } }; @@ -29,7 +29,7 @@ fn counter() -> impl Widget { let decrement = { let value = value.clone(); move |_, _| { - value.set(*value.get() - 1); + *value.get_mut() -= 1; println!("-1! Counter = {}", value.get()); } }; From 1aed813562c304f592d34f7f6d2056c0369e46ab Mon Sep 17 00:00:00 2001 From: Speykious Date: Mon, 24 Apr 2023 23:07:45 +0200 Subject: [PATCH 37/49] Remove now useless `set` method for `Lazy` --- lokui/src/state.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lokui/src/state.rs b/lokui/src/state.rs index b5a09d7..2fa6dc3 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -21,10 +21,6 @@ impl Lazy { pub fn get_mut(&self) -> RefMut { (*self.0).borrow_mut() } - - pub fn set(&self, val: T) { - *self.0.borrow_mut() = val; - } } impl Clone for Lazy { From 27f1aed3d17ea2f6dd173c4c2feededecddcbe61 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 25 Apr 2023 10:39:35 +0200 Subject: [PATCH 38/49] Make padding a wrapper --- lokui/examples/counter.rs | 21 ++++--------- lokui/src/components/mod.rs | 25 ++++++++++++--- lokui/src/components/pane.rs | 22 ++++--------- lokui/src/components/wrappers.rs | 53 ++++++++++++-------------------- lokui/src/state.rs | 18 +++++++++++ 5 files changed, 69 insertions(+), 70 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 8f736ae..96863b5 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -5,11 +5,10 @@ use std::io::{stdout, BufWriter, Write}; use lokui::components::button::button; use lokui::components::pane::pane; use lokui::components::text::text; -use lokui::components::wrappers::BackgroundState; use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use lokui::state::{lazy, Color}; +use lokui::state::{lazy, Color, RectState}; use lokui::widget::{Widget, WidgetContainer}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; @@ -38,30 +37,26 @@ fn counter() -> impl Widget { let font = lazy(Font::new(typeface, Some(20.))); pane() - .with_padding(Padding::splat(10.)) .with_layout( Layout::new() .with_anchor(Anchor::CENTER) .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), ) - .bg(lazy(BackgroundState::new( + .padding(Padding::splat(10.)) + .bg(lazy(RectState::new( Color::from_hex(0xff_2e428c), 10., None, ))) .child( pane() - .with_padding(Padding::vh(5., 10.)) .with_flex_layout(FlexLayout { direction: Direction::Horizontal, gap: 5., }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) - .bg(lazy(BackgroundState::new( - Color::from_hex(0x80_657cb1), - 5., - None, - ))) + .padding(Padding::vh(5., 10.)) + .bg(lazy(RectState::new(Color::from_hex(0x80_657cb1), 5., None))) .child( pane() .with_layout( @@ -71,11 +66,7 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER), ) .child(text(value, font.clone())) - .bg(lazy(BackgroundState::new( - Color::from_hex(0xff_33aa55), - 5., - None, - ))), + .bg(lazy(RectState::new(Color::from_hex(0xff_33aa55), 5., None))), ) .child( button(text("+1", font.clone())) diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index ff74acb..3b6d7d9 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,7 +1,8 @@ -use crate::state::Lazy; +use crate::layout::Padding; +use crate::state::{Lazy, RectState}; use crate::widget::Widget; -use self::wrappers::{BackgroundState, WithBackground}; +use self::wrappers::{WithBg, WithPadding}; pub mod button; pub mod pane; @@ -9,19 +10,33 @@ pub mod text; pub mod wrappers; pub trait WidgetExt: Widget { - fn bg(self, state: Lazy) -> WithBackground + fn bg(self, bg: Lazy) -> WithBg + where + Self: Sized; + + fn padding(self, padding: Padding) -> WithPadding where Self: Sized; } impl WidgetExt for T { - fn bg(self, state: Lazy) -> WithBackground + fn bg(self, state: Lazy) -> WithBg where Self: Sized, { - WithBackground { + WithBg { widget: self, state, } } + + fn padding(self, padding: Padding) -> WithPadding + where + Self: Sized, + { + WithPadding { + widget: self, + padding, + } + } } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index ee7e137..fb9535e 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -4,7 +4,7 @@ use skia_safe::Canvas; use crate::events::{Event, MousePosition}; use crate::indentation; -use crate::layout::{DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; +use crate::layout::{DimScalar, Direction, FlexLayout, Layout, SolvedLayout}; use crate::widget::{default_solve_layout, solve_height, solve_width, Widget, WidgetContainer}; struct PaneChild { @@ -26,7 +26,6 @@ impl PaneChild { #[derive(Default)] pub struct Pane { layout: Layout, - padding: Padding, flex_layout: Option, children: Vec, } @@ -41,11 +40,6 @@ impl Pane { self } - pub fn with_padding(mut self, padding: Padding) -> Self { - self.padding = padding; - self - } - pub fn with_flex_layout(mut self, flex_layout: FlexLayout) -> Self { self.flex_layout = Some(flex_layout); self @@ -70,7 +64,7 @@ impl Widget for Pane { fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { let layout = default_solve_layout(self, parent_layout); - let inner_layout = layout.padded(self.padding); + let inner_layout = layout; if let Some(flex_layout) = &self.flex_layout { solve_flex_layout(flex_layout, &mut self.children, inner_layout); } else { @@ -85,8 +79,6 @@ impl Widget for Pane { } fn min_width(&self) -> f32 { - let width_pad = self.padding.left + self.padding.right; - if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter()) @@ -96,7 +88,7 @@ impl Widget for Pane { }) .sum(); - return inner_min_width + width_pad; + return inner_min_width; } } @@ -105,12 +97,10 @@ impl Widget for Pane { .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); - inner_min_width + width_pad + inner_min_width } fn min_height(&self) -> f32 { - let height_pad = self.padding.top + self.padding.bottom; - if let Some(flex_layout) = self.flex_layout.as_ref() { if flex_layout.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter()) @@ -120,7 +110,7 @@ impl Widget for Pane { }) .sum(); - return inner_min_height + height_pad; + return inner_min_height; } } @@ -129,7 +119,7 @@ impl Widget for Pane { .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); - inner_min_height + height_pad + inner_min_height } fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs index de43ff8..d32f3a2 100644 --- a/lokui/src/components/wrappers.rs +++ b/lokui/src/components/wrappers.rs @@ -2,29 +2,30 @@ use std::ops::{Deref, DerefMut}; use skia_safe::{Canvas, Paint, RRect, Rect}; -use crate::anim::Property; use crate::events::Event; -use crate::layout::{Layout, Padding, SolvedLayout}; -use crate::state::{Color, Lazy}; +use crate::layout::{Layout, SolvedLayout, Padding}; +use crate::state::{Lazy, RectState}; use crate::widget::Widget; +// padding + pub struct WithPadding { - widget: W, - padding: Padding, + pub(super) widget: W, + pub(super) padding: Padding, } impl Deref for WithPadding { - type Target = W; + type Target = W; - fn deref(&self) -> &Self::Target { - &self.widget - } + fn deref(&self) -> &Self::Target { + &self.widget + } } impl DerefMut for WithPadding { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.widget - } + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } } impl Widget for WithPadding { @@ -53,28 +54,12 @@ impl Widget for WithPadding { } } -pub struct BackgroundState { - pub color: Property, - pub border_radius: Property, - pub stroke: Option<(Property, Property)>, -} - -impl BackgroundState { - pub fn new(color: Color, border_radius: f32, stroke: Option<(Color, f32)>) -> Self { - Self { - color: Property::new(color), - border_radius: Property::new(border_radius), - stroke: stroke.map(|(c, w)| (Property::new(c), Property::new(w))), - } - } -} - -pub struct WithBackground { - pub(crate) widget: W, - pub(crate) state: Lazy, +pub struct WithBg { + pub(super) widget: W, + pub(super) state: Lazy, } -impl Deref for WithBackground { +impl Deref for WithBg { type Target = W; fn deref(&self) -> &Self::Target { @@ -82,13 +67,13 @@ impl Deref for WithBackground { } } -impl DerefMut for WithBackground { +impl DerefMut for WithBg { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.widget } } -impl Widget for WithBackground { +impl Widget for WithBg { fn layout(&self) -> &Layout { self.widget.layout() } diff --git a/lokui/src/state.rs b/lokui/src/state.rs index 2fa6dc3..12d7ce2 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -3,6 +3,8 @@ use std::fmt::{self, Debug}; use std::ops::{Add, Mul, Sub}; use std::rc::Rc; +use crate::anim::Property; + pub struct Lazy(Rc>); pub fn lazy(val: T) -> Lazy { @@ -127,3 +129,19 @@ impl Mul for Color { self } } + +pub struct RectState { + pub color: Property, + pub border_radius: Property, + pub stroke: Option<(Property, Property)>, +} + +impl RectState { + pub fn new(color: Color, border_radius: f32, stroke: Option<(Color, f32)>) -> Self { + Self { + color: Property::new(color), + border_radius: Property::new(border_radius), + stroke: stroke.map(|(c, w)| (Property::new(c), Property::new(w))), + } + } +} From b2a4f5f6c8c11ef4c9351b86201be1def8e8fcc6 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 25 Apr 2023 10:45:25 +0200 Subject: [PATCH 39/49] Slightly decrease verbosity w/ color in RectState --- lokui/examples/counter.rs | 12 ++++-------- lokui/src/state.rs | 10 ++++++++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 96863b5..6b12abf 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -8,7 +8,7 @@ use lokui::components::text::text; use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; -use lokui::state::{lazy, Color, RectState}; +use lokui::state::{lazy, RectState}; use lokui::widget::{Widget, WidgetContainer}; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; @@ -43,11 +43,7 @@ fn counter() -> impl Widget { .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), ) .padding(Padding::splat(10.)) - .bg(lazy(RectState::new( - Color::from_hex(0xff_2e428c), - 10., - None, - ))) + .bg(lazy(RectState::new(0xff_2e428c, 10., None))) .child( pane() .with_flex_layout(FlexLayout { @@ -56,7 +52,7 @@ fn counter() -> impl Widget { }) .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) .padding(Padding::vh(5., 10.)) - .bg(lazy(RectState::new(Color::from_hex(0x80_657cb1), 5., None))) + .bg(lazy(RectState::new(0x80_657cb1, 5., None))) .child( pane() .with_layout( @@ -66,7 +62,7 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER), ) .child(text(value, font.clone())) - .bg(lazy(RectState::new(Color::from_hex(0xff_33aa55), 5., None))), + .bg(lazy(RectState::new(0xff_33aa55, 5., None))), ) .child( button(text("+1", font.clone())) diff --git a/lokui/src/state.rs b/lokui/src/state.rs index 12d7ce2..40b57ad 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -130,6 +130,12 @@ impl Mul for Color { } } +impl From for Color { + fn from(value: u32) -> Self { + Self::from_hex(value) + } +} + pub struct RectState { pub color: Property, pub border_radius: Property, @@ -137,9 +143,9 @@ pub struct RectState { } impl RectState { - pub fn new(color: Color, border_radius: f32, stroke: Option<(Color, f32)>) -> Self { + pub fn new(color: impl Into, border_radius: f32, stroke: Option<(Color, f32)>) -> Self { Self { - color: Property::new(color), + color: Property::new(color.into()), border_radius: Property::new(border_radius), stroke: stroke.map(|(c, w)| (Property::new(c), Property::new(w))), } From 58f247d3617dcc1b903365bc495d0ba20413a2b2 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 25 Apr 2023 10:51:55 +0200 Subject: [PATCH 40/49] Rename `FlexLayout` to `Flex` --- lokui/examples/counter.rs | 4 ++-- lokui/src/components/pane.rs | 32 ++++++++++++++++---------------- lokui/src/layout/mod.rs | 2 +- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 6b12abf..1c783d0 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -7,7 +7,7 @@ use lokui::components::pane::pane; use lokui::components::text::text; use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; -use lokui::layout::{Anchor, DimScalar, Direction, FlexLayout, Layout, Padding, SolvedLayout}; +use lokui::layout::{Anchor, DimScalar, Direction, Flex, Layout, Padding, SolvedLayout}; use lokui::state::{lazy, RectState}; use lokui::widget::{Widget, WidgetContainer}; use miniquad::skia::SkiaContext; @@ -46,7 +46,7 @@ fn counter() -> impl Widget { .bg(lazy(RectState::new(0xff_2e428c, 10., None))) .child( pane() - .with_flex_layout(FlexLayout { + .with_flex(Flex { direction: Direction::Horizontal, gap: 5., }) diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index fb9535e..7988d8f 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -4,7 +4,7 @@ use skia_safe::Canvas; use crate::events::{Event, MousePosition}; use crate::indentation; -use crate::layout::{DimScalar, Direction, FlexLayout, Layout, SolvedLayout}; +use crate::layout::{DimScalar, Direction, Flex, Layout, SolvedLayout}; use crate::widget::{default_solve_layout, solve_height, solve_width, Widget, WidgetContainer}; struct PaneChild { @@ -26,7 +26,7 @@ impl PaneChild { #[derive(Default)] pub struct Pane { layout: Layout, - flex_layout: Option, + flex: Option, children: Vec, } @@ -40,8 +40,8 @@ impl Pane { self } - pub fn with_flex_layout(mut self, flex_layout: FlexLayout) -> Self { - self.flex_layout = Some(flex_layout); + pub fn with_flex(mut self, flex: Flex) -> Self { + self.flex = Some(flex); self } } @@ -65,8 +65,8 @@ impl Widget for Pane { let layout = default_solve_layout(self, parent_layout); let inner_layout = layout; - if let Some(flex_layout) = &self.flex_layout { - solve_flex_layout(flex_layout, &mut self.children, inner_layout); + if let Some(flex) = &self.flex { + solve_flex_layout(flex, &mut self.children, inner_layout); } else { // Without flex-layout, all children are superposed to each other. @@ -79,8 +79,8 @@ impl Widget for Pane { } fn min_width(&self) -> f32 { - if let Some(flex_layout) = self.flex_layout.as_ref() { - if flex_layout.direction == Direction::Horizontal { + if let Some(flex) = self.flex.as_ref() { + if flex.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter()) .map(|child| match child.widget.layout().width { DimScalar::Fixed(w) => w, @@ -101,8 +101,8 @@ impl Widget for Pane { } fn min_height(&self) -> f32 { - if let Some(flex_layout) = self.flex_layout.as_ref() { - if flex_layout.direction == Direction::Vertical { + if let Some(flex) = self.flex.as_ref() { + if flex.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter()) .map(|child| match child.widget.layout().height { DimScalar::Fixed(h) => h, @@ -172,11 +172,11 @@ impl Widget for Pane { /// /// With a flex layout, all children are placed next to each other, vertically or horizontally. fn solve_flex_layout( - flex_layout: &FlexLayout, + flex: &Flex, children: &mut [PaneChild], inner_layout: SolvedLayout, ) { - match flex_layout.direction { + match flex.direction { Direction::Horizontal => { let fills_count = (children.iter()) .filter(|child| child.widget.layout().width.is_fill()) @@ -189,7 +189,7 @@ fn solve_flex_layout( .filter_map(|child| solve_width(child.widget.as_ref())) .sum(); - let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; + let gap_width = flex.gap * children.len().saturating_sub(1) as f32; let leftover_width = (inner_layout.width() - fixed_width - gap_width).max(0.); leftover_width / fills_count as f32 }; @@ -202,7 +202,7 @@ fn solve_flex_layout( let inner_layout = inner_layout.with_width(child_width).with_x(x); child.solved_layout = child.widget.solve_layout(&inner_layout); - x += child_width + flex_layout.gap; + x += child_width + flex.gap; } } Direction::Vertical => { @@ -220,7 +220,7 @@ fn solve_flex_layout( .filter_map(|child| solve_height(child.widget.as_ref())) .sum(); - let gap_width = flex_layout.gap * children.len().saturating_sub(1) as f32; + let gap_width = flex.gap * children.len().saturating_sub(1) as f32; let leftover_height = (inner_layout.height() - fixed_height - gap_width).max(0.); leftover_height / fills_count as f32 }; @@ -231,7 +231,7 @@ fn solve_flex_layout( let inner_layout = inner_layout.with_height(child_height).with_y(y); child.solved_layout = child.widget.solve_layout(&inner_layout); - y += child_height + flex_layout.gap; + y += child_height + flex.gap; } } } diff --git a/lokui/src/layout/mod.rs b/lokui/src/layout/mod.rs index 26afb4c..1a4687c 100644 --- a/lokui/src/layout/mod.rs +++ b/lokui/src/layout/mod.rs @@ -217,7 +217,7 @@ pub enum Direction { } #[derive(Clone, Debug, Default)] -pub struct FlexLayout { +pub struct Flex { pub direction: Direction, pub gap: f32, } From 363a9f0efa46bbd8794fe9a3fe9105b85b421fe7 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 25 Apr 2023 10:57:53 +0200 Subject: [PATCH 41/49] Introduce a prelude module for ease of imports --- lokui/examples/counter.rs | 20 ++++++++------------ lokui/src/lib.rs | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 1c783d0..1bd6af6 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -2,14 +2,10 @@ use std::io::{stdout, BufWriter, Write}; -use lokui::components::button::button; -use lokui::components::pane::pane; -use lokui::components::text::text; -use lokui::components::WidgetExt; use lokui::events::{Event, MousePosition}; -use lokui::layout::{Anchor, DimScalar, Direction, Flex, Layout, Padding, SolvedLayout}; -use lokui::state::{lazy, RectState}; -use lokui::widget::{Widget, WidgetContainer}; +use lokui::layout::SolvedLayout; +use lokui::prelude::*; + use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; @@ -40,7 +36,7 @@ fn counter() -> impl Widget { .with_layout( Layout::new() .with_anchor(Anchor::CENTER) - .with_dimension(DimScalar::Fixed(400.), DimScalar::Fixed(250.)), + .with_dimension(Fixed(400.), Fixed(250.)), ) .padding(Padding::splat(10.)) .bg(lazy(RectState::new(0xff_2e428c, 10., None))) @@ -50,14 +46,14 @@ fn counter() -> impl Widget { direction: Direction::Horizontal, gap: 5., }) - .with_layout(Layout::new().with_dimension(DimScalar::Fill, DimScalar::Fill)) + .with_layout(Layout::new().with_dimension(Fill, Fill)) .padding(Padding::vh(5., 10.)) .bg(lazy(RectState::new(0x80_657cb1, 5., None))) .child( pane() .with_layout( Layout::new() - .with_dimension(DimScalar::Fill, DimScalar::Fixed(50.)) + .with_dimension(Fill, Fixed(50.)) .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) @@ -68,7 +64,7 @@ fn counter() -> impl Widget { button(text("+1", font.clone())) .with_layout( Layout::new() - .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_dimension(Fixed(80.), Fixed(50.)) .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) @@ -78,7 +74,7 @@ fn counter() -> impl Widget { button(text("-1", font)) .with_layout( Layout::new() - .with_dimension(DimScalar::Fixed(80.), DimScalar::Fixed(50.)) + .with_dimension(Fixed(80.), Fixed(50.)) .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index 1959dad..96a5fdc 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -7,6 +7,21 @@ pub mod layout; pub mod state; pub mod widget; +pub mod prelude { + pub use crate::components::WidgetExt; + + pub use crate::components::button::button; + pub use crate::components::pane::pane; + pub use crate::components::text::text; + + pub use crate::layout::anchor::Anchor; + pub use crate::layout::padding::Padding; + pub use crate::layout::DimScalar::*; + pub use crate::layout::{Direction, Flex, Layout}; + pub use crate::state::{lazy, RectState}; + pub use crate::widget::{Widget, WidgetContainer}; +} + pub fn indentation(n: usize) -> String { " ".repeat(n) } From 9a7247a81ff87125553e9e22ed3f0c65818d02d6 Mon Sep 17 00:00:00 2001 From: Speykious Date: Tue, 25 Apr 2023 11:18:21 +0200 Subject: [PATCH 42/49] Actually making padding a wrapper was a bad idea --- lokui/examples/counter.rs | 4 +-- lokui/src/components/mod.rs | 17 +----------- lokui/src/components/pane.rs | 22 +++++++++++---- lokui/src/components/wrappers.rs | 47 +------------------------------- 4 files changed, 20 insertions(+), 70 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 1bd6af6..f4aa62a 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -38,7 +38,7 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER) .with_dimension(Fixed(400.), Fixed(250.)), ) - .padding(Padding::splat(10.)) + .with_padding(Padding::splat(10.)) .bg(lazy(RectState::new(0xff_2e428c, 10., None))) .child( pane() @@ -47,7 +47,7 @@ fn counter() -> impl Widget { gap: 5., }) .with_layout(Layout::new().with_dimension(Fill, Fill)) - .padding(Padding::vh(5., 10.)) + .with_padding(Padding::vh(5., 10.)) .bg(lazy(RectState::new(0x80_657cb1, 5., None))) .child( pane() diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index 3b6d7d9..13426b5 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,8 +1,7 @@ -use crate::layout::Padding; use crate::state::{Lazy, RectState}; use crate::widget::Widget; -use self::wrappers::{WithBg, WithPadding}; +use self::wrappers::WithBg; pub mod button; pub mod pane; @@ -13,10 +12,6 @@ pub trait WidgetExt: Widget { fn bg(self, bg: Lazy) -> WithBg where Self: Sized; - - fn padding(self, padding: Padding) -> WithPadding - where - Self: Sized; } impl WidgetExt for T { @@ -29,14 +24,4 @@ impl WidgetExt for T { state, } } - - fn padding(self, padding: Padding) -> WithPadding - where - Self: Sized, - { - WithPadding { - widget: self, - padding, - } - } } diff --git a/lokui/src/components/pane.rs b/lokui/src/components/pane.rs index 7988d8f..cf4d98b 100644 --- a/lokui/src/components/pane.rs +++ b/lokui/src/components/pane.rs @@ -4,7 +4,7 @@ use skia_safe::Canvas; use crate::events::{Event, MousePosition}; use crate::indentation; -use crate::layout::{DimScalar, Direction, Flex, Layout, SolvedLayout}; +use crate::layout::{DimScalar, Direction, Flex, Layout, SolvedLayout, Padding}; use crate::widget::{default_solve_layout, solve_height, solve_width, Widget, WidgetContainer}; struct PaneChild { @@ -26,6 +26,7 @@ impl PaneChild { #[derive(Default)] pub struct Pane { layout: Layout, + padding: Padding, flex: Option, children: Vec, } @@ -40,6 +41,11 @@ impl Pane { self } + pub fn with_padding(mut self, padding: Padding) -> Self { + self.padding = padding; + self + } + pub fn with_flex(mut self, flex: Flex) -> Self { self.flex = Some(flex); self @@ -64,7 +70,7 @@ impl Widget for Pane { fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { let layout = default_solve_layout(self, parent_layout); - let inner_layout = layout; + let inner_layout = layout.padded(self.padding); if let Some(flex) = &self.flex { solve_flex_layout(flex, &mut self.children, inner_layout); } else { @@ -79,6 +85,8 @@ impl Widget for Pane { } fn min_width(&self) -> f32 { + let width_pad = self.padding.left + self.padding.right; + if let Some(flex) = self.flex.as_ref() { if flex.direction == Direction::Horizontal { let inner_min_width: f32 = (self.children.iter()) @@ -88,7 +96,7 @@ impl Widget for Pane { }) .sum(); - return inner_min_width; + return inner_min_width + width_pad; } } @@ -97,10 +105,12 @@ impl Widget for Pane { .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); - inner_min_width + inner_min_width + width_pad } fn min_height(&self) -> f32 { + let height_pad = self.padding.top + self.padding.bottom; + if let Some(flex) = self.flex.as_ref() { if flex.direction == Direction::Vertical { let inner_min_height: f32 = (self.children.iter()) @@ -110,7 +120,7 @@ impl Widget for Pane { }) .sum(); - return inner_min_height; + return inner_min_height + height_pad; } } @@ -119,7 +129,7 @@ impl Widget for Pane { .max_by(|x, y| x.total_cmp(y)) .unwrap_or_default(); - inner_min_height + inner_min_height + height_pad } fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs index d32f3a2..9a121bb 100644 --- a/lokui/src/components/wrappers.rs +++ b/lokui/src/components/wrappers.rs @@ -3,57 +3,12 @@ use std::ops::{Deref, DerefMut}; use skia_safe::{Canvas, Paint, RRect, Rect}; use crate::events::Event; -use crate::layout::{Layout, SolvedLayout, Padding}; +use crate::layout::{Layout, SolvedLayout}; use crate::state::{Lazy, RectState}; use crate::widget::Widget; // padding -pub struct WithPadding { - pub(super) widget: W, - pub(super) padding: Padding, -} - -impl Deref for WithPadding { - type Target = W; - - fn deref(&self) -> &Self::Target { - &self.widget - } -} - -impl DerefMut for WithPadding { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.widget - } -} - -impl Widget for WithPadding { - fn layout(&self) -> &Layout { - self.widget.layout() - } - - fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - self.widget.solve_layout(parent_layout) - } - - fn min_width(&self) -> f32 { - self.widget.min_width() + self.padding.left + self.padding.right - } - - fn min_height(&self) -> f32 { - self.widget.min_height() + self.padding.top + self.padding.bottom - } - - fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { - self.widget.draw(canvas, layout); - } - - fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { - self.widget.handle_event(event, layout) - } -} - pub struct WithBg { pub(super) widget: W, pub(super) state: Lazy, From 8fcefa3de58c6c0a07c36261675d9f7bc4860fb6 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 14 May 2023 22:09:39 +0200 Subject: [PATCH 43/49] Add WithOnCLick and WithOnEvent wrappers --- lokui/examples/counter.rs | 106 ++++++++++++++-------- lokui/src/components/button.rs | 150 ------------------------------- lokui/src/components/mod.rs | 33 ++++++- lokui/src/components/wrappers.rs | 123 +++++++++++++++++++++++-- lokui/src/lib.rs | 3 +- 5 files changed, 218 insertions(+), 197 deletions(-) delete mode 100644 lokui/src/components/button.rs diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index f4aa62a..141707e 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -1,35 +1,77 @@ #![allow(clippy::unusual_byte_groupings)] use std::io::{stdout, BufWriter, Write}; +use std::time::Duration; +use lokui::anim::ease; use lokui::events::{Event, MousePosition}; use lokui::layout::SolvedLayout; use lokui::prelude::*; +use lokui::state::Color; use miniquad::skia::SkiaContext; use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; +/// Curried increment callback. +fn increment(value: Lazy) -> impl Fn(f32, f32) { + move |_, _| { + *value.get_mut() += 1; + println!("+1! Counter = {}", value.get()); + } +} + +/// Curried decrement callback. +fn decrement(value: Lazy) -> impl Fn(f32, f32) { + move |_, _| { + *value.get_mut() -= 1; + println!("-1! Counter = {}", value.get()); + } +} + +fn counter_button_color_handler(bg: Lazy) -> impl FnMut(Event) -> bool { + /// wrapper struct to have a simple non-copy background color + struct BgColor(u32); + + let mut bg_color = BgColor(0xff0051); + move |event| { + bg_color.0 = match event { + Event::MouseDown(_) => 0xa70038, + Event::MouseIn | Event::MouseUp(_) => 0x51ffff, + Event::MouseOut => 0xff0051, + _ => bg_color.0, + }; + + let color = Color::from_hex(0xff_000000 | bg_color.0); + (bg.get_mut().color).go_to(color, ease::out_quint, Duration::from_millis(500)); + + false + } +} + +fn counter_button( + text: impl Widget + 'static, + on_click: impl FnMut(f32, f32) + 'static, +) -> impl Widget { + let background = lazy(RectState::new(0xff_ff0051, 5., None)); + + pane() + .with_layout( + Layout::new() + .with_dimension(Fixed(80.), Fixed(50.)) + .with_origin(Anchor::CENTER) + .with_anchor(Anchor::CENTER), + ) + .child(text) + .bg(background.clone()) + .on_click(on_click) + .on_event(counter_button_color_handler(background)) +} + fn counter() -> impl Widget { let value = lazy(0); - let increment = { - let value = value.clone(); - move |_, _| { - *value.get_mut() += 1; - println!("+1! Counter = {}", value.get()); - } - }; - - let decrement = { - let value = value.clone(); - move |_, _| { - *value.get_mut() -= 1; - println!("-1! Counter = {}", value.get()); - } - }; - - let typeface = Typeface::new("Torus Pro", FontStyle::normal()).unwrap(); + let typeface = Typeface::new("Roboto", FontStyle::normal()).unwrap(); let font = lazy(Font::new(typeface, Some(20.))); pane() @@ -57,29 +99,17 @@ fn counter() -> impl Widget { .with_origin(Anchor::CENTER) .with_anchor(Anchor::CENTER), ) - .child(text(value, font.clone())) + .child(text(value.clone(), font.clone())) .bg(lazy(RectState::new(0xff_33aa55, 5., None))), ) - .child( - button(text("+1", font.clone())) - .with_layout( - Layout::new() - .with_dimension(Fixed(80.), Fixed(50.)) - .with_origin(Anchor::CENTER) - .with_anchor(Anchor::CENTER), - ) - .on_click(increment), - ) - .child( - button(text("-1", font)) - .with_layout( - Layout::new() - .with_dimension(Fixed(80.), Fixed(50.)) - .with_origin(Anchor::CENTER) - .with_anchor(Anchor::CENTER), - ) - .on_click(decrement), - ), + .child(counter_button( + text("+1", font.clone()), + increment(value.clone()), + )) + .child(counter_button( + text("-1", font), + decrement(value), + )), ) } diff --git a/lokui/src/components/button.rs b/lokui/src/components/button.rs deleted file mode 100644 index eae7796..0000000 --- a/lokui/src/components/button.rs +++ /dev/null @@ -1,150 +0,0 @@ -use std::fmt::Display; -use std::io; -use std::time::Duration; - -use skia_safe::{Canvas, Paint, Rect}; - -use crate::anim::{ease, Property}; -use crate::events::{Event, MousePosition}; -use crate::indentation; -use crate::layout::{Layout, Padding, SolvedLayout}; -use crate::state::{lazy, Color, Lazy}; -use crate::widget::{default_solve_layout, Widget}; - -use super::text::Text; - -pub struct Button { - layout: Layout, - text_layout: SolvedLayout, - text: Text, - color: Lazy>, - on_click: Option>, - enabled: Lazy, - is_mouse_down: bool, - hovered: bool, -} - -pub fn button(text: Text) -> Button { - Button { - layout: Layout::hug(), - text_layout: SolvedLayout::default(), - text, - color: lazy(Property::new(Color::from_hex(0xff0051))), - on_click: None, - enabled: Lazy::new(true), - is_mouse_down: false, - hovered: false, - } -} - -impl Button { - pub fn with_layout(mut self, layout: Layout) -> Self { - self.layout = layout; - self - } - - pub fn on_click(mut self, on_click: impl FnMut(f32, f32) + 'static) -> Self { - self.on_click = Some(Box::new(on_click)); - self - } -} - -impl Widget for Button { - fn layout(&self) -> &Layout { - &self.layout - } - - fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { - let layout = default_solve_layout(self, parent_layout); - self.text_layout = (self.text).solve_layout(&layout.padded(Padding::vh(5., 10.))); - layout - } - - fn min_width(&self) -> f32 { - self.text.min_width() + 10. - } - - fn min_height(&self) -> f32 { - self.text.min_height() + 5. - } - - fn debug(&self, w: &mut dyn io::Write, deepness: usize) -> io::Result<()> { - writeln!( - w, - "{}", - indentation(deepness), - self.text.text(), - ) - } - - fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { - let rect = Rect::from_xywh( - layout.x_start(), - layout.y_start(), - layout.width(), - layout.height(), - ); - - let mut paint = Paint::default(); - paint.set_anti_alias(true); - paint.set_stroke_width(2.); - - let color = self.color.get_mut().current().into_skia(); - - paint.set_stroke(false); - paint.set_color(color.with_a(0x80)); - canvas.draw_rect(rect, &paint); - - paint.set_stroke(true); - paint.set_color(color.with_a(0xff)); - canvas.draw_rect(rect, &paint); - - self.text.draw(canvas, &self.text_layout); - } - - fn handle_event(&mut self, event: Event, _layout: &SolvedLayout) -> bool { - if *self.enabled.get() { - let handled = match event { - Event::MouseDown(_) => { - self.is_mouse_down = true; - true - } - Event::MouseUp(MousePosition { x, y }) => { - if self.is_mouse_down { - self.is_mouse_down = false; - if let Some(on_click) = self.on_click.as_mut() { - (on_click)(x, y); - } - true - } else { - false - } - } - Event::MouseIn => { - self.hovered = true; - true - } - Event::MouseOut => { - self.hovered = false; - self.is_mouse_down = false; - true - } - _ => return false, - }; - - let color = match (*self.enabled.get(), self.is_mouse_down) { - (true, true) => 0xa70038, - (true, false) if self.hovered => 0x51ffff, - (true, false) => 0xff0051, - _ => 0x7a4553, - }; - - let color = Color::from_hex(color); - (self.color.get_mut()).go_to(color, ease::out_quint, Duration::from_millis(500)); - - handled - } else { - false - } - } -} diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index 13426b5..c8efec0 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -1,9 +1,9 @@ +use crate::events::Event; use crate::state::{Lazy, RectState}; use crate::widget::Widget; -use self::wrappers::WithBg; +use self::wrappers::*; -pub mod button; pub mod pane; pub mod text; pub mod wrappers; @@ -12,6 +12,14 @@ pub trait WidgetExt: Widget { fn bg(self, bg: Lazy) -> WithBg where Self: Sized; + + fn on_click(self, callback: impl FnMut(f32, f32) + 'static) -> WithOnClick + where + Self: Sized; + + fn on_event(self, callback: impl FnMut(Event) -> bool + 'static) -> WithOnEvent + where + Self: Sized; } impl WidgetExt for T { @@ -24,4 +32,25 @@ impl WidgetExt for T { state, } } + + fn on_click(self, callback: impl FnMut(f32, f32) + 'static) -> WithOnClick + where + Self: Sized, + { + WithOnClick { + widget: self, + on_click: Box::new(callback), + is_mouse_down: false, + } + } + + fn on_event(self, callback: impl FnMut(Event) -> bool + 'static) -> WithOnEvent + where + Self: Sized, + { + WithOnEvent { + widget: self, + callback: Box::new(callback), + } + } } diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs index 9a121bb..f0d4ee5 100644 --- a/lokui/src/components/wrappers.rs +++ b/lokui/src/components/wrappers.rs @@ -2,19 +2,19 @@ use std::ops::{Deref, DerefMut}; use skia_safe::{Canvas, Paint, RRect, Rect}; -use crate::events::Event; +use crate::events::{Event, MousePosition}; use crate::layout::{Layout, SolvedLayout}; use crate::state::{Lazy, RectState}; use crate::widget::Widget; -// padding +// bg -pub struct WithBg { +pub struct WithBg { pub(super) widget: W, pub(super) state: Lazy, } -impl Deref for WithBg { +impl Deref for WithBg { type Target = W; fn deref(&self) -> &Self::Target { @@ -22,7 +22,7 @@ impl Deref for WithBg { } } -impl DerefMut for WithBg { +impl DerefMut for WithBg { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.widget } @@ -79,3 +79,116 @@ impl Widget for WithBg { self.widget.handle_event(event, layout) } } + +// onclick + +pub struct WithOnClick { + pub(super) widget: W, + pub(super) on_click: Box, + pub(super) is_mouse_down: bool, +} + +impl Deref for WithOnClick { + type Target = W; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl DerefMut for WithOnClick { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + +impl Widget for WithOnClick { + fn layout(&self) -> &Layout { + self.widget.layout() + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.widget.solve_layout(parent_layout) + } + + fn min_width(&self) -> f32 { + self.widget.min_width() + } + + fn min_height(&self) -> f32 { + self.widget.min_height() + } + + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { + self.widget.draw(canvas, layout); + } + + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { + match event { + Event::MouseDown(_) => { + self.is_mouse_down = true; + } + Event::MouseUp(MousePosition { x, y }) => { + if self.is_mouse_down { + self.is_mouse_down = false; + (self.on_click)(x, y); + } + } + Event::MouseOut => self.is_mouse_down = false, + _ => (), + } + + self.widget.handle_event(event, layout) + } +} + +// onevent + +pub struct WithOnEvent { + pub(super) widget: W, + pub(super) callback: Box bool>, +} + +impl Deref for WithOnEvent { + type Target = W; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl DerefMut for WithOnEvent { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + +impl Widget for WithOnEvent { + fn layout(&self) -> &Layout { + self.widget.layout() + } + + fn solve_layout(&mut self, parent_layout: &SolvedLayout) -> SolvedLayout { + self.widget.solve_layout(parent_layout) + } + + fn min_width(&self) -> f32 { + self.widget.min_width() + } + + fn min_height(&self) -> f32 { + self.widget.min_height() + } + + fn draw(&self, canvas: &mut Canvas, layout: &SolvedLayout) { + self.widget.draw(canvas, layout); + } + + fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { + if !(self.callback)(event) { + return self.widget.handle_event(event, layout); + } + + false + } +} diff --git a/lokui/src/lib.rs b/lokui/src/lib.rs index 96a5fdc..f731597 100644 --- a/lokui/src/lib.rs +++ b/lokui/src/lib.rs @@ -10,7 +10,6 @@ pub mod widget; pub mod prelude { pub use crate::components::WidgetExt; - pub use crate::components::button::button; pub use crate::components::pane::pane; pub use crate::components::text::text; @@ -18,7 +17,7 @@ pub mod prelude { pub use crate::layout::padding::Padding; pub use crate::layout::DimScalar::*; pub use crate::layout::{Direction, Flex, Layout}; - pub use crate::state::{lazy, RectState}; + pub use crate::state::{lazy, Lazy, RectState}; pub use crate::widget::{Widget, WidgetContainer}; } From cf117557de2915416fb10ae29a3a2dd32ebed947 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 14 May 2023 22:16:22 +0200 Subject: [PATCH 44/49] Make `on_click` callback return a boolean --- lokui/examples/counter.rs | 8 +++++--- lokui/src/components/mod.rs | 4 ++-- lokui/src/components/wrappers.rs | 23 +++++++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 141707e..06f5dca 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -14,18 +14,20 @@ use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; /// Curried increment callback. -fn increment(value: Lazy) -> impl Fn(f32, f32) { +fn increment(value: Lazy) -> impl Fn(f32, f32) -> bool { move |_, _| { *value.get_mut() += 1; println!("+1! Counter = {}", value.get()); + true } } /// Curried decrement callback. -fn decrement(value: Lazy) -> impl Fn(f32, f32) { +fn decrement(value: Lazy) -> impl Fn(f32, f32) -> bool { move |_, _| { *value.get_mut() -= 1; println!("-1! Counter = {}", value.get()); + true } } @@ -51,7 +53,7 @@ fn counter_button_color_handler(bg: Lazy) -> impl FnMut(Event) -> boo fn counter_button( text: impl Widget + 'static, - on_click: impl FnMut(f32, f32) + 'static, + on_click: impl FnMut(f32, f32) -> bool + 'static, ) -> impl Widget { let background = lazy(RectState::new(0xff_ff0051, 5., None)); diff --git a/lokui/src/components/mod.rs b/lokui/src/components/mod.rs index c8efec0..0ed3728 100644 --- a/lokui/src/components/mod.rs +++ b/lokui/src/components/mod.rs @@ -13,7 +13,7 @@ pub trait WidgetExt: Widget { where Self: Sized; - fn on_click(self, callback: impl FnMut(f32, f32) + 'static) -> WithOnClick + fn on_click(self, callback: impl FnMut(f32, f32) -> bool + 'static) -> WithOnClick where Self: Sized; @@ -33,7 +33,7 @@ impl WidgetExt for T { } } - fn on_click(self, callback: impl FnMut(f32, f32) + 'static) -> WithOnClick + fn on_click(self, callback: impl FnMut(f32, f32) -> bool + 'static) -> WithOnClick where Self: Sized, { diff --git a/lokui/src/components/wrappers.rs b/lokui/src/components/wrappers.rs index f0d4ee5..49b0720 100644 --- a/lokui/src/components/wrappers.rs +++ b/lokui/src/components/wrappers.rs @@ -84,7 +84,7 @@ impl Widget for WithBg { pub struct WithOnClick { pub(super) widget: W, - pub(super) on_click: Box, + pub(super) on_click: Box bool>, pub(super) is_mouse_down: bool, } @@ -124,21 +124,32 @@ impl Widget for WithOnClick { } fn handle_event(&mut self, event: Event, layout: &SolvedLayout) -> bool { - match event { + let handled = match event { Event::MouseDown(_) => { self.is_mouse_down = true; + false } Event::MouseUp(MousePosition { x, y }) => { if self.is_mouse_down { self.is_mouse_down = false; (self.on_click)(x, y); + true + } else { + false } } - Event::MouseOut => self.is_mouse_down = false, - _ => (), + Event::MouseOut => { + self.is_mouse_down = false; + false + }, + _ => false, + }; + + if !handled { + self.widget.handle_event(event, layout) + } else { + true } - - self.widget.handle_event(event, layout) } } From 3845d9009cc35c137f4273a2be96523245927daf Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 14 May 2023 22:40:35 +0200 Subject: [PATCH 45/49] Add `argb_hex` method to `Color` --- lokui/src/state.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lokui/src/state.rs b/lokui/src/state.rs index 40b57ad..db8b465 100644 --- a/lokui/src/state.rs +++ b/lokui/src/state.rs @@ -77,6 +77,15 @@ impl Color { (a as u8, r as u8, g as u8, b as u8) } + pub fn argb_hex(&self) -> u32 { + let (a, r, g, b) = self.argb(); + let a = (a as u32) << 24; + let r = (r as u32) << 16; + let g = (g as u32) << 8; + let b = b as u32; + a | r | g | b + } + pub fn rgb_i32(&self) -> (i32, i32, i32, i32) { let (a, r, g, b) = self.argb_f32(); (a as i32, r as i32, g as i32, b as i32) From 6b6cb6e901dd481667cb99ad713f6c1ca18d9228 Mon Sep 17 00:00:00 2001 From: Speykious Date: Sun, 14 May 2023 22:41:10 +0200 Subject: [PATCH 46/49] Make counter example look slightly better --- lokui/examples/counter.rs | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 06f5dca..f972c25 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -35,12 +35,14 @@ fn counter_button_color_handler(bg: Lazy) -> impl FnMut(Event) -> boo /// wrapper struct to have a simple non-copy background color struct BgColor(u32); - let mut bg_color = BgColor(0xff0051); + let idle_color = bg.get_mut().color.current().argb_hex(); + + let mut bg_color = BgColor(idle_color); move |event| { bg_color.0 = match event { - Event::MouseDown(_) => 0xa70038, - Event::MouseIn | Event::MouseUp(_) => 0x51ffff, - Event::MouseOut => 0xff0051, + Event::MouseDown(_) => 0x3d3556, + Event::MouseIn | Event::MouseUp(_) => 0xaa95f0, + Event::MouseOut => idle_color, _ => bg_color.0, }; @@ -55,7 +57,7 @@ fn counter_button( text: impl Widget + 'static, on_click: impl FnMut(f32, f32) -> bool + 'static, ) -> impl Widget { - let background = lazy(RectState::new(0xff_ff0051, 5., None)); + let background = lazy(RectState::new(0xff_000000 | 0x8460f0, 5., None)); pane() .with_layout( @@ -74,7 +76,7 @@ fn counter() -> impl Widget { let value = lazy(0); let typeface = Typeface::new("Roboto", FontStyle::normal()).unwrap(); - let font = lazy(Font::new(typeface, Some(20.))); + let font = lazy(Font::new(typeface, Some(16.))); pane() .with_layout( @@ -83,7 +85,7 @@ fn counter() -> impl Widget { .with_dimension(Fixed(400.), Fixed(250.)), ) .with_padding(Padding::splat(10.)) - .bg(lazy(RectState::new(0xff_2e428c, 10., None))) + .bg(lazy(RectState::new(0xff_232128, 10., None))) .child( pane() .with_flex(Flex { @@ -92,7 +94,7 @@ fn counter() -> impl Widget { }) .with_layout(Layout::new().with_dimension(Fill, Fill)) .with_padding(Padding::vh(5., 10.)) - .bg(lazy(RectState::new(0x80_657cb1, 5., None))) + .bg(lazy(RectState::new(0xff_2e2c35, 5., None))) .child( pane() .with_layout( @@ -102,7 +104,7 @@ fn counter() -> impl Widget { .with_anchor(Anchor::CENTER), ) .child(text(value.clone(), font.clone())) - .bg(lazy(RectState::new(0xff_33aa55, 5., None))), + .bg(lazy(RectState::new(0xff_232128, 5., None))), ) .child(counter_button( text("+1", font.clone()), From 3fdf34b16882e7b255a68c83c40e1bf666233527 Mon Sep 17 00:00:00 2001 From: Speykious Date: Thu, 27 Jul 2023 01:01:27 +0200 Subject: [PATCH 47/49] Replace old lokinit code with winit for now --- Cargo.lock | 1027 +++++++++++++++++++++++--- lokui/Cargo.toml | 9 +- lokui/examples/common/skia_window.rs | 198 +++++ lokui/examples/counter.rs | 223 ++++-- 4 files changed, 1274 insertions(+), 183 deletions(-) create mode 100644 lokui/examples/common/skia_window.rs diff --git a/Cargo.lock b/Cargo.lock index 05275a0..69196a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,22 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ab_glyph" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe21446ad43aa56417a767f3e2f3d7c4ca522904de1dd640529a76e9c5c3b33c" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + [[package]] name = "adler" version = "1.0.2" @@ -17,6 +33,42 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-activity" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c77a0045eda8b888c76ea473c2b0515ba6f471d318f8927c5c72240937035a6" +dependencies = [ + "android-properties", + "bitflags 1.3.2", + "cc", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "arrayref" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "autocfg" version = "1.1.0" @@ -31,9 +83,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.13.1" +version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" [[package]] name = "base64ct" @@ -43,23 +95,24 @@ checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" [[package]] name = "bindgen" -version = "0.64.0" +version = "0.65.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cexpr", "clang-sys", "lazy_static", "lazycell", "log", "peeking_take_while", + "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", - "syn", + "syn 2.0.27", "which", ] @@ -69,6 +122,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" + [[package]] name = "block-buffer" version = "0.10.4" @@ -78,23 +137,64 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + [[package]] name = "bumpalo" version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" +[[package]] +name = "bytemuck" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "calloop" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" +dependencies = [ + "log", + "nix 0.25.1", + "slotmap", + "thiserror", + "vec_map", +] + [[package]] name = "cc" version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +dependencies = [ + "jobserver", +] [[package]] name = "cexpr" @@ -111,6 +211,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" + +[[package]] +name = "cgl" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] + [[package]] name = "clang-sys" version = "1.6.1" @@ -128,6 +243,47 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "520fbf3c07483f94e3e3ca9d0cfd913d7718ef2483d2cfd91c0d9e91474ab913" +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "foreign-types", + "libc", +] + [[package]] name = "cpufeatures" version = "0.2.6" @@ -178,11 +334,38 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "dlib" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794" +dependencies = [ + "libloading", +] + +[[package]] +name = "downcast-rs" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" + [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "filetime" @@ -192,8 +375,8 @@ checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" dependencies = [ "cfg-if", "libc", - "redox_syscall", - "windows-sys", + "redox_syscall 0.2.16", + "windows-sys 0.48.0", ] [[package]] @@ -206,6 +389,21 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.1.0" @@ -236,17 +434,101 @@ dependencies = [ "wasi", ] +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "glutin" +version = "0.30.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b0385782048be65f0a9dd046c469d6a758a53fe1aa63a8111dea394d2ffa2f" +dependencies = [ + "bitflags 1.3.2", + "cfg_aliases", + "cgl", + "core-foundation", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading", + "objc2", + "once_cell", + "raw-window-handle", + "wayland-sys 0.30.1", + "windows-sys 0.45.0", + "x11-dl", +] + +[[package]] +name = "glutin-winit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629a873fc04062830bfe8f97c03773bcd7b371e23bcc465d0a61448cd1588fa4" +dependencies = [ + "cfg_aliases", + "glutin", + "raw-window-handle", + "winit", +] + +[[package]] +name = "glutin_egl_sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b3bcbddc51573b977fc6dca5d93867e4f29682cdbaf5d13e48f4fa4346d4d87" +dependencies = [ + "gl_generator", + "windows-sys 0.45.0", +] + +[[package]] +name = "glutin_glx_sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b53cb5fe568964aa066a3ba91eac5ecbac869fb0842cd0dc9e412434f1a1494" +dependencies = [ + "gl_generator", + "x11-dl", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef89398e90033fc6bc65e9bd42fd29bbbfd483bda5b56dc5562f455550618165" +dependencies = [ + "gl_generator", +] + [[package]] name = "hashbrown" -version = "0.12.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" [[package]] name = "heck" @@ -266,19 +548,46 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.9.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "autocfg", + "equivalent", "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "jobserver" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" +dependencies = [ + "libc", +] [[package]] name = "js-sys" @@ -289,6 +598,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + [[package]] name = "lazy_static" version = "1.4.0" @@ -360,24 +675,37 @@ version = "0.1.0" name = "lokui" version = "0.1.0" dependencies = [ - "miniquad", + "gl", + "glutin", + "glutin-winit", + "raw-window-handle", "skia-safe", + "winit", ] [[package]] -name = "malloc_buf" -version = "0.0.6" +name = "memchr" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" dependencies = [ "libc", ] [[package]] -name = "memchr" -version = "2.5.0" +name = "memoffset" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] [[package]] name = "minimal-lexical" @@ -386,31 +714,79 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] -name = "miniquad" -version = "0.4.0-alpha.0" -source = "git+https://github.com/loki-chat/lokinit#2ec39bfacc0830538640ca68addd24da94766b1c" +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" dependencies = [ "libc", - "ndk-sys", - "objc", - "skia-safe", - "winapi", + "log", + "wasi", + "windows-sys 0.45.0", ] [[package]] -name = "miniz_oxide" -version = "0.6.2" +name = "ndk" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ - "adler", + "bitflags 1.3.2", + "jni-sys", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-sys" -version = "0.2.2" +version = "0.4.1+23.1.7779620" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" +dependencies = [ + "jni-sys", +] + +[[package]] +name = "nix" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1bcdd74c20ad5d95aacd60ef9ba40fdf77f767051040541df557b7a9b2a2121" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "nix" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "libc", + "memoffset", +] [[package]] name = "nom" @@ -471,12 +847,50 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "num_enum" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" dependencies = [ - "malloc_buf", + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe31e5425d3d0b89a15982c024392815da40689aceb34bad364d58732bcfd649" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", ] [[package]] @@ -485,6 +899,27 @@ version = "1.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +[[package]] +name = "orbclient" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9829e16c5e112e94efb5e2ad1fe17f8c1c99bb0fcdc8c65c44e935d904767d" +dependencies = [ + "cfg-if", + "redox_syscall 0.2.16", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "owned_ttf_parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" +dependencies = [ + "ttf-parser", +] + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -528,17 +963,55 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" + +[[package]] +name = "png" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "flate2", + "miniz_oxide", +] + [[package]] name = "ppv-lite86" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "prettyplease" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64d9ba0963cdcea2e1b2230fbae2bab30eb25a174be395c41e764bfb65dd62" +dependencies = [ + "proc-macro2", + "syn 2.0.27", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit", +] + [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] @@ -555,9 +1028,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -592,13 +1065,28 @@ dependencies = [ "getrandom", ] +[[package]] +name = "raw-window-handle" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" + [[package]] name = "redox_syscall" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -661,21 +1149,47 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustls" -version = "0.20.8" +version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" +checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" dependencies = [ "log", "ring", + "rustls-webpki 0.101.2", "sct", - "webpki", +] + +[[package]] +name = "rustls-webpki" +version = "0.100.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513722fd73ad80a71f72b61009ea1b584bcfa1483ca93949c8f290298837fa59" +dependencies = [ + "ring", + "untrusted", ] [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "sct" @@ -687,17 +1201,30 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sctk-adwaita" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda4e97be1fd174ccc2aae81c8b694e803fa99b34e8fd0f057a9d70698e3ed09" +dependencies = [ + "ab_glyph", + "log", + "memmap2", + "smithay-client-toolkit", + "tiny-skia", +] + [[package]] name = "serde" -version = "1.0.160" +version = "1.0.159" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" dependencies = [ "itoa", "ryu", @@ -706,9 +1233,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0efd8caf556a6cebd3b285caf480045fcc1ac04f6bd786b09a6f11af30c4fcf4" +checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" dependencies = [ "serde", ] @@ -742,9 +1269,9 @@ dependencies = [ [[package]] name = "skia-bindings" -version = "0.60.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d166792c15b8ebd180b83e3b9ab38ef69c09468ed26c11692fb59c5af9bc1d" +checksum = "0ffc650a2d45d9acb7e251568dc6193d882cf66aeb78e5dcc08bc2b816ffbbd5" dependencies = [ "bindgen", "cc", @@ -760,21 +1287,49 @@ dependencies = [ [[package]] name = "skia-safe" -version = "0.60.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fee050b67b2124a5745484f4216999951feeeb1577ef1515e9194ea6d9a9612" +checksum = "8ae6df6412e061d9792938efc923dc41236edf778058ecc63dc385cd5d5a9ac1" dependencies = [ - "bitflags", + "bitflags 2.3.3", "lazy_static", "skia-bindings", ] +[[package]] +name = "slotmap" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342" +dependencies = [ + "version_check", +] + [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "smithay-client-toolkit" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f307c47d32d2715eb2e0ece5589057820e0e5e70d07c247d1063e844e107f454" +dependencies = [ + "bitflags 1.3.2", + "calloop", + "dlib", + "lazy_static", + "log", + "memmap2", + "nix 0.24.3", + "pkg-config", + "wayland-client", + "wayland-cursor", + "wayland-protocols", +] + [[package]] name = "spin" version = "0.5.2" @@ -791,6 +1346,12 @@ dependencies = [ "der", ] +[[package]] +name = "strict-num" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" + [[package]] name = "subtle" version = "2.5.0" @@ -808,17 +1369,73 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn" +version = "2.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tar" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" +checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96" dependencies = [ "filetime", "libc", "xattr", ] +[[package]] +name = "thiserror" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.27", +] + +[[package]] +name = "tiny-skia" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfef3412c6975196fdfac41ef232f910be2bb37b9dd3313a49a1a6bc815a5bdb" +dependencies = [ + "arrayref", + "arrayvec", + "bytemuck", + "cfg-if", + "png", + "tiny-skia-path", +] + +[[package]] +name = "tiny-skia-path" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4b5edac058fc98f51c935daea4d805b695b38e2f151241cad125ade2a2ac20d" +dependencies = [ + "arrayref", + "bytemuck", + "strict-num", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -836,9 +1453,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.7.3" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b403acf6f2bb0859c93c7f0d967cb4a75a7ac552100f9322faf64dc047669b21" +checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" dependencies = [ "serde", "serde_spanned", @@ -848,18 +1465,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.1" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ab8ed2edee10b50132aed5f331333428b011c99402b5a534154ed15746f9622" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.8" +version = "0.19.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13" +checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" dependencies = [ "indexmap", "serde", @@ -868,6 +1485,12 @@ dependencies = [ "winnow", ] +[[package]] +name = "ttf-parser" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" + [[package]] name = "typenum" version = "1.16.0" @@ -903,17 +1526,17 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.6.2" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" +checksum = "0b11c96ac7ee530603dcdf68ed1557050f374ce55a5a07193ebf8cbc9f8927e9" dependencies = [ "base64", "flate2", "log", "once_cell", "rustls", + "rustls-webpki 0.100.1", "url", - "webpki", "webpki-roots", ] @@ -928,6 +1551,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.4" @@ -961,7 +1590,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-shared", ] @@ -983,7 +1612,7 @@ checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -995,32 +1624,107 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" [[package]] -name = "web-sys" -version = "0.3.61" +name = "wayland-client" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" dependencies = [ - "js-sys", - "wasm-bindgen", + "bitflags 1.3.2", + "downcast-rs", + "libc", + "nix 0.24.3", + "scoped-tls", + "wayland-commons", + "wayland-scanner", + "wayland-sys 0.29.5", ] [[package]] -name = "webpki" -version = "0.22.0" +name = "wayland-commons" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ - "ring", - "untrusted", + "nix 0.24.3", + "once_cell", + "smallvec", + "wayland-sys 0.29.5", +] + +[[package]] +name = "wayland-cursor" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" +dependencies = [ + "nix 0.24.3", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" +dependencies = [ + "bitflags 1.3.2", + "wayland-client", + "wayland-commons", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" +dependencies = [ + "proc-macro2", + "quote", + "xml-rs", +] + +[[package]] +name = "wayland-sys" +version = "0.29.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" +dependencies = [ + "dlib", + "lazy_static", + "pkg-config", +] + +[[package]] +name = "wayland-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +dependencies = [ + "dlib", + "lazy_static", + "log", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +dependencies = [ + "js-sys", + "wasm-bindgen", ] [[package]] name = "webpki-roots" -version = "0.22.6" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" +checksum = "b03058f88386e5ff5310d9111d53f48b17d732b401aeb83a8d5190f2ac459338" dependencies = [ - "webpki", + "rustls-webpki 0.100.1", ] [[package]] @@ -1056,13 +1760,37 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.0", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -1071,66 +1799,154 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +[[package]] +name = "winit" +version = "0.28.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "866db3f712fffba75d31bf0cdecf357c8aeafd158c5b7ab51dba2a2b2d47f196" +dependencies = [ + "android-activity", + "bitflags 1.3.2", + "cfg_aliases", + "core-foundation", + "core-graphics", + "dispatch", + "instant", + "libc", + "log", + "mio", + "ndk", + "objc2", + "once_cell", + "orbclient", + "percent-encoding", + "raw-window-handle", + "redox_syscall 0.3.5", + "sctk-adwaita", + "smithay-client-toolkit", + "wasm-bindgen", + "wayland-client", + "wayland-commons", + "wayland-protocols", + "wayland-scanner", + "web-sys", + "windows-sys 0.45.0", + "x11-dl", +] + [[package]] name = "winnow" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8970b36c66498d8ff1d66685dc86b91b29db0c7739899012f63a63814b4b28" +checksum = "25b5872fa2e10bd067ae946f927e726d7d603eaeb6e02fa6a350e0722d2b8c11" dependencies = [ "memchr", ] +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + [[package]] name = "xattr" version = "0.2.3" @@ -1140,6 +1956,21 @@ dependencies = [ "libc", ] +[[package]] +name = "xcursor" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7" +dependencies = [ + "nom", +] + +[[package]] +name = "xml-rs" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" + [[package]] name = "zeroize" version = "1.6.0" diff --git a/lokui/Cargo.toml b/lokui/Cargo.toml index 6236309..6b0c92d 100644 --- a/lokui/Cargo.toml +++ b/lokui/Cargo.toml @@ -6,8 +6,11 @@ version = "0.1.0" edition = "2021" [dependencies] -skia-safe = { version = "0.60.0", features = ["gl"] } +skia-safe = { version = "0.63.0", features = ["gl"] } [dev-dependencies] -miniquad = { git = "https://github.com/loki-chat/lokinit" } -# miniquad = { path = "../../lokinit" } +gl = "0.14.0" +glutin = "0.30.9" +glutin-winit = "0.3.0" +raw-window-handle = "0.5.2" +winit = "0.28.6" diff --git a/lokui/examples/common/skia_window.rs b/lokui/examples/common/skia_window.rs new file mode 100644 index 0000000..4968cfb --- /dev/null +++ b/lokui/examples/common/skia_window.rs @@ -0,0 +1,198 @@ +use std::{error::Error, ffi::CString, num::NonZeroU32}; + +use gl::types::*; +use glutin::{ + config::{ConfigTemplateBuilder, GlConfig}, + context::{ + ContextApi, ContextAttributesBuilder, NotCurrentGlContextSurfaceAccessor, + PossiblyCurrentContext, + }, + display::{GetGlDisplay, GlDisplay}, + surface::{Surface as GlutinSurface, SurfaceAttributesBuilder, WindowSurface}, +}; +use glutin_winit::DisplayBuilder; +use raw_window_handle::HasRawWindowHandle; +use skia_safe::{ + gpu::{gl::FramebufferInfo, BackendRenderTarget, SurfaceOrigin}, + ColorType, Surface, +}; +use winit::{ + dpi::{PhysicalSize, Size}, + event_loop::EventLoop, + window::{Window, WindowBuilder}, +}; + +// Guarantee the drop order inside the FnMut closure. `Window` _must_ be dropped after +// `DirectContext`. +// +// https://github.com/rust-skia/rust-skia/issues/476 +pub struct SkiaWindowCtx { + pub fb_info: FramebufferInfo, + pub num_samples: usize, + pub stencil_size: usize, + pub surface: Surface, + pub gl_surface: GlutinSurface, + pub gr_context: skia_safe::gpu::DirectContext, + pub gl_context: PossiblyCurrentContext, + pub window: Window, + pub events: EventLoop<()>, +} + +pub fn create_skia_window( + title: &str, + width: u32, + height: u32, +) -> Result> { + let events = EventLoop::new(); + + let winit_window_builder = WindowBuilder::new() + .with_title(title) + .with_inner_size(Size::Physical(PhysicalSize::new(width, height))); + + let template = ConfigTemplateBuilder::new() + .with_alpha_size(8) + .with_transparency(true); + + let display_builder = DisplayBuilder::new().with_window_builder(Some(winit_window_builder)); + let (window, gl_config) = display_builder + .build(&events, template, |configs| { + // Find the config with the minimum number of samples. Usually Skia takes care of + // anti-aliasing and may not be able to create appropriate Surfaces for samples > 0. + // See https://github.com/rust-skia/rust-skia/issues/782 + // And https://github.com/rust-skia/rust-skia/issues/764 + configs + .reduce(|accum, config| { + let transparency_check = config.supports_transparency().unwrap_or(false) + & !accum.supports_transparency().unwrap_or(false); + + if transparency_check || config.num_samples() < accum.num_samples() { + config + } else { + accum + } + }) + .unwrap() + }) + .unwrap(); + + println!("Picked a config with {} samples", gl_config.num_samples()); + + let mut window = window.expect("Could not create window with OpenGL context"); + let raw_window_handle = window.raw_window_handle(); + + // The context creation part. It can be created before surface and that's how + // it's expected in multithreaded + multiwindow operation mode, since you + // can send NotCurrentContext, but not Surface. + let context_attributes = ContextAttributesBuilder::new().build(Some(raw_window_handle)); + + // Since glutin by default tries to create OpenGL core context, which may not be + // present we should try gles. + let fallback_context_attributes = ContextAttributesBuilder::new() + .with_context_api(ContextApi::Gles(None)) + .build(Some(raw_window_handle)); + + let not_current_gl_context = unsafe { + gl_config + .display() + .create_context(&gl_config, &context_attributes) + .unwrap_or_else(|_| { + gl_config + .display() + .create_context(&gl_config, &fallback_context_attributes) + .expect("failed to create context") + }) + }; + + let attrs = SurfaceAttributesBuilder::::new().build( + raw_window_handle, + NonZeroU32::new(width).unwrap(), + NonZeroU32::new(height).unwrap(), + ); + + let gl_surface = unsafe { + gl_config + .display() + .create_window_surface(&gl_config, &attrs) + .expect("Could not create gl window surface") + }; + + let gl_context = not_current_gl_context + .make_current(&gl_surface) + .expect("Could not make GL context current when setting up skia renderer"); + + gl::load_with(|s| { + gl_config + .display() + .get_proc_address(CString::new(s).unwrap().as_c_str()) + }); + + let interface = skia_safe::gpu::gl::Interface::new_load_with(|name| { + if name == "eglGetCurrentDisplay" { + return std::ptr::null(); + } + gl_config + .display() + .get_proc_address(CString::new(name).unwrap().as_c_str()) + }) + .expect("Could not create interface"); + + let mut gr_context = skia_safe::gpu::DirectContext::new_gl(Some(interface), None) + .expect("Could not create direct context"); + + let fb_info = { + let mut fboid: GLint = 0; + unsafe { gl::GetIntegerv(gl::FRAMEBUFFER_BINDING, &mut fboid) }; + + FramebufferInfo { + fboid: fboid.try_into().unwrap(), + format: skia_safe::gpu::gl::Format::RGBA8.into(), + } + }; + + let num_samples = gl_config.num_samples() as usize; + let stencil_size = gl_config.stencil_size() as usize; + + let surface = create_surface( + &mut window, + fb_info, + &mut gr_context, + num_samples, + stencil_size, + ); + + Ok(SkiaWindowCtx { + fb_info, + num_samples, + stencil_size, + surface, + gl_surface, + gr_context, + gl_context, + window, + events, + }) +} + +pub fn create_surface( + window: &mut Window, + fb_info: FramebufferInfo, + gr_context: &mut skia_safe::gpu::DirectContext, + num_samples: usize, + stencil_size: usize, +) -> Surface { + let size = window.inner_size(); + let size = (size.width as i32, size.height as i32); + + let backend_render_target = + BackendRenderTarget::new_gl(size, num_samples, stencil_size, fb_info); + + Surface::from_backend_render_target( + gr_context, + &backend_render_target, + SurfaceOrigin::BottomLeft, + ColorType::RGBA8888, + None, + None, + ) + .expect("Could not create skia surface") +} diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index f972c25..5cb0317 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -1,17 +1,27 @@ #![allow(clippy::unusual_byte_groupings)] -use std::io::{stdout, BufWriter, Write}; -use std::time::Duration; +use std::num::NonZeroU32; +use std::time::{Duration, Instant}; +use glutin::surface::GlSurface; use lokui::anim::ease; -use lokui::events::{Event, MousePosition}; +use lokui::events::MousePosition; use lokui::layout::SolvedLayout; use lokui::prelude::*; +use lokui::events::Event as LokuiEvent; +use winit::event::{ElementState, Event, MouseButton}; + use lokui::state::Color; -use miniquad::skia::SkiaContext; -use miniquad::{conf, EventHandler}; use skia_safe::{Font, FontStyle, Typeface}; +use skia_window::create_skia_window; +use winit::event::{KeyboardInput, VirtualKeyCode, WindowEvent}; +use winit::event_loop::ControlFlow; + +use crate::skia_window::create_surface; + +#[path = "common/skia_window.rs"] +mod skia_window; /// Curried increment callback. fn increment(value: Lazy) -> impl Fn(f32, f32) -> bool { @@ -31,7 +41,7 @@ fn decrement(value: Lazy) -> impl Fn(f32, f32) -> bool { } } -fn counter_button_color_handler(bg: Lazy) -> impl FnMut(Event) -> bool { +fn counter_button_color_handler(bg: Lazy) -> impl FnMut(LokuiEvent) -> bool { /// wrapper struct to have a simple non-copy background color struct BgColor(u32); @@ -40,9 +50,9 @@ fn counter_button_color_handler(bg: Lazy) -> impl FnMut(Event) -> boo let mut bg_color = BgColor(idle_color); move |event| { bg_color.0 = match event { - Event::MouseDown(_) => 0x3d3556, - Event::MouseIn | Event::MouseUp(_) => 0xaa95f0, - Event::MouseOut => idle_color, + LokuiEvent::MouseDown(_) => 0x3d3556, + LokuiEvent::MouseIn | LokuiEvent::MouseUp(_) => 0xaa95f0, + LokuiEvent::MouseOut => idle_color, _ => bg_color.0, }; @@ -110,86 +120,135 @@ fn counter() -> impl Widget { text("+1", font.clone()), increment(value.clone()), )) - .child(counter_button( - text("-1", font), - decrement(value), - )), + .child(counter_button(text("-1", font), decrement(value))), ) } -struct Stage { +struct RootWidgetTree { root_widget: W, root_layout: SolvedLayout, window_layout: SolvedLayout, } -impl EventHandler for Stage { - fn update(&mut self, _skia_ctx: &mut SkiaContext) {} - - fn mouse_button_down_event( - &mut self, - _skia_ctx: &mut SkiaContext, - _button: miniquad::MouseButton, - x: f32, - y: f32, - ) { - let event = Event::MouseDown(MousePosition { x, y }); - self.root_widget.handle_event(event, &self.root_layout); - } - - fn mouse_button_up_event( - &mut self, - _skia_ctx: &mut SkiaContext, - _button: miniquad::MouseButton, - x: f32, - y: f32, - ) { - let event = Event::MouseUp(MousePosition { x, y }); - self.root_widget.handle_event(event, &self.root_layout); - } - - fn mouse_motion_event(&mut self, _skia_ctx: &mut SkiaContext, x: f32, y: f32) { - let event = Event::MouseMove(MousePosition { x, y }); - self.root_widget.handle_event(event, &self.root_layout); - } - - fn resize_event(&mut self, skia_ctx: &mut SkiaContext, width: f32, height: f32) { - self.window_layout = SolvedLayout::from_top_left(0., 0., width, height); - self.root_layout = self.root_widget.solve_layout(&self.window_layout); - skia_ctx.recreate_surface(width as i32, height as i32); - } - - fn draw(&mut self, skia_ctx: &mut SkiaContext) { - let canvas = skia_ctx.surface.canvas(); - canvas.clear(skia_safe::Color::from(0xff_161a1d)); - self.root_widget.draw(canvas, &self.root_layout); - skia_ctx.dctx.flush(None); - } -} - fn main() { - let mut root_pane = counter(); - let window_layout = SolvedLayout::from_top_left(0., 0., 1280., 720.); - let root_layout = root_pane.solve_layout(&window_layout); - - let mut writer = BufWriter::new(stdout()); - root_pane.debug(&mut writer, 0).unwrap(); - writer.flush().unwrap(); - - miniquad::start( - conf::Conf { - high_dpi: true, - window_width: 1280, - window_height: 720, - window_title: "Lokui GUI Framework Prototype".to_owned(), - ..Default::default() - }, - move || { - Box::new(Stage { - root_widget: root_pane, - root_layout, - window_layout, - }) - }, - ); + let mut win = + create_skia_window("Lokui GUI Framework Prototype (Counter Example)", 1280, 720).unwrap(); + + let mut rwt = { + let mut root_widget = counter(); + let window_layout = SolvedLayout::from_top_left(0., 0., 1280., 720.); + let root_layout = root_widget.solve_layout(&window_layout); + + RootWidgetTree { + root_widget, + root_layout, + window_layout, + } + }; + + let mut frame = 0_usize; + let mut previous_frame_start = Instant::now(); + let mut x = 0; + let mut y = 0; + + win.events.run(move |event, _, control_flow| { + let frame_start = Instant::now(); + let mut draw_frame = false; + + match event { + Event::LoopDestroyed => {} + Event::WindowEvent { event, .. } => match event { + WindowEvent::CloseRequested => { + *control_flow = ControlFlow::Exit; + return; + } + WindowEvent::Resized(physical_size) => { + /* First resize the opengl drawable */ + let (width, height): (u32, u32) = physical_size.into(); + + rwt.window_layout = + SolvedLayout::from_top_left(0., 0., width as f32, height as f32); + rwt.root_layout = rwt.root_widget.solve_layout(&rwt.window_layout); + + win.surface = create_surface( + &mut win.window, + win.fb_info, + &mut win.gr_context, + win.num_samples, + win.stencil_size, + ); + + win.gl_surface.resize( + &win.gl_context, + NonZeroU32::new(width.max(1)).unwrap(), + NonZeroU32::new(height.max(1)).unwrap(), + ); + } + WindowEvent::MouseInput { state, button, .. } => { + if button == MouseButton::Left { + let mouse_pos = MousePosition { + x: x as f32, + y: y as f32, + }; + + let event = match state { + ElementState::Pressed => LokuiEvent::MouseUp(mouse_pos), + ElementState::Released => LokuiEvent::MouseDown(mouse_pos), + }; + + rwt.root_widget.handle_event(event, &rwt.root_layout); + } + } + WindowEvent::CursorMoved { position, .. } => { + x = position.x as u32; + y = position.y as u32; + + let event = LokuiEvent::MouseMove(MousePosition { + x: position.x as f32, + y: position.y as f32, + }); + rwt.root_widget.handle_event(event, &rwt.root_layout); + } + WindowEvent::KeyboardInput { + input: KeyboardInput { + virtual_keycode, .. + }, + .. + } => { + if let Some(VirtualKeyCode::Q) = virtual_keycode { + *control_flow = ControlFlow::Exit; + } + + frame = frame.saturating_sub(10); + win.window.request_redraw(); + } + _ => (), + }, + Event::RedrawRequested(_) => { + draw_frame = true; + } + _ => (), + } + + let expected_frame_length_seconds = 1.0 / 20.0; + let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); + + if frame_start - previous_frame_start > frame_duration { + draw_frame = true; + previous_frame_start = frame_start; + } + + if draw_frame { + frame += 1; + + let canvas = win.surface.canvas(); + canvas.clear(skia_safe::Color::from(0xff_161a1d)); + rwt.root_widget.draw(canvas, &rwt.root_layout); + + win.gr_context.flush_and_submit(); + win.gl_surface.swap_buffers(&win.gl_context).unwrap(); + } + + *control_flow = ControlFlow::WaitUntil(previous_frame_start + frame_duration) + }); } From bbd32b9aead28bad1c4456621bebd6beea9bce91 Mon Sep 17 00:00:00 2001 From: Speykious Date: Thu, 27 Jul 2023 11:24:57 +0200 Subject: [PATCH 48/49] Ah yes, the up is made out of down --- lokui/examples/counter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 5cb0317..1053c4e 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -192,8 +192,8 @@ fn main() { }; let event = match state { - ElementState::Pressed => LokuiEvent::MouseUp(mouse_pos), - ElementState::Released => LokuiEvent::MouseDown(mouse_pos), + ElementState::Pressed => LokuiEvent::MouseDown(mouse_pos), + ElementState::Released => LokuiEvent::MouseUp(mouse_pos), }; rwt.root_widget.handle_event(event, &rwt.root_layout); From 65083482bb66f8aadcea8ae0087beca117ed4eff Mon Sep 17 00:00:00 2001 From: Speykious Date: Thu, 27 Jul 2023 17:13:46 +0200 Subject: [PATCH 49/49] Fix Skia drop order bug and simplify example --- lokui/examples/counter.rs | 108 +++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/lokui/examples/counter.rs b/lokui/examples/counter.rs index 1053c4e..32ac5c5 100644 --- a/lokui/examples/counter.rs +++ b/lokui/examples/counter.rs @@ -1,7 +1,7 @@ #![allow(clippy::unusual_byte_groupings)] use std::num::NonZeroU32; -use std::time::{Duration, Instant}; +use std::time::Duration; use glutin::surface::GlSurface; use lokui::anim::ease; @@ -14,9 +14,8 @@ use winit::event::{ElementState, Event, MouseButton}; use lokui::state::Color; use skia_safe::{Font, FontStyle, Typeface}; -use skia_window::create_skia_window; +use skia_window::{create_skia_window, SkiaWindowCtx}; use winit::event::{KeyboardInput, VirtualKeyCode, WindowEvent}; -use winit::event_loop::ControlFlow; use crate::skia_window::create_surface; @@ -131,8 +130,7 @@ struct RootWidgetTree { } fn main() { - let mut win = - create_skia_window("Lokui GUI Framework Prototype (Counter Example)", 1280, 720).unwrap(); + let win = create_skia_window("Lokui GUI Framework Prototype (Counter)", 1280, 720).unwrap(); let mut rwt = { let mut root_widget = counter(); @@ -146,21 +144,34 @@ fn main() { } }; - let mut frame = 0_usize; - let mut previous_frame_start = Instant::now(); let mut x = 0; let mut y = 0; - win.events.run(move |event, _, control_flow| { - let frame_start = Instant::now(); - let mut draw_frame = false; + let SkiaWindowCtx { + fb_info, + num_samples, + stencil_size, + mut surface, + gl_surface, + mut gr_context, + gl_context, + mut window, + events, + } = win; + + window.request_redraw(); + events.run(move |event, _, control_flow| { + let window = &mut window; + let surface = &mut surface; + let gr_context = &mut gr_context; + let gl_context = &gl_context; match event { Event::LoopDestroyed => {} Event::WindowEvent { event, .. } => match event { WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; - return; + println!("Closing..."); + control_flow.set_exit(); } WindowEvent::Resized(physical_size) => { /* First resize the opengl drawable */ @@ -170,19 +181,22 @@ fn main() { SolvedLayout::from_top_left(0., 0., width as f32, height as f32); rwt.root_layout = rwt.root_widget.solve_layout(&rwt.window_layout); - win.surface = create_surface( - &mut win.window, - win.fb_info, - &mut win.gr_context, - win.num_samples, - win.stencil_size, + *surface = create_surface( + window, + fb_info, + gr_context, + num_samples, + stencil_size, ); - win.gl_surface.resize( - &win.gl_context, + gl_surface.resize( + gl_context, NonZeroU32::new(width.max(1)).unwrap(), NonZeroU32::new(height.max(1)).unwrap(), ); + + window.request_redraw(); + control_flow.set_wait(); } WindowEvent::MouseInput { state, button, .. } => { if button == MouseButton::Left { @@ -198,6 +212,8 @@ fn main() { rwt.root_widget.handle_event(event, &rwt.root_layout); } + + control_flow.set_wait(); } WindowEvent::CursorMoved { position, .. } => { x = position.x as u32; @@ -208,47 +224,31 @@ fn main() { y: position.y as f32, }); rwt.root_widget.handle_event(event, &rwt.root_layout); + + control_flow.set_wait(); } WindowEvent::KeyboardInput { - input: KeyboardInput { - virtual_keycode, .. - }, + input: + KeyboardInput { + virtual_keycode: Some(VirtualKeyCode::Q), + .. + }, .. - } => { - if let Some(VirtualKeyCode::Q) = virtual_keycode { - *control_flow = ControlFlow::Exit; - } - - frame = frame.saturating_sub(10); - win.window.request_redraw(); - } - _ => (), + } => control_flow.set_exit(), + _ => control_flow.set_wait(), }, Event::RedrawRequested(_) => { - draw_frame = true; - } - _ => (), - } - - let expected_frame_length_seconds = 1.0 / 20.0; - let frame_duration = Duration::from_secs_f32(expected_frame_length_seconds); - - if frame_start - previous_frame_start > frame_duration { - draw_frame = true; - previous_frame_start = frame_start; - } + let canvas = surface.canvas(); + canvas.clear(skia_safe::Color::from(0xff_161a1d)); + rwt.root_widget.draw(canvas, &rwt.root_layout); - if draw_frame { - frame += 1; + gr_context.flush_and_submit(); + gl_surface.swap_buffers(gl_context).unwrap(); - let canvas = win.surface.canvas(); - canvas.clear(skia_safe::Color::from(0xff_161a1d)); - rwt.root_widget.draw(canvas, &rwt.root_layout); - - win.gr_context.flush_and_submit(); - win.gl_surface.swap_buffers(&win.gl_context).unwrap(); + window.request_redraw(); + control_flow.set_wait(); + } + _ => control_flow.set_wait(), } - - *control_flow = ControlFlow::WaitUntil(previous_frame_start + frame_duration) }); }