diff --git a/Cargo.lock b/Cargo.lock index e12080c..a9bd68f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -94,6 +94,27 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "image", + "log", + "objc2 0.6.2", + "objc2-app-kit 0.3.1", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.1", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "wl-clipboard-rs", + "x11rb", +] + [[package]] name = "arg_enum_proc_macro" version = "0.3.4" @@ -221,7 +242,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", ] [[package]] @@ -339,6 +360,15 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "codespan-reporting" version = "0.12.0" @@ -511,6 +541,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +dependencies = [ + "bitflags 2.9.3", + "objc2 0.6.2", +] + [[package]] name = "dlib" version = "0.5.2" @@ -583,6 +623,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + [[package]] name = "exr" version = "1.73.0" @@ -598,6 +644,12 @@ dependencies = [ "zune-inflate", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "fdeflate" version = "0.3.7" @@ -607,6 +659,12 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + [[package]] name = "flate2" version = "1.1.2" @@ -1322,6 +1380,15 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" @@ -1331,13 +1398,25 @@ dependencies = [ "bitflags 2.9.3", "block2", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-quartz-core", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" +dependencies = [ + "bitflags 2.9.3", + "objc2 0.6.2", + "objc2-core-graphics", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -1346,9 +1425,9 @@ checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1358,8 +1437,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1370,8 +1449,32 @@ checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" +dependencies = [ + "bitflags 2.9.3", + "dispatch2", + "objc2 0.6.2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.3", + "dispatch2", + "objc2 0.6.2", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -1381,8 +1484,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -1393,9 +1496,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ "block2", - "objc2", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1414,7 +1517,29 @@ dependencies = [ "block2", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.3", + "objc2 0.6.2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.3", + "objc2 0.6.2", + "objc2-core-foundation", ] [[package]] @@ -1424,9 +1549,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1437,8 +1562,8 @@ checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1449,8 +1574,8 @@ checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -1460,8 +1585,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1472,12 +1597,12 @@ checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", "objc2-quartz-core", "objc2-symbols", @@ -1492,8 +1617,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ "block2", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -1504,9 +1629,9 @@ checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.9.3", "block2", - "objc2", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -1533,6 +1658,16 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_pipe" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "owned_ttf_parser" version = "0.25.1" @@ -1577,6 +1712,16 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2193,6 +2338,19 @@ version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" +[[package]] +name = "tempfile" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.8", + "windows-sys 0.52.0", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -2343,6 +2501,18 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +[[package]] +name = "tree_magic_mini" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c" +dependencies = [ + "memchr", + "nom", + "once_cell", + "petgraph", +] + [[package]] name = "ttf-parser" version = "0.20.0" @@ -2365,6 +2535,7 @@ checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" name = "ui" version = "0.1.0" dependencies = [ + "arboard", "bytemuck", "cosmic-text", "fxhash", @@ -3213,9 +3384,9 @@ dependencies = [ "libc", "memmap2", "ndk", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", "percent-encoding", @@ -3260,6 +3431,25 @@ dependencies = [ "bitflags 2.9.3", ] +[[package]] +name = "wl-clipboard-rs" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" +dependencies = [ + "libc", + "log", + "os_pipe", + "rustix 0.38.44", + "tempfile", + "thiserror 2.0.12", + "tree_magic_mini", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-protocols-wlr", +] + [[package]] name = "x11-dl" version = "2.21.0" diff --git a/Cargo.toml b/Cargo.toml index 254581d..9bfd945 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ image = "0.25.6" cosmic-text = "0.14.2" unicode-segmentation = "1.12.0" fxhash = "0.2.1" +arboard = { version = "3.6.1", features = ["wayland-data-control"] } diff --git a/src/core/empty.rs b/src/core/empty.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/core/mod.rs b/src/core/mod.rs index 033ddb8..a7d05c0 100644 --- a/src/core/mod.rs +++ b/src/core/mod.rs @@ -1,7 +1,9 @@ mod align; mod image; +mod offset; mod pad; mod rect; +mod scroll; mod sense; mod sized; mod span; @@ -12,8 +14,10 @@ mod trait_fns; pub use align::*; pub use image::*; +pub use offset::*; pub use pad::*; pub use rect::*; +pub use scroll::*; pub use sense::*; pub use sized::*; pub use span::*; diff --git a/src/core/offset.rs b/src/core/offset.rs new file mode 100644 index 0000000..e35e41c --- /dev/null +++ b/src/core/offset.rs @@ -0,0 +1,17 @@ +use crate::prelude::*; + +pub struct Offset { + pub inner: WidgetId, + pub amt: UiVec2, +} + +impl Widget for Offset { + fn draw(&mut self, painter: &mut Painter) { + let region = UiRegion::full().offset(self.amt); + painter.widget_within(&self.inner, region); + } + + fn desired_size(&mut self, ctx: &mut SizeCtx) -> UiVec2 { + ctx.size(&self.inner) + } +} diff --git a/src/core/pad.rs b/src/core/pad.rs index aa69a32..0b7f318 100644 --- a/src/core/pad.rs +++ b/src/core/pad.rs @@ -31,10 +31,10 @@ impl Widget for Padded { } pub struct Padding { - left: f32, - right: f32, - top: f32, - bottom: f32, + pub left: f32, + pub right: f32, + pub top: f32, + pub bottom: f32, } impl Padding { diff --git a/src/core/scroll.rs b/src/core/scroll.rs new file mode 100644 index 0000000..d297b9e --- /dev/null +++ b/src/core/scroll.rs @@ -0,0 +1,19 @@ +use crate::prelude::*; + +pub struct Scroll { + pub inner: Offset, + pub size: UiVec2, +} + +impl Widget for Scroll { + fn draw(&mut self, painter: &mut Painter) { + self.inner.draw(painter); + } + fn desired_size(&mut self, _: &mut SizeCtx) -> UiVec2 { + self.size + } +} + +pub struct ScrollModule { + +} diff --git a/src/core/sense.rs b/src/core/sense.rs index e0dadfa..6031f5a 100644 --- a/src/core/sense.rs +++ b/src/core/sense.rs @@ -29,6 +29,7 @@ pub enum Sense { HoverStart, Hovering, HoverEnd, + Scroll, } pub struct Senses(Vec); @@ -47,6 +48,7 @@ pub struct CursorState { pub pos: Vec2, pub exists: bool, pub buttons: CursorButtons, + pub scroll_delta: Vec2, } #[derive(Default, Clone)] @@ -72,6 +74,13 @@ impl CursorButtons { } } +impl CursorState { + pub fn end_frame(&mut self) { + self.buttons.end_frame(); + self.scroll_delta = Vec2::ZERO; + } +} + #[derive(Debug, Clone, Copy, Default, PartialEq)] pub enum ActivationState { Start, @@ -81,29 +90,27 @@ pub enum ActivationState { Off, } -pub struct Sensor { +pub struct Sensor { pub senses: Senses, - pub f: Rc>, + pub f: Rc>, } -pub type SensorMap = HashMap>; +pub type SensorMap = HashMap>; pub type SenseShape = UiRegion; -pub struct SensorGroup { +pub struct SensorGroup { pub hover: ActivationState, - pub sensors: Vec>, + pub sensors: Vec>, } #[derive(Clone)] -pub struct SenseData { +pub struct CursorData { pub cursor: Vec2, pub size: Vec2, + pub scroll_delta: Vec2, } -pub trait SenseFn: Fn(&mut Ctx, SenseData) + 'static {} -impl SenseFn for F {} - pub struct SensorModule { - map: SensorMap, + map: SensorMap, active: HashMap>, } @@ -125,6 +132,9 @@ impl UiModule for SensorModule { fn on_remove(&mut self, id: &Id) { self.map.remove(id); + for layer in self.active.values_mut() { + layer.remove(id); + } } fn on_move(&mut self, inst: &WidgetInstance) { @@ -165,7 +175,7 @@ impl SensorModule { let Some(list) = module.active.get_mut(&i) else { continue; }; - let mut ran = false; + let mut sensed = false; for (id, shape) in list.iter() { let group = module.map.get_mut(id).unwrap(); let region = shape.to_screen(window_size); @@ -174,19 +184,20 @@ impl SensorModule { if group.hover == ActivationState::Off { continue; } + sensed = true; for sensor in &mut group.sensors { - if should_run(&sensor.senses, &cursor.buttons, group.hover) { - ran = true; - let sctx = SenseData { + if should_run(&sensor.senses, cursor, group.hover) { + let data = CursorData { cursor: cursor.pos - region.top_left, size: region.bot_right - region.top_left, + scroll_delta: cursor.scroll_delta, }; - (sensor.f)(ctx, sctx); + (sensor.f)(ctx, data); } } } - if ran { + if sensed { break; } } @@ -198,15 +209,16 @@ impl SensorModule { } } -pub fn should_run(senses: &Senses, cursor: &CursorButtons, hover: ActivationState) -> bool { +pub fn should_run(senses: &Senses, cursor: &CursorState, hover: ActivationState) -> bool { for sense in senses.iter() { if match sense { - Sense::PressStart(button) => cursor.select(button).is_start(), - Sense::Pressing(button) => cursor.select(button).is_on(), - Sense::PressEnd(button) => cursor.select(button).is_end(), + Sense::PressStart(button) => cursor.buttons.select(button).is_start(), + Sense::Pressing(button) => cursor.buttons.select(button).is_on(), + Sense::PressEnd(button) => cursor.buttons.select(button).is_end(), Sense::HoverStart => hover.is_start(), Sense::Hovering => hover.is_on(), Sense::HoverEnd => hover.is_end(), + Sense::Scroll => cursor.scroll_delta != Vec2::ZERO, } { return true; } @@ -259,12 +271,12 @@ impl ActivationState { impl Event for Senses { type Module = SensorModule; - type Data = SenseData; + type Data = CursorData; } impl Event for Sense { type Module = SensorModule; - type Data = SenseData; + type Data = CursorData; } impl::Data> + Into, Ctx: 'static> EventModule @@ -292,7 +304,7 @@ impl::Data> + Into, Ctx: 'static> Even } }) .collect(); - Some(move |ctx: &mut Ctx, data: SenseData| { + Some(move |ctx: &mut Ctx, data: CursorData| { for f in &fs { f(ctx, data.clone()); } @@ -303,7 +315,7 @@ impl::Data> + Into, Ctx: 'static> Even } } -impl Default for SensorGroup { +impl Default for SensorGroup { fn default() -> Self { Self { hover: Default::default(), diff --git a/src/core/text_edit.rs b/src/core/text_edit.rs index cdf596f..5acfeac 100644 --- a/src/core/text_edit.rs +++ b/src/core/text_edit.rs @@ -43,7 +43,7 @@ impl Widget for TextEdit { painter.primitive_within( RectPrimitive::color(Color::WHITE), UiRegion::from_size_align(size, Align::TopLeft) - .shifted(offset) + .offset(offset) .within(®ion), ); } else { @@ -247,7 +247,13 @@ impl<'a> TextEditCtx<'a> { } _ => return TextInputResult::Unused, }, - Key::Character(text) => self.insert(text), + Key::Character(text) => { + if modifiers.control && text == "v" { + return TextInputResult::Paste; + } else { + self.insert(text) + } + } _ => return TextInputResult::Unused, } TextInputResult::Used @@ -272,6 +278,7 @@ pub enum TextInputResult { Unused, Unfocus, Submit, + Paste, } impl TextInputResult { diff --git a/src/core/trait_fns.rs b/src/core/trait_fns.rs index 505a21c..02f4e56 100644 --- a/src/core/trait_fns.rs +++ b/src/core/trait_fns.rs @@ -7,6 +7,7 @@ pub trait CoreWidget { fn center(self) -> impl WidgetFn; fn label(self, label: impl Into) -> impl WidgetIdFn; fn sized(self, size: impl Into) -> impl WidgetFn; + fn offset(self, amt: impl Into) -> impl WidgetFn; } impl, Tag> CoreWidget for W { @@ -42,6 +43,13 @@ impl, Tag> CoreWidget for W { size: size.into(), } } + + fn offset(self, amt: impl Into) -> impl WidgetFn { + move |ui| Offset { + inner: self.add(ui).any(), + amt: amt.into(), + } + } } pub trait CoreWidgetArr, Tag> { diff --git a/src/layout/orientation.rs b/src/layout/orientation.rs index 993c29e..a56ed1e 100644 --- a/src/layout/orientation.rs +++ b/src/layout/orientation.rs @@ -85,7 +85,7 @@ pub enum Align { } impl Align { - pub const fn anchor(&self) -> Vec2 { + pub const fn rel(&self) -> Vec2 { match self { Self::TopLeft => vec2(0.0, 0.0), Self::Top => vec2(0.5, 0.0), diff --git a/src/layout/painter.rs b/src/layout/painter.rs index 436f0b4..ff413c1 100644 --- a/src/layout/painter.rs +++ b/src/layout/painter.rs @@ -131,7 +131,9 @@ impl<'a> PainterCtx<'a> { if active.region == region { return; } else if active.region.size() == region.size() { - self.mov(id, region); + // TODO: epsilon? + let from = active.region; + self.mov(id, from, region); return; } let active = self.remove(id).unwrap(); @@ -185,22 +187,21 @@ impl<'a> PainterCtx<'a> { self.active.insert(id, instance); } - fn mov(&mut self, id: Id, to: UiRegion) { + fn mov(&mut self, id: Id, from: UiRegion, to: UiRegion) { let active = self.active.get_mut(&id).unwrap(); // children will not be changed, so this technically should not be needed // probably need unsafe - let from = active.region; for h in &active.primitives { let region = self.layers[h.layer].primitives.region_mut(h); *region = region.outside(&from).within(&to); } - active.region = to; + active.region = active.region.outside(&from).within(&to); for m in self.modules.iter_mut() { m.on_move(active); } let children = active.children.clone(); for child in children { - self.mov(child, to); + self.mov(child, from, to); } } diff --git a/src/layout/pos.rs b/src/layout/pos.rs index 6ac2c61..6dcba40 100644 --- a/src/layout/pos.rs +++ b/src/layout/pos.rs @@ -11,23 +11,20 @@ pub struct UiVec2 { } impl UiVec2 { + pub const ZERO: Self = Self { + rel: Vec2::ZERO, + abs: Vec2::ZERO, + }; + /// expands this position into a sized region centered at self pub fn expand(&self, size: impl Into) -> UiRegion { let size = size.into(); UiRegion { - top_left: self.shifted(-size / 2.0), - bot_right: self.shifted(size / 2.0), + top_left: self.offset(-size / 2.0), + bot_right: self.offset(size / 2.0), } } - pub const fn anchor(anchor: Vec2) -> Self { - Self::rel(anchor) - } - - pub const fn offset(offset: Vec2) -> Self { - Self::abs(offset) - } - pub const fn abs(abs: Vec2) -> Self { Self { rel: Vec2::ZERO, @@ -42,11 +39,12 @@ impl UiVec2 { } } - pub const fn shift(&mut self, offset: impl const Into) { - self.abs += offset.into(); + pub const fn shift(&mut self, offset: impl const Into) { + let offset = offset.into(); + *self += offset; } - pub const fn shifted(mut self, offset: Vec2) -> Self { + pub const fn offset(mut self, offset: impl const Into) -> Self { self.shift(offset); self } @@ -118,7 +116,7 @@ impl_op!(UiVec2 Sub sub; rel abs); impl const From for UiVec2 { fn from(align: Align) -> Self { - Self::anchor(align.anchor()) + Self::rel(align.rel()) } } @@ -128,6 +126,12 @@ impl Align { } } +impl const From for UiVec2 { + fn from(abs: Vec2) -> Self { + Self::abs(abs) + } +} + #[derive(Clone, Copy, Debug, Default)] pub struct UiScalar { pub rel: f32, @@ -198,10 +202,10 @@ impl UiRegion { bot_right: Align::BotRight.into(), } } - pub fn anchor(anchor: Vec2) -> Self { + pub fn rel(anchor: Vec2) -> Self { Self { - top_left: UiVec2::anchor(anchor), - bot_right: UiVec2::anchor(anchor), + top_left: UiVec2::rel(anchor), + bot_right: UiVec2::rel(anchor), } } pub fn within(&self, parent: &Self) -> Self { @@ -229,13 +233,13 @@ impl UiRegion { std::mem::swap(&mut self.top_left, &mut self.bot_right); } - pub fn shift(&mut self, offset: impl Into) { + pub fn shift(&mut self, offset: impl Into) { let offset = offset.into(); self.top_left.shift(offset); self.bot_right.shift(offset); } - pub fn shifted(mut self, offset: impl Into) -> Self { + pub fn offset(mut self, offset: impl Into) -> Self { self.shift(offset); self } @@ -265,9 +269,9 @@ impl UiRegion { pub fn from_size_align(size: Vec2, align: Align) -> Self { let mut top_left = UiVec2::from(align); - top_left.abs -= size * align.anchor(); + top_left.abs -= size * align.rel(); let mut bot_right = UiVec2::from(align); - bot_right.abs += size * (Vec2::ONE - align.anchor()); + bot_right.abs += size * (Vec2::ONE - align.rel()); Self { top_left, bot_right, @@ -276,11 +280,11 @@ impl UiRegion { pub fn from_ui_size_align(size: UiVec2, align: Align) -> Self { let mut top_left = UiVec2::from(align); - top_left.abs -= size.abs * align.anchor(); - top_left.rel -= size.rel * align.anchor(); + top_left.abs -= size.abs * align.rel(); + top_left.rel -= size.rel * align.rel(); let mut bot_right = UiVec2::from(align); - bot_right.abs += size.abs * (Vec2::ONE - align.anchor()); - bot_right.rel += size.rel * (Vec2::ONE - align.anchor()); + bot_right.abs += size.abs * (Vec2::ONE - align.rel()); + bot_right.rel += size.rel * (Vec2::ONE - align.rel()); Self { top_left, bot_right, diff --git a/src/lib.rs b/src/lib.rs index 4623200..5cab667 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,6 @@ #![feature(const_trait_impl)] #![feature(const_convert)] #![feature(map_try_insert)] -#![feature(trait_alias)] #![feature(unboxed_closures)] #![feature(fn_traits)] #![feature(const_cmp)] diff --git a/src/testing/input.rs b/src/testing/input.rs index 77b99df..b1de954 100644 --- a/src/testing/input.rs +++ b/src/testing/input.rs @@ -3,7 +3,7 @@ use ui::{ layout::Vec2, }; use winit::{ - event::{MouseButton, WindowEvent}, + event::{MouseButton, MouseScrollDelta, WindowEvent}, keyboard::{Key, NamedKey}, }; @@ -32,6 +32,13 @@ impl Input { _ => (), } } + WindowEvent::MouseWheel { delta, .. } => { + let delta = match *delta { + MouseScrollDelta::LineDelta(x, y) => Vec2::new(x, y), + MouseScrollDelta::PixelDelta(pos) => Vec2::new(pos.x as f32, pos.y as f32), + }; + self.cursor.scroll_delta = delta; + } WindowEvent::CursorLeft { .. } => { self.cursor.exists = false; self.modifiers.clear(); @@ -56,7 +63,7 @@ impl Input { } pub fn end_frame(&mut self) { - self.cursor.buttons.end_frame(); + self.cursor.end_frame(); } } diff --git a/src/testing/mod.rs b/src/testing/mod.rs index f5a8e23..3ad9710 100644 --- a/src/testing/mod.rs +++ b/src/testing/mod.rs @@ -1,6 +1,7 @@ use std::sync::Arc; use app::App; +use arboard::Clipboard; use cosmic_text::Family; use render::Renderer; use ui::prelude::*; @@ -22,6 +23,7 @@ pub struct Client { ui: Ui, info: WidgetId, focus: Option>, + clipboard: Clipboard, } #[derive(Eq, PartialEq, Hash, Clone)] @@ -127,6 +129,15 @@ impl Client { .add_static(&mut ui); let texts = Span::empty(Dir::DOWN).add_static(&mut ui); + let msg_area = ( + Rect::new(Color::SKY), + texts + .offset(UiVec2::ZERO) + .edit_on(Sense::Scroll, |w, data| { + w.amt += UiVec2::abs(data.scroll_delta * 50.0); + }), + ) + .stack(); let add_text = text_edit("add") .text_align(Align::Left) .size(30) @@ -151,7 +162,7 @@ impl Client { }) .add(&mut ui); let text_edit_scroll = ( - (Rect::new(Color::SKY), texts).stack(), + msg_area, ( Rect::new(Color::WHITE.darker(0.9)), ( @@ -217,6 +228,7 @@ impl Client { ui, info, focus: None, + clipboard: Clipboard::new().unwrap(), } } @@ -249,14 +261,20 @@ impl Client { if let Some(sel) = &self.focus && event.state.is_pressed() { - match self.ui.text(sel).apply_event(&event, &self.input.modifiers) { + let mut text = self.ui.text(sel); + match text.apply_event(&event, &self.input.modifiers) { TextInputResult::Unfocus => { self.focus = None; } TextInputResult::Submit => { self.run_event(&sel.clone(), Submit, ()); } - _ => (), + TextInputResult::Paste => { + if let Ok(t) = self.clipboard.get_text() { + text.insert(&t); + } + } + TextInputResult::Unused | TextInputResult::Used => (), } } }