More progress on input/event handling

This commit is contained in:
Jonathan Johnson 2023-10-19 14:56:18 -07:00
parent ff9fa043c3
commit 983b6a97ad
No known key found for this signature in database
GPG key ID: A66D6A34D6620579
16 changed files with 957 additions and 388 deletions

197
Cargo.lock generated
View file

@ -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"

View file

@ -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,

View file

@ -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> {

View file

@ -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)
}

View file

@ -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<ManagedWidget> {
self.current_node.parent()
}
pub fn redraw_when_changed<T>(&self, value: &Dynamic<T>) {
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<ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
) -> Size<UPx> {
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<Px>) -> 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<Rect<Px>> {
self.current_node.last_rendered_at()
}
pub(crate) fn hover(&mut self, location: Point<Px>) {
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<T> Deref for Exclusive<'_, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
match self {
Exclusive::Borrowed(wrapped) => wrapped,
Exclusive::Owned(wrapped) => wrapped,
}
}
}
impl<T> 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<ConstraintLimit>) -> Size<UPx> {
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<UPx>) -> 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<ManagedWidget> {
self.current_node.parent()
}
pub fn redraw_when_changed<T>(&self, value: &Dynamic<T>) {
value.redraw_when_changed(self.window.handle());
}
#[must_use]
pub fn last_rendered_at(&self) -> Option<Rect<Px>> {
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
}

View file

@ -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<UPx>) -> 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,
}
}
}

View file

@ -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<Lp> 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<dyn AnyComponent>);
@ -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::<Global>("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::<Global>("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;

View file

@ -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<T> {

View file

@ -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::<BoxedWidget>::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<ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
context: &mut Context<'_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx>;
#[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<Px>, context: &mut Context<'_, '_>) -> bool {
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool {
false
}
#[allow(unused_variables)]
fn hover(&mut self, location: Point<Px>, context: &mut Context<'_, '_>) {}
fn hover(&mut self, location: Point<Px>, 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<Px>,
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<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut Context<'_, '_>,
context: &mut EventContext<'_, '_>,
) {
}
@ -90,7 +89,7 @@ pub trait Widget: Send + UnwindSafe + Debug + 'static {
location: Option<Point<Px>>,
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
}

View file

@ -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<ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
context: &mut Context<'_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
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)
})
}
}

View file

@ -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<Px>, _context: &mut Context<'_, '_>) -> bool {
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool {
true
}
@ -107,7 +103,7 @@ impl Widget for Button {
_location: Point<Px>,
_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<Px>,
_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<Point<Px>>,
_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<crate::ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
_context: &mut Context<'_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
self.label.map(|label| {
let measured = graphics.measure_text::<Px>(label, Color::WHITE, Some(width));
let measured = context
.graphics
.measure_text::<Px>(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<Px>, context: &mut Context<'_, '_>) {
fn hover(&mut self, _location: Point<Px>, 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();
}
}

View file

@ -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<F>(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<crate::ConstraintLimit>,
_graphics: &mut Graphics<'_, '_, '_>,
_context: &mut Context<'_, '_>,
_context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
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<F> 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);
}
}

View file

@ -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<Px>,
context: &mut crate::context::Context<'_, '_>,
) -> bool {
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool {
true
}
fn mouse_down(
&mut self,
location: Point<Px>,
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<Point<Px>>,
device_id: kludgine::app::winit::event::DeviceId,
button: kludgine::app::winit::event::MouseButton,
context: &mut crate::context::Context<'_, '_>,
location: Point<Px>,
_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<crate::ConstraintLimit>,
graphics: &mut crate::graphics::Graphics<'_, '_, '_>,
context: &mut crate::context::Context<'_, '_>,
context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>,
) -> kludgine::figures::Size<kludgine::figures::units::UPx> {
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::<Px>(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<usize>,
}
fn cursor_glyph(buffer: &Buffer, cursor: &Cursor) -> Result<(Point<Px>, 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,
}

View file

@ -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<crate::ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
_context: &mut Context<'_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
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()

View file

@ -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<ConstraintLimit>,
graphics: &mut Graphics<'_, '_, '_>,
context: &mut Context<'_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) -> Size<UPx> {
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)
}
}

View file

@ -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<f64>,
) {
@ -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<ManagedWidget> {
match each_widget(context) {
HANDLED => Some(context.widget().clone()),