diff --git a/Cargo.lock b/Cargo.lock index 55234db..256ee83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,12 +44,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - [[package]] name = "allocator-api2" version = "0.2.16" @@ -64,12 +58,12 @@ checksum = "0f1063fbee2ac6d371ffcb55cb443f4d5b469f1b8eace60042878242bb534169" [[package]] name = "android-activity" -version = "0.5.0-beta.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934936a9ad4dc79563cd6644fcef68dc328f105d927679454de39ad03ca1cfe8" +checksum = "052ad56e336bcc615a214bffbeca6c181ee9550acec193f0327e0b103b033a4d" dependencies = [ "android-properties", - "bitflags 2.4.0", + "bitflags 2.4.1", "cc", "cesu8", "jni", @@ -101,9 +95,9 @@ dependencies = [ [[package]] name = "appit" version = "0.1.0" -source = "git+https://github.com/khonsulabs/appit#0503b3d4668ec9e153952e509462216a2c5d85a9" +source = "git+https://github.com/khonsulabs/appit#665107f59b0d1b1cc6780800fa1e172faf615f07" dependencies = [ - "raw-window-handle", + "raw-window-handle 0.5.2", "winit", ] @@ -184,9 +178,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "block" @@ -367,16 +361,17 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0b68966c2543609f8d92f9d33ac3b719b2a67529b0c6c0b3e025637b477eef9" +checksum = "75acbfb314aeb4f5210d379af45ed1ec2c98c7f1790bf57b8a4c562ac0c51b71" dependencies = [ - "aliasable", "fontdb", "libm", "log", "rangemap", + "rustc-hash", "rustybuzz", + "self_cell", "swash", "sys-locale", "unicode-bidi", @@ -397,7 +392,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e16e44ab292b1dddfdaf7be62cfd8877df52f2f3fde5858d95bab606be259f20" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "libloading 0.8.1", "winapi", ] @@ -475,16 +470,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "fontdb" -version = "0.14.1" +name = "fontconfig-parser" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8d8cbea8f21307d7e84bca254772981296f058a1d36b461bf4d83a7499fc9e" +checksum = "674e258f4b5d2dcd63888c01c68413c51f565e8af99d2f7701c7b81d79ef41c4" dependencies = [ + "roxmltree", +] + +[[package]] +name = "fontdb" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020e203f177c0fb250fb19455a252e838d2bbbce1f80f25ecc42402aafa8cd38" +dependencies = [ + "fontconfig-parser", "log", - "memmap2 0.6.2", + "memmap2 0.8.0", "slotmap", "tinyvec", - "ttf-parser", + "ttf-parser 0.19.2", ] [[package]] @@ -572,7 +577,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "gpu-alloc-types", ] @@ -582,7 +587,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", ] [[package]] @@ -604,9 +609,9 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "gpu-descriptor-types", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -615,7 +620,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", ] [[package]] @@ -626,9 +631,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.1" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" +checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" dependencies = [ "ahash", "allocator-api2", @@ -702,7 +707,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", - "hashbrown 0.14.1", + "hashbrown 0.14.2", ] [[package]] @@ -776,7 +781,7 @@ dependencies = [ [[package]] name = "kludgine" version = "0.1.0" -source = "git+https://github.com/khonsulabs/kludgine#6f8df041075f1c25f30cb53a134f06931c1b7a37" +source = "git+https://github.com/khonsulabs/kludgine#80d722cbf4fbb1e1819f2b5ab8cb52bb331e9cf4" dependencies = [ "ahash", "alot", @@ -838,9 +843,9 @@ checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -910,9 +915,9 @@ dependencies = [ [[package]] name = "memmap2" -version = "0.6.2" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d28bba84adfe6646737845bc5ebbfa2c08424eb1c37e94a1fd2a82adb56a872" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" dependencies = [ "libc", ] @@ -941,7 +946,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "623b5e6cefd76e58f774bd3cc0c6f5c7615c58c03a97815245a25c3c9bdee318" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -972,7 +977,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1ceaaa4eedaece7e4ec08c55c640ba03dbb73fb812a6570a59bcf1930d0f70e" dependencies = [ "bit-set", - "bitflags 2.4.0", + "bitflags 2.4.1", "codespan-reporting", "hexf-parse", "indexmap 1.9.3", @@ -987,16 +992,16 @@ dependencies = [ [[package]] name = "ndk" -version = "0.8.0-beta.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f58741784b0f6ac12311c3f6fbdb3c766a992f8914d035c77a07b5fd2940dc" +checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "jni-sys", "log", "ndk-sys", "num_enum", - "raw-window-handle", + "raw-window-handle 0.6.0", "thiserror", ] @@ -1008,9 +1013,9 @@ checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] name = "ndk-sys" -version = "0.5.0-beta.0+25.2.9519653" +version = "0.5.0+25.2.9519653" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff38603775cba10d0f1141fab1b167df73662a0636a4b631c187fe11fb624042" +checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" dependencies = [ "jni-sys", ] @@ -1164,7 +1169,7 @@ version = "0.3.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8378ac0dfbd4e7895f2d2c1f1345cab3836910baf3a300b000d04250f0c8428f" dependencies = [ - "redox_syscall", + "redox_syscall 0.3.5", ] [[package]] @@ -1173,7 +1178,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "706de7e2214113d63a8238d1910463cfce781129a6f263d13fdb09ff64355ba4" dependencies = [ - "ttf-parser", + "ttf-parser 0.19.2", ] [[package]] @@ -1188,13 +1193,13 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.8" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.4.1", "smallvec", "windows-targets 0.48.5", ] @@ -1284,6 +1289,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" +[[package]] +name = "raw-window-handle" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a9830a0e1b9fb145ebb365b8bc4ccd75f290f98c0247deafbbe2c75cefb544" + [[package]] name = "redox_syscall" version = "0.3.5" @@ -1293,12 +1304,30 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "redox_syscall" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +dependencies = [ + "bitflags 1.3.2", +] + [[package]] name = "renderdoc-sys" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "216080ab382b992234dda86873c18d4c48358f5cfcb70fd693d7f6f2131b628b" +[[package]] +name = "roxmltree" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862340e351ce1b271a378ec53f304a5558f7db87f3769dc655a8f6ecbb68b302" +dependencies = [ + "xmlparser", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1313,11 +1342,11 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" -version = "0.38.19" +version = "0.38.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" +checksum = "67ce50cb2e16c2903e30d1cbccfd8387a74b9d4c938b6a4c5ec6cc7556f7a8a0" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", @@ -1326,18 +1355,18 @@ dependencies = [ [[package]] name = "rustybuzz" -version = "0.8.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82eea22c8f56965eeaf3a209b3d24508256c7b920fb3b6211b8ba0f7c0583250" +checksum = "2ee8fe2a8461a0854a37101fe7a1b13998d0cfa987e43248e81d2a5f4570f6fa" dependencies = [ "bitflags 1.3.2", "bytemuck", "libm", "smallvec", - "ttf-parser", + "ttf-parser 0.20.0", "unicode-bidi-mirroring", "unicode-ccc", - "unicode-general-category", + "unicode-properties", "unicode-script", ] @@ -1375,6 +1404,12 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "self_cell" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c309e515543e67811222dbc9e3dd7e1056279b782e1dacffe4242b718734fb6" + [[package]] name = "serde" version = "1.0.189" @@ -1512,18 +1547,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", @@ -1593,6 +1628,12 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49d64318d8311fc2668e48b63969f4343e0a85c4a109aa8460d6672e364b8bd1" +[[package]] +name = "ttf-parser" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" + [[package]] name = "unicode-bidi" version = "0.3.13" @@ -1611,12 +1652,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" -[[package]] -name = "unicode-general-category" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" - [[package]] name = "unicode-ident" version = "1.0.12" @@ -1629,6 +1664,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" +[[package]] +name = "unicode-properties" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f91c8b21fbbaa18853c3d0801c78f4fc94cdb976699bb03e832e75f7fd22f0" + [[package]] name = "unicode-script" version = "0.5.5" @@ -1861,7 +1902,7 @@ dependencies = [ "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "smallvec", "static_assertions", "wasm-bindgen", @@ -1880,13 +1921,13 @@ checksum = "0f8a44dd301a30ceeed3c27d8c0090433d3da04d7b2a4042738095a424d12ae7" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.4.0", + "bitflags 2.4.1", "codespan-reporting", "log", "naga", "parking_lot", "profiling", - "raw-window-handle", + "raw-window-handle 0.5.2", "rustc-hash", "smallvec", "thiserror", @@ -1905,7 +1946,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.4.0", + "bitflags 2.4.1", "block", "core-graphics-types", "d3d12", @@ -1925,7 +1966,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle", + "raw-window-handle 0.5.2", "renderdoc-sys", "rustc-hash", "smallvec", @@ -1942,7 +1983,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee64d7398d0c2f9ca48922c902ef69c42d000c759f3db41e355f4a570b052b67" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "js-sys", "web-sys", ] @@ -2142,7 +2183,7 @@ checksum = "ab977231134a3123c5382f0358b728118e70c216ec99017aa24e9eed35d5e3e1" dependencies = [ "android-activity", "atomic-waker", - "bitflags 2.4.0", + "bitflags 2.4.1", "bytemuck", "calloop", "cfg_aliases", @@ -2161,8 +2202,8 @@ dependencies = [ "once_cell", "orbclient", "percent-encoding", - "raw-window-handle", - "redox_syscall", + "raw-window-handle 0.5.2", + "redox_syscall 0.3.5", "rustix", "sctk-adwaita", "smithay-client-toolkit", @@ -2242,7 +2283,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6924668544c48c0133152e7eec86d644a056ca3d09275eb8d5cdb9855f9d8699" dependencies = [ - "bitflags 2.4.0", + "bitflags 2.4.1", "dlib", "log", "once_cell", @@ -2255,6 +2296,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "054a8e68b76250b253f671d1268cb7f1ae089ec35e195b2efb2a4e9a836d0621" +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + [[package]] name = "yazi" version = "0.1.6" diff --git a/examples/canvas.rs b/examples/canvas.rs index 82a4131..b9b4eb4 100644 --- a/examples/canvas.rs +++ b/examples/canvas.rs @@ -7,11 +7,11 @@ use kludgine::Color; fn main() -> gooey::Result<()> { let mut angle = Angle::degrees(0); - Canvas::new(move |graphics, _window| { + Canvas::new(move |context| { angle += Angle::degrees(1); - let center = Point::from(graphics.size()).into_signed() / 2; - graphics.draw_text( + let center = Point::from(context.graphics.size()).into_signed() / 2; + context.graphics.draw_text( "Canvas exposes the full power of Kludgine", Color::WHITE, kludgine::text::TextOrigin::Center, @@ -20,7 +20,7 @@ fn main() -> gooey::Result<()> { None, None, ); - graphics.draw_shape( + context.graphics.draw_shape( &Shape::filled_rect( Rect::new(Point::new(Px(-50), Px(-50)), Size::new(Px(100), Px(100))), Color::RED, diff --git a/examples/input.rs b/examples/input.rs index 4393ddd..e22400d 100644 --- a/examples/input.rs +++ b/examples/input.rs @@ -1,6 +1,5 @@ -use gooey::dynamic::Dynamic; use gooey::widget::Widget; -use gooey::widgets::{Button, Input}; +use gooey::widgets::Input; use gooey::EventLoopError; fn main() -> Result<(), EventLoopError> { diff --git a/examples/style.rs b/examples/style.rs index d225183..30052bc 100644 --- a/examples/style.rs +++ b/examples/style.rs @@ -1,5 +1,6 @@ use gooey::children::Children; use gooey::styles::{Styles, TextColor}; +use gooey::widget::Widget; use gooey::widgets::array::Array; use gooey::widgets::{Button, Style}; use gooey::window::Window; @@ -10,11 +11,12 @@ fn main() -> Result<(), EventLoopError> { Window::for_widget(Array::rows( Children::new() .with_widget(Button::new("Default")) - .with_widget(Style::new( - Styles::new().with(&TextColor, Color::RED), - Button::new("Styled"), - )), + .with_widget(styled(Button::new("Styled"))), )) .styles(Styles::new().with(&TextColor, Color::GREEN)) .run() } + +fn styled(w: impl Widget) -> Style { + Style::new(Styles::new().with(&TextColor, Color::RED), w) +} diff --git a/src/context.rs b/src/context.rs index 5f1a0d7..fb9e2aa 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,80 +1,35 @@ use std::ops::{Deref, DerefMut}; -use kludgine::app::winit::event::{DeviceId, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase}; +use kludgine::app::winit::event::{ + DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase, +}; use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{IntoSigned, Point, Rect, Size}; +use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::Kludgine; use crate::dynamic::Dynamic; use crate::graphics::Graphics; -use crate::styles::{ComponentDefaultvalue, Styles}; +use crate::styles::{ComponentDefaultvalue, HighlightColor, Styles}; use crate::widget::{BoxedWidget, EventHandling, ManagedWidget}; use crate::window::RunningWindow; use crate::ConstraintLimit; -pub struct Context<'context, 'window> { - current_node: &'context ManagedWidget, - window: &'context mut RunningWindow<'window>, - pending_state: PendingState<'context>, +pub struct EventContext<'context, 'window> { + pub widget: WidgetContext<'context, 'window>, + pub kludgine: &'context mut Kludgine, } -impl<'context, 'window> Context<'context, 'window> { - pub fn new( - current_node: &'context ManagedWidget, - window: &'context mut RunningWindow<'window>, - ) -> Self { - Self { - current_node, - window, - pending_state: PendingState::Owned(PendingWidgetState { - focus: current_node - .tree - .focused_widget() - .map(|id| current_node.tree.widget(id)), - active: current_node - .tree - .active_widget() - .map(|id| current_node.tree.widget(id)), - }), - } +impl<'context, 'window> EventContext<'context, 'window> { + pub fn new(widget: WidgetContext<'context, 'window>, kludgine: &'context mut Kludgine) -> Self { + Self { widget, kludgine } } pub fn for_other<'child>( &'child mut self, widget: &'child ManagedWidget, - ) -> Context<'child, 'window> { - Context { - current_node: widget, - window: &mut *self.window, - pending_state: self.pending_state.borrowed(), - } - } - - pub(crate) fn parent(&self) -> Option { - self.current_node.parent() - } - - pub fn redraw_when_changed(&self, value: &Dynamic) { - value.redraw_when_changed(self.window.handle()); - } - - pub fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>) { - // TODO this should not use clip_rect, because it forces UPx, and once - // we have scrolling, we can have negative offsets of rectangles where - // it's clipped partially. - self.current_node - .note_rendered_rect(graphics.clip_rect().into_signed()); - self.current_node.lock().redraw(graphics, self); - } - - pub fn measure( - &mut self, - available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - ) -> Size { - self.current_node - .lock() - .measure(available_space, graphics, self) + ) -> EventContext<'child, 'window> { + EventContext::new(self.widget.for_other(widget), self.kludgine) } pub fn hit_test(&mut self, location: Point) -> bool { @@ -114,11 +69,14 @@ impl<'context, 'window> Context<'context, 'window> { device_id: DeviceId, input: KeyEvent, is_synthetic: bool, - kludgine: &mut Kludgine, ) -> EventHandling { self.current_node .lock() - .keyboard_input(device_id, input, is_synthetic, kludgine, self) + .keyboard_input(device_id, input, is_synthetic, self) + } + + pub fn ime(&mut self, ime: Ime) -> EventHandling { + self.current_node.lock().ime(ime, self) } pub fn mouse_wheel( @@ -132,30 +90,6 @@ impl<'context, 'window> Context<'context, 'window> { .mouse_wheel(device_id, delta, phase, self) } - #[must_use] - pub fn push_child(&mut self, child: BoxedWidget) -> ManagedWidget { - let pushed_widget = self - .current_node - .tree - .push_boxed(child, Some(self.current_node)); - pushed_widget - .lock() - .mounted(&mut self.for_other(&pushed_widget)); - pushed_widget - } - - pub fn remove_child(&mut self, child: &ManagedWidget) { - self.current_node - .tree - .remove_child(child, self.current_node); - child.lock().unmounted(&mut self.for_other(child)); - } - - #[must_use] - pub fn last_rendered_at(&self) -> Option> { - self.current_node.last_rendered_at() - } - pub(crate) fn hover(&mut self, location: Point) { let newly_hovered = match self.current_node.tree.hover(Some(self.current_node)) { Ok(old_hover) => { @@ -178,6 +112,250 @@ impl<'context, 'window> Context<'context, 'window> { old_hover.lock().unhover(&mut old_hover_context); } } +} + +impl<'context, 'window> Deref for EventContext<'context, 'window> { + type Target = WidgetContext<'context, 'window>; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl<'context, 'window> DerefMut for EventContext<'context, 'window> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + +pub enum Exclusive<'a, T> { + Borrowed(&'a mut T), + Owned(T), +} + +impl Deref for Exclusive<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + Exclusive::Borrowed(wrapped) => wrapped, + Exclusive::Owned(wrapped) => wrapped, + } + } +} + +impl DerefMut for Exclusive<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + Exclusive::Borrowed(wrapped) => wrapped, + Exclusive::Owned(wrapped) => wrapped, + } + } +} + +pub struct GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> { + pub widget: WidgetContext<'context, 'window>, + pub graphics: Exclusive<'context, Graphics<'clip, 'gfx, 'pass>>, +} + +impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> { + pub fn for_other<'child>( + &'child mut self, + widget: &'child ManagedWidget, + ) -> GraphicsContext<'child, 'window, 'clip, 'gfx, 'pass> { + GraphicsContext { + widget: self.widget.for_other(widget), + graphics: Exclusive::Borrowed(&mut *self.graphics), + } + } + + pub fn measure(&mut self, available_space: Size) -> Size { + self.current_node.lock().measure(available_space, self) + } + + pub fn redraw(&mut self) { + // TODO this should not use clip_rect, because it forces UPx, and once + // we have scrolling, we can have negative offsets of rectangles where + // it's clipped partially. + self.current_node + .note_rendered_rect(self.graphics.clip_rect().into_signed()); + self.current_node.lock().redraw(self); + } + + pub fn clipped_to(&mut self, clip: Rect) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> { + GraphicsContext { + widget: self.widget.borrowed(), + graphics: Exclusive::Owned(self.graphics.clipped_to(clip)), + } + } + + pub fn draw_focus_ring(&mut self, styles: &Styles) { + let visible_rect = Rect::from(self.graphics.size() - (UPx(1), UPx(1))); + let focus_ring = Shape::stroked_rect( + visible_rect, + styles.get_or_default(&HighlightColor), + StrokeOptions::default(), + ); + self.graphics + .draw_shape(&focus_ring, Point::default(), None, None); + } +} + +impl<'context, 'window, 'clip, 'gfx, 'pass> Deref + for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> +{ + type Target = WidgetContext<'context, 'window>; + + fn deref(&self) -> &Self::Target { + &self.widget + } +} + +impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut + for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.widget + } +} + +pub trait AsEventContext<'window> { + fn as_event_context(&mut self) -> EventContext<'_, 'window>; + + #[must_use] + fn push_child(&mut self, child: BoxedWidget) -> ManagedWidget { + let mut context = self.as_event_context(); + let pushed_widget = context + .current_node + .tree + .push_boxed(child, Some(context.current_node)); + pushed_widget + .lock() + .mounted(&mut context.for_other(&pushed_widget)); + pushed_widget + } + + fn remove_child(&mut self, child: &ManagedWidget) { + let mut context = self.as_event_context(); + context + .current_node + .tree + .remove_child(child, context.current_node); + child.lock().unmounted(&mut context.for_other(child)); + } + + fn apply_pending_state(&mut self) { + let mut context = self.as_event_context(); + let active = context.pending_state.active.take(); + if context.current_node.tree.active_widget() != active.as_ref().map(|active| active.id) { + let new = match context.current_node.tree.activate(active.as_ref()) { + Ok(old) => { + if let Some(old) = old { + let mut old_context = context.for_other(&old); + old.lock().deactivate(&mut old_context); + } + true + } + Err(_) => false, + }; + if new { + if let Some(active) = active { + active.lock().activate(&mut context); + } + } + } + + let focus = context.pending_state.focus.take(); + if context.current_node.tree.focused_widget() != focus.as_ref().map(|focus| focus.id) { + let new = match context.current_node.tree.focus(focus.as_ref()) { + Ok(old) => { + if let Some(old) = old { + let mut old_context = context.for_other(&old); + old.lock().blur(&mut old_context); + } + true + } + Err(_) => false, + }; + if new { + if let Some(focus) = focus { + focus.lock().focus(&mut context); + } + } + } + } +} + +impl<'window> AsEventContext<'window> for EventContext<'_, 'window> { + fn as_event_context(&mut self) -> EventContext<'_, 'window> { + EventContext::new(self.widget.borrowed(), self.kludgine) + } +} + +impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> { + fn as_event_context(&mut self) -> EventContext<'_, 'window> { + EventContext::new(self.widget.borrowed(), &mut self.graphics) + } +} + +pub struct WidgetContext<'context, 'window> { + current_node: &'context ManagedWidget, + window: &'context mut RunningWindow<'window>, + pending_state: PendingState<'context>, +} + +impl<'context, 'window> WidgetContext<'context, 'window> { + pub(crate) fn new( + current_node: &'context ManagedWidget, + window: &'context mut RunningWindow<'window>, + ) -> Self { + Self { + current_node, + window, + pending_state: PendingState::Owned(PendingWidgetState { + focus: current_node + .tree + .focused_widget() + .map(|id| current_node.tree.widget(id)), + active: current_node + .tree + .active_widget() + .map(|id| current_node.tree.widget(id)), + }), + } + } + + pub fn borrowed(&mut self) -> WidgetContext<'_, 'window> { + WidgetContext { + current_node: self.current_node, + window: &mut *self.window, + pending_state: self.pending_state.borrowed(), + } + } + + pub fn for_other<'child>( + &'child mut self, + widget: &'child ManagedWidget, + ) -> WidgetContext<'child, 'window> { + WidgetContext { + current_node: widget, + window: &mut *self.window, + pending_state: self.pending_state.borrowed(), + } + } + + pub(crate) fn parent(&self) -> Option { + self.current_node.parent() + } + + pub fn redraw_when_changed(&self, value: &Dynamic) { + value.redraw_when_changed(self.window.handle()); + } + + #[must_use] + pub fn last_rendered_at(&self) -> Option> { + self.current_node.last_rendered_at() + } pub fn focus(&mut self) { self.pending_state.focus = Some(self.current_node.clone()); @@ -238,46 +416,6 @@ impl<'context, 'window> Context<'context, 'window> { self.pending_state.focus.as_ref() == Some(self.current_node) } - fn apply_pending_state(&mut self) { - let active = self.pending_state.active.take(); - if self.current_node.tree.active_widget() != active.as_ref().map(|active| active.id) { - let new = match self.current_node.tree.activate(active.as_ref()) { - Ok(old) => { - if let Some(old) = old { - let mut old_context = self.for_other(&old); - old.lock().deactivate(&mut old_context); - } - true - } - Err(_) => false, - }; - if new { - if let Some(active) = active { - active.lock().activate(self); - } - } - } - - let focus = self.pending_state.focus.take(); - if self.current_node.tree.focused_widget() != focus.as_ref().map(|focus| focus.id) { - let new = match self.current_node.tree.focus(focus.as_ref()) { - Ok(old) => { - if let Some(old) = old { - let mut old_context = self.for_other(&old); - old.lock().blur(&mut old_context); - } - true - } - Err(_) => false, - }; - if new { - if let Some(focus) = focus { - focus.lock().focus(self); - } - } - } - } - #[must_use] pub const fn widget(&self) -> &ManagedWidget { self.current_node @@ -293,22 +431,24 @@ impl<'context, 'window> Context<'context, 'window> { } } -impl Drop for Context<'_, '_> { +impl dyn AsEventContext<'_> {} + +impl Drop for EventContext<'_, '_> { fn drop(&mut self) { - if matches!(self.pending_state, PendingState::Owned(_)) { + if matches!(self.widget.pending_state, PendingState::Owned(_)) { self.apply_pending_state(); } } } -impl<'window> Deref for Context<'_, 'window> { +impl<'window> Deref for WidgetContext<'_, 'window> { type Target = RunningWindow<'window>; fn deref(&self) -> &Self::Target { self.window } } -impl<'window> DerefMut for Context<'_, 'window> { +impl<'window> DerefMut for WidgetContext<'_, 'window> { fn deref_mut(&mut self) -> &mut Self::Target { self.window } diff --git a/src/graphics.rs b/src/graphics.rs index 2aa912e..e03b55f 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -4,10 +4,10 @@ use kludgine::figures::units::UPx; use kludgine::figures::Rect; pub struct Graphics<'clip, 'gfx, 'pass> { - renderer: GraphicsContext<'clip, 'gfx, 'pass>, + renderer: RenderContext<'clip, 'gfx, 'pass>, } -enum GraphicsContext<'clip, 'gfx, 'pass> { +enum RenderContext<'clip, 'gfx, 'pass> { Renderer(kludgine::render::Renderer<'gfx, 'pass>), Clipped(kludgine::ClipGuard<'clip, kludgine::render::Renderer<'gfx, 'pass>>), } @@ -16,13 +16,13 @@ impl<'clip, 'gfx, 'pass> Graphics<'clip, 'gfx, 'pass> { #[must_use] pub fn new(renderer: kludgine::render::Renderer<'gfx, 'pass>) -> Self { Self { - renderer: GraphicsContext::Renderer(renderer), + renderer: RenderContext::Renderer(renderer), } } pub fn clipped_to(&mut self, clip: Rect) -> Graphics<'_, 'gfx, 'pass> { Graphics { - renderer: GraphicsContext::Clipped(self.deref_mut().clipped_to(clip)), + renderer: RenderContext::Clipped(self.deref_mut().clipped_to(clip)), } } } @@ -32,8 +32,8 @@ impl<'gfx, 'pass> Deref for Graphics<'_, 'gfx, 'pass> { fn deref(&self) -> &Self::Target { match &self.renderer { - GraphicsContext::Renderer(renderer) => renderer, - GraphicsContext::Clipped(clipped) => clipped, + RenderContext::Renderer(renderer) => renderer, + RenderContext::Clipped(clipped) => clipped, } } } @@ -41,8 +41,8 @@ impl<'gfx, 'pass> Deref for Graphics<'_, 'gfx, 'pass> { impl<'gfx, 'pass> DerefMut for Graphics<'_, 'gfx, 'pass> { fn deref_mut(&mut self) -> &mut Self::Target { match &mut self.renderer { - GraphicsContext::Renderer(renderer) => renderer, - GraphicsContext::Clipped(clipped) => &mut *clipped, + RenderContext::Renderer(renderer) => renderer, + RenderContext::Clipped(clipped) => &mut *clipped, } } } diff --git a/src/styles.rs b/src/styles.rs index fabfbba..5189fd7 100644 --- a/src/styles.rs +++ b/src/styles.rs @@ -59,6 +59,7 @@ use std::fmt::Debug; use std::panic::{RefUnwindSafe, UnwindSafe}; use kludgine::figures::units::{Lp, Px}; +use kludgine::figures::ScreenScale; use kludgine::Color; #[derive(Debug, Clone)] @@ -155,6 +156,33 @@ impl From for Dimension { } } +impl ScreenScale for Dimension { + type Lp = Lp; + type Px = Px; + + fn into_px(self, scale: kludgine::figures::Fraction) -> Px { + match self { + Dimension::Px(px) => px, + Dimension::Lp(lp) => lp.into_px(scale), + } + } + + fn from_px(px: Px, _scale: kludgine::figures::Fraction) -> Self { + Self::from(px) + } + + fn into_lp(self, scale: kludgine::figures::Fraction) -> Lp { + match self { + Dimension::Px(px) => px.into_lp(scale), + Dimension::Lp(lp) => lp, + } + } + + fn from_lp(lp: Lp, _scale: kludgine::figures::Fraction) -> Self { + Self::from(lp) + } +} + #[derive(Debug, Clone)] pub struct BoxedComponent(Arc); @@ -280,6 +308,40 @@ impl NamedComponent for Cow<'_, ComponentName> { } } +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct TextSize; + +impl NamedComponent for TextSize { + fn name(&self) -> Cow<'_, ComponentName> { + Cow::Owned(ComponentName::named::("text_size")) + } +} + +impl ComponentDefinition for TextSize { + type ComponentType = Dimension; + + fn default_value(&self) -> Dimension { + Dimension::Lp(Lp::points(12)) + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +pub struct LineHeight; + +impl NamedComponent for LineHeight { + fn name(&self) -> Cow<'_, ComponentName> { + Cow::Owned(ComponentName::named::("line_height")) + } +} + +impl ComponentDefinition for LineHeight { + type ComponentType = Dimension; + + fn default_value(&self) -> Dimension { + Dimension::Lp(Lp::points(14)) + } +} + #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub struct TextColor; diff --git a/src/utils.rs b/src/utils.rs index b66d115..f309538 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,10 +1,12 @@ use std::ops::Deref; use std::sync::OnceLock; +use kludgine::app::winit::event::Modifiers; use kludgine::app::winit::keyboard::ModifiersState; pub trait ModifiersExt { fn primary(&self) -> bool; + fn word_select(&self) -> bool; } impl ModifiersExt for ModifiersState { @@ -17,6 +19,26 @@ impl ModifiersExt for ModifiersState { fn primary(&self) -> bool { self.control_key() } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn word_select(&self) -> bool { + self.alt_key() + } + + #[cfg(not(any(target_os = "macos", target_os = "ios")))] + fn word_select(&self) -> bool { + self.control_key() + } +} + +impl ModifiersExt for Modifiers { + fn primary(&self) -> bool { + self.state().primary() + } + + fn word_select(&self) -> bool { + self.state().word_select() + } } pub struct Lazy { diff --git a/src/widget.rs b/src/widget.rs index 6409f18..1ce4ad5 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -5,14 +5,14 @@ use std::panic::UnwindSafe; use std::sync::{Arc, Mutex, MutexGuard, PoisonError}; use kludgine::app::winit::error::EventLoopError; -use kludgine::app::winit::event::{DeviceId, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase}; +use kludgine::app::winit::event::{ + DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase, +}; use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{Point, Rect, Size}; -use kludgine::Kludgine; -use crate::context::Context; +use crate::context::{EventContext, GraphicsContext}; use crate::dynamic::Dynamic; -use crate::graphics::Graphics; use crate::styles::{Component, Group, Styles}; use crate::tree::{Tree, WidgetId}; use crate::window::{RunningWindow, Window, WindowBehavior}; @@ -26,42 +26,41 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { Window::::new(BoxedWidget::new(self)).run() } - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context<'_, '_>); + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); fn measure( &mut self, available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - context: &mut Context<'_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size; #[allow(unused_variables)] - fn mounted(&mut self, context: &mut Context<'_, '_>) {} + fn mounted(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn unmounted(&mut self, context: &mut Context<'_, '_>) {} + fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn hit_test(&mut self, location: Point, context: &mut Context<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, context: &mut EventContext<'_, '_>) -> bool { false } #[allow(unused_variables)] - fn hover(&mut self, location: Point, context: &mut Context<'_, '_>) {} + fn hover(&mut self, location: Point, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn unhover(&mut self, context: &mut Context<'_, '_>) {} + fn unhover(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn focus(&mut self, context: &mut Context<'_, '_>) {} + fn focus(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn blur(&mut self, context: &mut Context<'_, '_>) {} + fn blur(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn activate(&mut self, context: &mut Context<'_, '_>) {} + fn activate(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] - fn deactivate(&mut self, context: &mut Context<'_, '_>) {} + fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {} #[allow(unused_variables)] fn mouse_down( @@ -69,7 +68,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) -> EventHandling { UNHANDLED } @@ -80,7 +79,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) { } @@ -90,7 +89,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { location: Option>, device_id: DeviceId, button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) { } @@ -100,11 +99,14 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { device_id: DeviceId, input: KeyEvent, is_synthetic: bool, - kludgine: &mut Kludgine, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) -> EventHandling { UNHANDLED } + #[allow(unused_variables)] + fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { + UNHANDLED + } #[allow(unused_variables)] fn mouse_wheel( @@ -112,7 +114,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) -> EventHandling { UNHANDLED } diff --git a/src/widgets/array.rs b/src/widgets/array.rs index 24c3bac..a4bac12 100644 --- a/src/widgets/array.rs +++ b/src/widgets/array.rs @@ -5,8 +5,7 @@ use kludgine::figures::units::UPx; use kludgine::figures::{Point, Rect, Size}; use crate::children::Children; -use crate::context::Context; -use crate::graphics::Graphics; +use crate::context::{AsEventContext, EventContext, GraphicsContext}; use crate::widget::{IntoValue, ManagedWidget, Value, Widget}; use crate::ConstraintLimit; @@ -45,7 +44,7 @@ impl Array { Self::new(ArrayDirection::rows(), children) } - fn synchronize_children(&mut self, context: &mut Context<'_, '_>) { + fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) { let current_generation = self.children.generation(); if current_generation.map_or_else( || self.children.map(Children::len) != self.layout.children.len(), @@ -91,30 +90,30 @@ impl Array { } impl Widget for Array { - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context) { - self.synchronize_children(context); + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + self.synchronize_children(&mut context.as_event_context()); self.layout.update( Size::new( - ConstraintLimit::Known(graphics.size().width), - ConstraintLimit::Known(graphics.size().height), + ConstraintLimit::Known(context.graphics.size().width), + ConstraintLimit::Known(context.graphics.size().height), ), |child_index, constraints| { context .for_other(&self.synced_children[child_index]) - .measure(constraints, graphics) + .measure(constraints) }, ); for (index, layout) in self.layout.iter().enumerate() { let child = &self.synced_children[index]; if layout.size > 0 { - let mut clipped = graphics.clipped_to(Rect::new( + let mut clipped = context.clipped_to(Rect::new( self.layout.orientation.make_point(layout.offset, UPx(0)), self.layout .orientation .make_size(layout.size, self.layout.other), )); - context.for_other(child).redraw(&mut clipped); + clipped.for_other(child).redraw(); } } } @@ -122,16 +121,15 @@ impl Widget for Array { fn measure( &mut self, available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - context: &mut Context<'_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size { - self.synchronize_children(context); + self.synchronize_children(&mut context.as_event_context()); self.layout .update(available_space, |child_index, constraints| { context .for_other(&self.synced_children[child_index]) - .measure(constraints, graphics) + .measure(constraints) }) } } diff --git a/src/widgets/button.rs b/src/widgets/button.rs index df45739..cfc88a8 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -5,11 +5,10 @@ use kludgine::app::winit::event::{DeviceId, ElementState, KeyEvent, MouseButton} use kludgine::app::winit::keyboard::KeyCode; use kludgine::figures::units::{Px, UPx}; use kludgine::figures::{IntoUnsigned, Point, Rect, Size}; -use kludgine::shapes::{Shape, StrokeOptions}; -use kludgine::{Color, Kludgine}; +use kludgine::shapes::Shape; +use kludgine::Color; -use crate::context::Context; -use crate::graphics::Graphics; +use crate::context::{EventContext, GraphicsContext}; use crate::names::Name; use crate::styles::{ ComponentDefinition, ComponentGroup, ComponentName, HighlightColor, NamedComponent, TextColor, @@ -49,8 +48,8 @@ impl Button { } impl Widget for Button { - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context) { - let center = Point::from(graphics.size()) / 2; + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + let center = Point::from(context.graphics.size()) / 2; if let Value::Dynamic(label) = &self.label { context.redraw_when_changed(label); } @@ -63,7 +62,7 @@ impl Widget for Button { &ButtonHoverBackground, ]); - let visible_rect = Rect::from(graphics.size() - (UPx(1), UPx(1))); + let visible_rect = Rect::from(context.graphics.size() - (UPx(1), UPx(1))); let background = if context.active() { styles.get_or_default(&ButtonActiveBackground) @@ -73,20 +72,17 @@ impl Widget for Button { styles.get_or_default(&ButtonBackground) }; let background = Shape::filled_rect(visible_rect, background); - graphics.draw_shape(&background, Point::default(), None, None); + context + .graphics + .draw_shape(&background, Point::default(), None, None); if context.focused() { - let focus_ring = Shape::stroked_rect( - visible_rect, - styles.get_or_default(&HighlightColor), - StrokeOptions::default(), - ); - graphics.draw_shape(&focus_ring, Point::default(), None, None); + context.draw_focus_ring(&styles); } - let width = graphics.size().width; + let width = context.graphics.size().width; self.label.map(|label| { - graphics.draw_text( + context.graphics.draw_text( label, styles.get_or_default(&TextColor), kludgine::text::TextOrigin::Center, @@ -98,7 +94,7 @@ impl Widget for Button { }); } - fn hit_test(&mut self, _location: Point, _context: &mut Context<'_, '_>) -> bool { + fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_, '_>) -> bool { true } @@ -107,7 +103,7 @@ impl Widget for Button { _location: Point, _device_id: DeviceId, _button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) -> EventHandling { self.buttons_pressed += 1; context.activate(); @@ -119,7 +115,7 @@ impl Widget for Button { location: Point, _device_id: DeviceId, _button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) { let changed = if Rect::from( context @@ -144,7 +140,7 @@ impl Widget for Button { location: Option>, _device_id: DeviceId, _button: MouseButton, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) { self.buttons_pressed -= 1; if self.buttons_pressed == 0 { @@ -170,12 +166,13 @@ impl Widget for Button { fn measure( &mut self, available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - _context: &mut Context<'_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size { let width = available_space.width.max().try_into().unwrap_or(Px::MAX); self.label.map(|label| { - let measured = graphics.measure_text::(label, Color::WHITE, Some(width)); + let measured = context + .graphics + .measure_text::(label, Color::WHITE, Some(width)); let mut size = measured.size.into_unsigned(); size.height = size.height.max(measured.line_height.into_unsigned()); @@ -188,8 +185,7 @@ impl Widget for Button { _device_id: DeviceId, input: KeyEvent, _is_synthetic: bool, - kludgine: &mut Kludgine, - context: &mut Context<'_, '_>, + context: &mut EventContext<'_, '_>, ) -> EventHandling { if input.physical_key == KeyCode::Space { let changed = match input.state { @@ -208,27 +204,27 @@ impl Widget for Button { } } - fn unhover(&mut self, context: &mut Context<'_, '_>) { + fn unhover(&mut self, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } - fn hover(&mut self, _location: Point, context: &mut Context<'_, '_>) { + fn hover(&mut self, _location: Point, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } - fn focus(&mut self, context: &mut Context<'_, '_>) { + fn focus(&mut self, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } - fn blur(&mut self, context: &mut Context<'_, '_>) { + fn blur(&mut self, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } - fn activate(&mut self, context: &mut Context<'_, '_>) { + fn activate(&mut self, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } - fn deactivate(&mut self, context: &mut Context<'_, '_>) { + fn deactivate(&mut self, context: &mut EventContext<'_, '_>) { context.set_needs_redraw(); } } diff --git a/src/widgets/canvas.rs b/src/widgets/canvas.rs index 6234eab..93b3d80 100644 --- a/src/widgets/canvas.rs +++ b/src/widgets/canvas.rs @@ -5,8 +5,7 @@ use std::time::{Duration, Instant}; use kludgine::figures::units::UPx; use kludgine::figures::Size; -use crate::context::Context; -use crate::graphics::Graphics; +use crate::context::GraphicsContext; use crate::widget::Widget; #[must_use] @@ -20,8 +19,7 @@ impl Canvas { pub fn new(render: F) -> Self where F: for<'clip, 'gfx, 'pass, 'context, 'window> FnMut( - &mut Graphics<'clip, 'gfx, 'pass>, - &mut Context<'context, 'window>, + &mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, ) + Send + UnwindSafe + 'static, @@ -42,8 +40,8 @@ impl Canvas { } impl Widget for Canvas { - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context<'_, '_>) { - self.render.render(graphics, context); + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + self.render.render(context); if let Some(target_frame_duration) = self.target_frame_duration { let now = Instant::now(); @@ -58,8 +56,7 @@ impl Widget for Canvas { fn measure( &mut self, available_space: Size, - _graphics: &mut Graphics<'_, '_, '_>, - _context: &mut Context<'_, '_>, + _context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size { Size::new(available_space.width.max(), available_space.height.max()) } @@ -72,19 +69,18 @@ impl Debug for Canvas { } trait RenderFunction: Send + UnwindSafe + 'static { - fn render(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context<'_, '_>); + fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); } impl RenderFunction for F where F: for<'clip, 'gfx, 'pass, 'context, 'window> FnMut( - &mut Graphics<'clip, 'gfx, 'pass>, - &mut Context<'context, 'window>, + &mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, ) + Send + UnwindSafe + 'static, { - fn render(&mut self, graphics: &mut Graphics<'_, '_, '_>, window: &mut Context<'_, '_>) { - self(graphics, window); + fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + self(context); } } diff --git a/src/widgets/input.rs b/src/widgets/input.rs index 253f496..cae02f5 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -1,13 +1,19 @@ +use std::cmp::Ordering; use std::fmt::Debug; +use kludgine::app::winit::event::Ime; use kludgine::app::winit::keyboard::Key; -use kludgine::cosmic_text::{Action, Attrs, Buffer, Edit, Editor, FontSystem, Metrics, Shaping}; +use kludgine::cosmic_text::{Action, Attrs, Buffer, Cursor, Edit, Editor, Metrics, Shaping}; use kludgine::figures::units::Px; -use kludgine::figures::{FloatConversion, IntoUnsigned, Point}; +use kludgine::figures::{ + FloatConversion, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size, +}; +use kludgine::shapes::Shape; use kludgine::text::TextOrigin; use kludgine::{Color, Kludgine}; -use crate::styles::{Styles, TextColor}; +use crate::context::{EventContext, WidgetContext}; +use crate::styles::{HighlightColor, LineHeight, Styles, TextColor, TextSize}; use crate::utils::ModifiersExt; use crate::widget::{EventHandling, IntoValue, Value, Widget, HANDLED, UNHANDLED}; @@ -29,13 +35,28 @@ impl Input { } } - fn editor_mut(&mut self, font_system: &mut FontSystem, styles: &Styles) -> &mut Editor { + fn editor_mut(&mut self, kludgine: &mut Kludgine, styles: &Styles) -> &mut Editor { match (&self.editor, self.text.generation()) { (Some(editor), generation) if editor.generation == generation => {} (_, generation) => { - let mut buffer = Buffer::new(font_system, Metrics::new(12., 18.)); + let scale = kludgine.scale(); + let mut buffer = Buffer::new( + kludgine.font_system(), + Metrics::new( + styles.get_or_default(&TextSize).into_px(scale).into_float(), + styles + .get_or_default(&LineHeight) + .into_px(scale) + .into_float(), + ), + ); self.text.map(|text| { - buffer.set_text(font_system, text, Attrs::new(), Shaping::Advanced); + buffer.set_text( + kludgine.font_system(), + text, + Attrs::new(), + Shaping::Advanced, + ); }); self.editor = Some(LiveEditor { editor: Editor::new(buffer), @@ -46,6 +67,10 @@ impl Input { &mut self.editor.as_mut().expect("just initialized").editor } + + fn styles(context: &WidgetContext<'_, '_>) -> Styles { + context.query_style(&[&TextColor, &TextSize, &LineHeight]) + } } impl Debug for Input { @@ -57,52 +82,186 @@ impl Debug for Input { } impl Widget for Input { - fn hit_test( - &mut self, - location: Point, - context: &mut crate::context::Context<'_, '_>, - ) -> bool { + fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_, '_>) -> bool { true } fn mouse_down( &mut self, location: Point, - device_id: kludgine::app::winit::event::DeviceId, - button: kludgine::app::winit::event::MouseButton, - context: &mut crate::context::Context<'_, '_>, + _device_id: kludgine::app::winit::event::DeviceId, + _button: kludgine::app::winit::event::MouseButton, + context: &mut EventContext<'_, '_>, ) -> EventHandling { - // self.editor_mut(, styles); - + context.focus(); + let styles = context.query_style(&[&TextColor]); + self.editor_mut(context.kludgine, &styles).action( + context.kludgine.font_system(), + Action::Click { + x: location.x.0, + y: location.y.0, + }, + ); + context.set_needs_redraw(); HANDLED } - fn mouse_up( + fn mouse_drag( &mut self, - location: Option>, - device_id: kludgine::app::winit::event::DeviceId, - button: kludgine::app::winit::event::MouseButton, - context: &mut crate::context::Context<'_, '_>, + location: Point, + _device_id: kludgine::app::winit::event::DeviceId, + _button: kludgine::app::winit::event::MouseButton, + context: &mut EventContext<'_, '_>, ) { - context.focus(); + let styles = context.query_style(&[&TextColor]); + self.editor_mut(context.kludgine, &styles).action( + context.kludgine.font_system(), + Action::Drag { + x: location.x.0, + y: location.y.0, + }, + ); + context.set_needs_redraw(); } - fn redraw( - &mut self, - graphics: &mut crate::graphics::Graphics<'_, '_, '_>, - context: &mut crate::context::Context<'_, '_>, - ) { - let size = graphics.size(); - let styles = context.query_style(&[&TextColor]); - let editor = self.editor_mut(graphics.font_system(), &styles); + #[allow(clippy::too_many_lines)] + fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { + let size = context.graphics.size(); + let styles = context.query_style(&[&TextColor, &HighlightColor]); + let highlight = styles.get_or_default(&HighlightColor); + let editor = self.editor_mut(&mut context.graphics, &styles); + let cursor = editor.cursor(); + let selection = editor.select_opt(); let buffer = editor.buffer_mut(); buffer.set_size( - graphics.font_system(), + context.graphics.font_system(), size.width.into_float(), size.height.into_float(), ); - buffer.shape_until_scroll(graphics.font_system()); - graphics.draw_text_buffer( + buffer.shape_until_scroll(context.graphics.font_system()); + + if context.focused() { + context.draw_focus_ring(&styles); + context.set_ime_allowed(true); + let line_height = Px::from_float(buffer.metrics().line_height); + if let Some(selection) = selection { + let (start, end) = if selection < cursor { + (selection, cursor) + } else { + (cursor, selection) + }; + + match (cursor_glyph(buffer, &start), cursor_glyph(buffer, &end)) { + (Ok((start_position, _)), Ok((end_position, end_width))) => { + if start_position.y == end_position.y { + // Single line selection + let width = end_position.x - start_position.x + end_width; + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new(start_position, Size::new(width, line_height)), + highlight, + ), + Point::default(), + None, + None, + ); + } else { + // Draw from start to end of line, + let width = size.width.into_signed() - start_position.x; + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new(start_position, Size::new(width, line_height)), + highlight, + ), + Point::default(), + None, + None, + ); + // Fill region between + let bottom_of_first_line = start_position.y + line_height; + let distance_between = end_position.y - bottom_of_first_line; + if distance_between > 0 { + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new( + Point::new(Px(0), bottom_of_first_line), + Size::new(size.width.into_signed(), distance_between), + ), + highlight, + ), + Point::default(), + None, + None, + ); + } + // Draw from 0 to end + width + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new( + Point::new(Px(0), end_position.y), + Size::new(end_position.x + end_width, line_height), + ), + highlight, + ), + Point::default(), + None, + None, + ); + } + } + (Ok((start_position, _)), Err(_)) => { + let width = size.width.into_signed() - start_position.x; + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new(start_position, Size::new(width, line_height)), + highlight, + ), + Point::default(), + None, + None, + ); + } + (Err(_), Ok((end_position, end_width))) => { + if end_position.y > 0 { + todo!("fill above start"); + } + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new( + Point::new(Px(0), end_position.y), + Size::new(end_position.x + end_width, line_height), + ), + highlight, + ), + Point::default(), + None, + None, + ); + } + (Err(start_not_visible), Err(end_not_visible)) + if start_not_visible != end_not_visible => + { + todo!("render full selection") + } + (Err(_), Err(_)) => {} + } + } else if let Ok((location, _)) = cursor_glyph(buffer, &cursor) { + context.graphics.draw_shape( + &Shape::filled_rect( + Rect::new( + location, + Size::new(Px(1), line_height), + ), + highlight, // TODO cursor should be a bold color, highlight probably not. This should have its own color. + ), + Point::default(), + None, + None, + ); + } + } + + context.graphics.draw_text_buffer( buffer, styles.get_or_default(&TextColor), TextOrigin::TopLeft, @@ -115,18 +274,18 @@ impl Widget for Input { fn measure( &mut self, available_space: kludgine::figures::Size, - graphics: &mut crate::graphics::Graphics<'_, '_, '_>, - context: &mut crate::context::Context<'_, '_>, + context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>, ) -> kludgine::figures::Size { let styles = context.query_style(&[&TextColor]); - let editor = self.editor_mut(graphics.font_system(), &styles); + let editor = self.editor_mut(&mut context.graphics, &styles); let buffer = editor.buffer_mut(); buffer.set_size( - graphics.font_system(), + context.graphics.font_system(), available_space.width.max().into_float(), available_space.height.max().into_float(), ); - graphics + context + .graphics .measure_text_buffer::(buffer, Color::WHITE) .size .into_unsigned() @@ -134,22 +293,59 @@ impl Widget for Input { fn keyboard_input( &mut self, - device_id: kludgine::app::winit::event::DeviceId, + _device_id: kludgine::app::winit::event::DeviceId, input: kludgine::app::winit::event::KeyEvent, - is_synthetic: bool, - kludgine: &mut Kludgine, - context: &mut crate::context::Context<'_, '_>, + _is_synthetic: bool, + context: &mut EventContext<'_, '_>, ) -> EventHandling { if !input.state.is_pressed() { return UNHANDLED; } let styles = context.query_style(&[&TextColor]); - let editor = &mut self.editor.as_mut().expect("input without editor").editor; + let editor = self.editor_mut(context.kludgine, &styles); + println!( + "Keyboard input: {:?}. {:?}, {:?}", + input.logical_key, input.text, input.physical_key + ); match (input.logical_key, input.text) { - (Key::Backspace, _) => { - editor.action(kludgine.font_system(), Action::Backspace); + (key @ (Key::Backspace | Key::Delete), _) => { + editor.action( + context.kludgine.font_system(), + match key { + Key::Backspace => Action::Backspace, + Key::Delete => Action::Delete, + _ => unreachable!("previously matched"), + }, + ); + context.set_needs_redraw(); + HANDLED + } + (key @ (Key::ArrowLeft | Key::ArrowDown | Key::ArrowUp | Key::ArrowRight), _) => { + let modifiers = context.modifiers(); + match (editor.select_opt(), modifiers.state().shift_key()) { + (None, true) => { + editor.set_select_opt(Some(editor.cursor())); + } + (Some(_), false) => { + editor.set_select_opt(None); + } + _ => {} + }; + + editor.action( + context.kludgine.font_system(), + match key { + Key::ArrowLeft if modifiers.word_select() => Action::PreviousWord, + Key::ArrowLeft => Action::Left, + Key::ArrowDown => Action::Down, + Key::ArrowUp => Action::Up, + Key::ArrowRight if modifiers.word_select() => Action::NextWord, + Key::ArrowRight => Action::Right, + _ => unreachable!("previously matched"), + }, + ); context.set_needs_redraw(); HANDLED } @@ -162,11 +358,27 @@ impl Widget for Input { } } - fn focus(&mut self, context: &mut crate::context::Context<'_, '_>) { + fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { + match ime { + Ime::Enabled | Ime::Disabled => {} + Ime::Preedit(text, cursor) => { + println!("TODO: preview IME input {text}, cursor: {cursor:?}"); + } + Ime::Commit(text) => { + self.editor_mut(context.kludgine, &Self::styles(&context.widget)) + .insert_string(&text, None); + } + } + + HANDLED + } + + fn focus(&mut self, context: &mut EventContext<'_, '_>) { context.set_ime_allowed(true); } - fn blur(&mut self, context: &mut crate::context::Context<'_, '_>) { + fn blur(&mut self, context: &mut EventContext<'_, '_>) { + println!("Blur"); context.set_ime_allowed(false); } } @@ -175,3 +387,74 @@ struct LiveEditor { editor: Editor, generation: Option, } + +fn cursor_glyph(buffer: &Buffer, cursor: &Cursor) -> Result<(Point, Px), NotVisible> { + // let cursor = buffer.layout_cursor(cursor); + + let mut layout_cursor = buffer.layout_cursor(cursor); + // TODO this is because of a TODO inside of layout_cursor. It currently + // falls back to 0,0 on the current line, rather than picking the last one. + if layout_cursor.glyph == 0 && layout_cursor.layout == 0 && cursor.index > 0 { + layout_cursor.glyph = usize::MAX; + } + let mut return_after_character = false; + let searching_for = match buffer + .lines + .get(layout_cursor.line) + .and_then(|line| { + line.layout_opt() + .as_ref() + .expect("line layout missing") + .get(layout_cursor.layout) + }) + .and_then(|layout| layout.glyphs.get(layout_cursor.glyph)) + // TODO these should progressively fail rather than a single or_else. + .or_else(|| { + return_after_character = true; + buffer + .lines + .last() + .and_then(|line| { + line.layout_opt() + .as_ref() + .expect("line layout missing") + .last() + }) + .and_then(|layout| layout.glyphs.last()) + }) { + Some(glyph) => glyph, + None => return Err(NotVisible::Before), + } + .start; + + for (index, run) in buffer.layout_runs().enumerate() { + match run.line_i.cmp(&cursor.line) { + Ordering::Less => continue, + Ordering::Equal => {} + Ordering::Greater => { + if index > 0 { + return Err(NotVisible::After); + } + return Err(NotVisible::Before); + } + } + if let Some(glyph) = run.glyphs.iter().find(|g| g.start == searching_for) { + let physical = glyph.physical((0., run.line_y), 1.); + let position = Point::new(Px(physical.x), Px::from_float(run.line_top)); + let width = Px::from_float(glyph.w); + return Ok(if return_after_character { + (Point::new(position.x + width, position.y), Px(0)) + } else { + (position, width) + }); + } + } + + Err(NotVisible::After) +} + +#[derive(Debug, Eq, PartialEq)] +enum NotVisible { + Before, + After, +} diff --git a/src/widgets/label.rs b/src/widgets/label.rs index a3b64cf..3d47d14 100644 --- a/src/widgets/label.rs +++ b/src/widgets/label.rs @@ -3,8 +3,7 @@ use kludgine::figures::{Point, Size}; use kludgine::text::TextOrigin; use kludgine::Color; -use crate::context::Context; -use crate::graphics::Graphics; +use crate::context::GraphicsContext; use crate::styles::TextColor; use crate::widget::{IntoValue, Value, Widget}; @@ -22,15 +21,15 @@ impl Label { } impl Widget for Label { - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context<'_, '_>) { - let center = Point::from(graphics.size()) / 2; + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + let center = Point::from(context.graphics.size()) / 2; if let Value::Dynamic(contents) = &mut self.contents { context.redraw_when_changed(contents); } let styles = context.query_style(&[&TextColor]); - let width = graphics.size().width; + let width = context.graphics.size().width; self.contents.map(|contents| { - graphics.draw_text( + context.graphics.draw_text( contents, styles.get_or_default(&TextColor), TextOrigin::Center, @@ -45,12 +44,12 @@ impl Widget for Label { fn measure( &mut self, available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - _context: &mut Context<'_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size { let width = available_space.width.max().try_into().unwrap_or(Px::MAX); self.contents.map(|contents| { - graphics + context + .graphics .measure_text(contents, Color::RED, Some(width)) .size .try_cast() diff --git a/src/widgets/style.rs b/src/widgets/style.rs index efd18d4..f01367e 100644 --- a/src/widgets/style.rs +++ b/src/widgets/style.rs @@ -1,8 +1,7 @@ use kludgine::figures::units::UPx; use kludgine::figures::Size; -use crate::context::Context; -use crate::graphics::Graphics; +use crate::context::{AsEventContext, EventContext, GraphicsContext}; use crate::styles::Styles; use crate::widget::{BoxedWidget, ManagedWidget, Widget}; use crate::ConstraintLimit; @@ -28,12 +27,12 @@ impl Style { } impl Widget for Style { - fn mounted(&mut self, context: &mut Context<'_, '_>) { + fn mounted(&mut self, context: &mut EventContext<'_, '_>) { context.attach_styles(self.styles.clone()); self.mounted_child = Some(context.push_child(self.child.clone())); } - fn unmounted(&mut self, context: &mut Context<'_, '_>) { + fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { let child = self .mounted_child .take() @@ -41,21 +40,20 @@ impl Widget for Style { context.remove_child(&child); } - fn redraw(&mut self, graphics: &mut Graphics<'_, '_, '_>, context: &mut Context<'_, '_>) { + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { context .for_other( self.mounted_child .as_ref() .expect("measuring without being mounted"), ) - .redraw(graphics); + .redraw(); } fn measure( &mut self, available_space: Size, - graphics: &mut Graphics<'_, '_, '_>, - context: &mut Context<'_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_, '_>, ) -> Size { context .for_other( @@ -63,6 +61,6 @@ impl Widget for Style { .as_ref() .expect("measuring without being mounted"), ) - .measure(available_space, graphics) + .measure(available_space) } } diff --git a/src/window.rs b/src/window.rs index 6c718f0..22ead98 100644 --- a/src/window.rs +++ b/src/window.rs @@ -5,7 +5,7 @@ use std::panic::{AssertUnwindSafe, UnwindSafe}; use kludgine::app::winit::dpi::PhysicalPosition; use kludgine::app::winit::error::EventLoopError; use kludgine::app::winit::event::{ - DeviceId, ElementState, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase, + DeviceId, ElementState, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase, }; use kludgine::app::winit::keyboard::KeyCode; use kludgine::app::WindowBehavior as _; @@ -14,7 +14,7 @@ use kludgine::figures::Point; use kludgine::render::Drawing; use kludgine::Kludgine; -use crate::context::Context; +use crate::context::{EventContext, Exclusive, GraphicsContext, WidgetContext}; use crate::graphics::Graphics; use crate::styles::Styles; use crate::tree::Tree; @@ -162,8 +162,11 @@ where graphics.reset_text_attributes(); self.root.tree.reset_render_order(); let graphics = self.contents.new_frame(graphics); - let mut context = Context::new(&self.root, &mut window); - context.redraw(&mut Graphics::new(graphics)); + GraphicsContext { + widget: WidgetContext::new(&self.root, &mut window), + graphics: Exclusive::Owned(Graphics::new(graphics)), + } + .redraw(); } fn render<'pass>( @@ -187,7 +190,7 @@ where .expect("called more than once") } - fn close_requested(&mut self, mut window: RunningWindow<'_>) -> bool { + fn close_requested(&mut self, mut window: RunningWindow<'_>, _kludgine: &mut Kludgine) -> bool { self.request_close(&mut window) } @@ -224,17 +227,17 @@ where fn keyboard_input( &mut self, mut window: RunningWindow<'_>, + kludgine: &mut Kludgine, device_id: DeviceId, input: KeyEvent, is_synthetic: bool, - kludgine: &mut Kludgine, ) { - let target = self.root.tree.hovered_widget().unwrap_or(self.root.id); + let target = self.root.tree.focused_widget().unwrap_or(self.root.id); let target = self.root.tree.widget(target); - let mut target = Context::new(&target, &mut window); + let mut target = EventContext::new(WidgetContext::new(&target, &mut window), kludgine); let handled = recursively_handle_event(&mut target, |widget| { - widget.keyboard_input(device_id, input.clone(), is_synthetic, kludgine) + widget.keyboard_input(device_id, input.clone(), is_synthetic) }) .is_some(); drop(target); @@ -254,6 +257,7 @@ where fn mouse_wheel( &mut self, mut window: RunningWindow<'_>, + kludgine: &mut Kludgine, device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, @@ -261,7 +265,7 @@ where let widget = self.root.tree.hovered_widget().unwrap_or(self.root.id); let widget = self.root.tree.widget(widget); - let mut widget = Context::new(&widget, &mut window); + let mut widget = EventContext::new(WidgetContext::new(&widget, &mut window), kludgine); recursively_handle_event(&mut widget, |widget| { widget.mouse_wheel(device_id, delta, phase) }); @@ -269,13 +273,19 @@ where // fn modifiers_changed(&mut self, window: kludgine::app::Window<'_, ()>) {} - fn ime(&mut self, mut window: RunningWindow<'_>, ime: kludgine::app::winit::event::Ime) { - dbg!(ime); + fn ime(&mut self, mut window: RunningWindow<'_>, kludgine: &mut Kludgine, ime: Ime) { + let target = self.root.tree.focused_widget().unwrap_or(self.root.id); + let target = self.root.tree.widget(target); + let mut target = EventContext::new(WidgetContext::new(&target, &mut window), kludgine); + + let _handled = + recursively_handle_event(&mut target, |widget| widget.ime(ime.clone())).is_some(); } fn cursor_moved( &mut self, mut window: RunningWindow<'_>, + kludgine: &mut Kludgine, device_id: DeviceId, position: PhysicalPosition, ) { @@ -285,13 +295,15 @@ where if let Some(state) = self.mouse_state.devices.get(&device_id) { // Mouse Drag for (button, handler) in state { - let mut context = Context::new(handler, &mut window); + let mut context = + EventContext::new(WidgetContext::new(handler, &mut window), kludgine); let last_rendered_at = context.last_rendered_at().expect("passed hit test"); context.mouse_drag(location - last_rendered_at.origin, device_id, *button); } } else { // Hover - let mut context = Context::new(&self.root, &mut window); + let mut context = + EventContext::new(WidgetContext::new(&self.root, &mut window), kludgine); self.mouse_state.widget = None; for widget in self.root.tree.widgets_at_point(location) { let mut widget_context = context.for_other(&widget); @@ -315,9 +327,15 @@ where } } - fn cursor_left(&mut self, mut window: RunningWindow<'_>, _device_id: DeviceId) { + fn cursor_left( + &mut self, + mut window: RunningWindow<'_>, + kludgine: &mut Kludgine, + _device_id: DeviceId, + ) { if self.mouse_state.widget.take().is_some() { - let mut context = Context::new(&self.root, &mut window); + let mut context = + EventContext::new(WidgetContext::new(&self.root, &mut window), kludgine); context.clear_hover(); } } @@ -325,19 +343,20 @@ where fn mouse_input( &mut self, mut window: RunningWindow<'_>, + kludgine: &mut Kludgine, device_id: DeviceId, state: ElementState, button: MouseButton, ) { match state { ElementState::Pressed => { - Context::new(&self.root, &mut window).clear_focus(); + WidgetContext::new(&self.root, &mut window).clear_focus(); if let (ElementState::Pressed, Some(location), Some(hovered)) = (state, &self.mouse_state.location, &self.mouse_state.widget) { if let Some(handler) = recursively_handle_event( - &mut Context::new(hovered, &mut window), + &mut EventContext::new(WidgetContext::new(hovered, &mut window), kludgine), |context| { let relative = *location - context.last_rendered_at().expect("passed hit test").origin; @@ -363,7 +382,8 @@ where self.mouse_state.devices.remove(&device_id); } - let mut context = Context::new(&handler, &mut window); + let mut context = + EventContext::new(WidgetContext::new(&handler, &mut window), kludgine); let relative = if let (Some(last_rendered), Some(location)) = (context.last_rendered_at(), self.mouse_state.location) @@ -378,7 +398,12 @@ where } } - fn event(&mut self, event: WindowCommand, mut window: RunningWindow<'_>) { + fn event( + &mut self, + mut window: RunningWindow<'_>, + _kludgine: &mut Kludgine, + event: WindowCommand, + ) { match event { WindowCommand::Redraw => { window.set_needs_redraw(); @@ -388,8 +413,8 @@ where } fn recursively_handle_event( - context: &mut Context<'_, '_>, - mut each_widget: impl FnMut(&mut Context<'_, '_>) -> EventHandling, + context: &mut EventContext<'_, '_>, + mut each_widget: impl FnMut(&mut EventContext<'_, '_>) -> EventHandling, ) -> Option { match each_widget(context) { HANDLED => Some(context.widget().clone()),