Merge pull request #123 from khonsulabs/offscreen

Initial implementation of offscreen rendering
This commit is contained in:
Jonathan Johnson 2024-01-04 16:10:33 -08:00 committed by GitHub
commit 4dc5cb5cc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 3204 additions and 971 deletions

View file

@ -5,6 +5,7 @@ on: [push]
jobs: jobs:
test: test:
strategy: strategy:
fail-fast: false
matrix: matrix:
version: ["stable", "1.70.0"] version: ["stable", "1.70.0"]
os: ["ubuntu-latest", "windows-latest", "macos-latest"] os: ["ubuntu-latest", "windows-latest", "macos-latest"]
@ -16,10 +17,15 @@ jobs:
if: matrix.os == 'ubuntu-latest' if: matrix.os == 'ubuntu-latest'
run: | run: |
sudo apt-get update -y -qq sudo apt-get update -y -qq
sudo add-apt-repository ppa:oibaf/graphics-drivers -y
# vulkan sdk
wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add -
sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-jammy.list https://packages.lunarg.com/vulkan/lunarg-vulkan-jammy.list
# install dependencies
sudo apt-get update sudo apt-get update
sudo apt-get install -y \ sudo apt-get install -y \
libegl1-mesa libgl1-mesa-dri libxcb-xfixes0-dev mesa-vulkan-drivers libegl-mesa0 libgl1-mesa-dri libxcb-xfixes0-dev vulkan-sdk mesa-vulkan-drivers
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
@ -30,6 +36,13 @@ jobs:
run: | run: |
cargo clippy --all-features --all-targets cargo clippy --all-features --all-targets
- name: Run default features unit tests - name: Compile with all features
run: |
cargo build --all-features --all-targets
- name: Run all features unit tests
# for msrv, we only check build compatibility, as it's possible bugs are
# fixed purely by updating the rust version.
if: matrix.version == 'stable'
run: | run: |
cargo test --all-features --all-targets cargo test --all-features --all-targets

1
.gitignore vendored
View file

@ -1 +1,2 @@
target/ target/
examples/offscreen*.png

View file

@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Breaking Changes ### Breaking Changes
- All context types no longer accept a `'window` lifetime. For most end-user
code, it means removing one elided lifetime from these types:
- `WidgetContext`
- `EventContext`
- `LayoutContext`
- `GraphicsContext`
- `WidgetContext`'s `Deref` target is now `&mut dyn PlatformWindow`. This change
ensures all widgets utilize a shared interface between any host architecture.
- All `DeviceId` parameters have been changed to a `DeviceId` type provided by
Cushy. This allows for creating arbitrary input device IDs when creating an
integration with other frameworks or driving simulated input in a
`VirtualWindow`.
- `WidgetRef` is now a `struct` instead of an enum. This refactor changes the - `WidgetRef` is now a `struct` instead of an enum. This refactor changes the
mounted state to be stored in a `WindowLocal`, ensuring `WidgetRef`s work mounted state to be stored in a `WindowLocal`, ensuring `WidgetRef`s work
properly when used in a `WidgetInstance` shared between multiple windows. properly when used in a `WidgetInstance` shared between multiple windows.
@ -60,14 +72,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `WidgetCacheKey` now includes the `KludgineId` of the context it was created - `WidgetCacheKey` now includes the `KludgineId` of the context it was created
from. This ensures if a `WidgetInstance` moves or is shared between windows, from. This ensures if a `WidgetInstance` moves or is shared between windows,
the cache is invalidated. the cache is invalidated.
- All `Dynamic` mapping functions now utilize weak references, and clean up as - All `Dynamic` mapping functions now utilize weak references, and the
necessary if a value is not able to be upgraded. `CallbackHandle` now contains a strong reference to the originating dynamic.
This should have no visible impact on end-user code.
- `ForEach`/`MapEach`'s implementations for tuples are now defined using - `ForEach`/`MapEach`'s implementations for tuples are now defined using
`Source<T>` and `DynamicRead<T>`. This allows combinations of `Dynamic<T>`s `Source<T>` and `DynamicRead<T>`. This allows combinations of `Dynamic<T>`s
and `DynamicReader<T>`s to be used in for_each/map_each expressions. and `DynamicReader<T>`s to be used in for_each/map_each expressions.
### Added ### Added
- Cushy now supports being embedded in any wgpu application. Here are the API
highlights:
- `CushyWindow` is a type that contains the state of a standalone window. It
defines an API designed to enable full control with winit integration into
any wgpu application. This type's design is inspired by wpgu's
"Encapsulating Graphics Work" article. Each of its functions require being
passed a type that implements `PlatformWindowImplementation`, which exposes
all APIs Cushy needs to be fully functional.
- `VirtualWindow` is a type that makes it easy to render a Cushy interface in
any wgpu application where no winit integration is desired. It utilizes
`VirtualState` as its `PlatformWindowImplementation`. This type also exposes
a design inspired by wpgu's "Encapsulating Graphics Work" article.
- `WindowDynamicState` is a set of dynamics that can be updated through
external threads and tasks.
- is a new trait that allows
customizing the behavior that Cushy widgets need to be rendered.
- Cushy now supports easily rendering a virtual window: `VirtualRecorder`. This
type utilizes a `VirtualWindow` and provides easy access to captured images.
This type has the ability to capture animated PNGs as well as still images.
- `figures` is now directly re-exported at this crate's root. Kludgine still - `figures` is now directly re-exported at this crate's root. Kludgine still
also provides this export, so existing references through kludgine will also provides this export, so existing references through kludgine will
continue to work. This was added as an attempt to fix links on docs.rs (see continue to work. This was added as an attempt to fix links on docs.rs (see
@ -115,6 +148,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
clone `self` before calling the `into_` function. This has only been done in clone `self` before calling the `into_` function. This has only been done in
situations where it is known or likely that the clone being performed is situations where it is known or likely that the clone being performed is
cheap. cheap.
- `CallbackHandle` now has `weak()` and `forget_owners()`. These functions allow
a `CallbackHandle` to release its strong references to the `Dynamic` that the
callback is installed on. This enables forming weak callback graphs that clean
up independent of one another.
- `Source<T>::weak_clone` returns a `Dynamic<T>` with a clone of each value
stored in the original source. The returned dynamic holds no strong references
to the original source.
- `Point`, `Size`, and `Rect` now implement `LinearInterpolate`.
- `MakeWidget::build_virtual_window()` returns a builder for a `VirtualWindow`.
- `MakeWidget::build_recorder()` returns a builder for a `VirtualRecorder`.
- `Space::dynamic()` returns a space that dynamically colors itself using
component provided. This allows the spacer to use values from the theme at
runtime.
- `Space::primary()` returns a space that contains the primary color.
[99]: https://github.com/khonsulabs/cushy/issues/99 [99]: https://github.com/khonsulabs/cushy/issues/99
[120]: https://github.com/khonsulabs/cushy/issues/120 [120]: https://github.com/khonsulabs/cushy/issues/120

51
Cargo.lock generated
View file

@ -582,6 +582,7 @@ dependencies = [
"kempt", "kempt",
"kludgine", "kludgine",
"palette", "palette",
"png",
"pollster", "pollster",
"rand", "rand",
"tracing", "tracing",
@ -987,6 +988,13 @@ dependencies = [
"bitflags 2.4.1", "bitflags 2.4.1",
] ]
[[package]]
name = "guide-examples"
version = "0.0.0"
dependencies = [
"cushy",
]
[[package]] [[package]]
name = "half" name = "half"
version = "2.2.1" version = "2.2.1"
@ -1179,7 +1187,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]] [[package]]
name = "kludgine" name = "kludgine"
version = "0.7.0" version = "0.7.0"
source = "git+https://github.com/khonsulabs/kludgine#0bda2a6dc273aa49338f23ea5190aefdf037d740" source = "git+https://github.com/khonsulabs/kludgine#8017775228d22b5efce6d6b7a89e81dfc9b25961"
dependencies = [ dependencies = [
"ahash", "ahash",
"alot", "alot",
@ -1192,6 +1200,7 @@ dependencies = [
"intentional", "intentional",
"justjson", "justjson",
"lyon_tessellation", "lyon_tessellation",
"palette",
"pollster", "pollster",
"smallvec", "smallvec",
"unicode-bidi", "unicode-bidi",
@ -1827,9 +1836,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa"
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"syn", "syn",
@ -1858,9 +1867,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.71" version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -1891,9 +1900,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -2173,18 +2182,18 @@ checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.193" version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.193" version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2331,9 +2340,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.43" version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -2360,18 +2369,18 @@ dependencies = [
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.52" version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83a48fd946b02c0a526b2e9481c8e2a17755e47039164a86c4070446e3a4614d" checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [ dependencies = [
"thiserror-impl", "thiserror-impl",
] ]
[[package]] [[package]]
name = "thiserror-impl" name = "thiserror-impl"
version = "1.0.52" version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3" checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -3185,9 +3194,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]] [[package]]
name = "winit" name = "winit"
version = "0.29.7" version = "0.29.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd430cd4560ee9c48885a4ef473b609a56796e37b1e18222abee146143f7457" checksum = "0dc1a7ae1076890701c7dd71ea35b2aebaf9aeb7b8868ac2d33b1c7e8ef93c00"
dependencies = [ dependencies = [
"ahash", "ahash",
"android-activity", "android-activity",
@ -3233,9 +3242,9 @@ dependencies = [
[[package]] [[package]]
name = "winnow" name = "winnow"
version = "0.5.31" version = "0.5.32"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c" checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6"
dependencies = [ dependencies = [
"memchr", "memchr",
] ]

View file

@ -1,5 +1,5 @@
[workspace] [workspace]
members = ["cushy-macros"] members = ["cushy-macros", "guide/guide-examples"]
[package] [package]
name = "cushy" name = "cushy"
@ -39,6 +39,9 @@ cushy-macros = { version = "0.2.0", path = "cushy-macros" }
arboard = "3.2.1" arboard = "3.2.1"
zeroize = "1.6.1" zeroize = "1.6.1"
unicode-segmentation = "1.10.1" unicode-segmentation = "1.10.1"
pollster = "0.3.0"
png = "0.17.10"
image = { version = "0.24.7", features = ["png"] }
# [patch.crates-io] # [patch.crates-io]
@ -57,9 +60,7 @@ unicode-segmentation = "1.10.1"
opt-level = 2 opt-level = 2
[dev-dependencies] [dev-dependencies]
pollster = "0.3.0"
rand = "0.8.5" rand = "0.8.5"
image = { version = "0.24.7", features = ["png"] }
[profile.release] [profile.release]
# debug = true # debug = true

View file

@ -4,6 +4,7 @@
use cushy::value::{Destination, Dynamic, Source}; use cushy::value::{Destination, Dynamic, Source};
use cushy::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetTag, HANDLED}; use cushy::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetTag, HANDLED};
use cushy::widgets::Custom; use cushy::widgets::Custom;
use cushy::window::DeviceId;
use cushy::Run; use cushy::Run;
use figures::units::{Lp, UPx}; use figures::units::{Lp, UPx};
use figures::{ScreenScale, Size}; use figures::{ScreenScale, Size};
@ -90,14 +91,14 @@ impl Default for Toggle {
} }
impl Widget for Toggle { impl Widget for Toggle {
fn redraw(&mut self, context: &mut cushy::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut cushy::context::GraphicsContext<'_, '_, '_, '_>) {
context.fill(self.color.get_tracking_redraw(context)); context.fill(self.color.get_tracking_redraw(context));
} }
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<cushy::ConstraintLimit>, available_space: Size<cushy::ConstraintLimit>,
context: &mut cushy::context::LayoutContext<'_, '_, '_, '_, '_>, context: &mut cushy::context::LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
Size::new( Size::new(
available_space.width.min(), available_space.width.min(),
@ -108,7 +109,7 @@ impl Widget for Toggle {
fn hit_test( fn hit_test(
&mut self, &mut self,
_location: figures::Point<figures::units::Px>, _location: figures::Point<figures::units::Px>,
_context: &mut cushy::context::EventContext<'_, '_>, _context: &mut cushy::context::EventContext<'_>,
) -> bool { ) -> bool {
true true
} }
@ -116,9 +117,9 @@ impl Widget for Toggle {
fn mouse_down( fn mouse_down(
&mut self, &mut self,
_location: figures::Point<figures::units::Px>, _location: figures::Point<figures::units::Px>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
_context: &mut cushy::context::EventContext<'_, '_>, _context: &mut cushy::context::EventContext<'_>,
) -> cushy::widget::EventHandling { ) -> cushy::widget::EventHandling {
self.value.toggle(); self.value.toggle();

View file

@ -0,0 +1,36 @@
use std::time::Duration;
use cushy::animation::easings::EaseInOutSine;
use cushy::widget::MakeWidget;
use figures::units::Px;
use figures::{Point, Size};
fn ui() -> impl MakeWidget {
"Hello World".into_button().centered()
}
fn main() {
let mut recorder = ui()
.build_recorder()
.size(Size::new(320, 240))
.finish()
.unwrap();
let initial_point = Point::new(Px::new(140), Px::new(150));
recorder.set_cursor_position(initial_point);
recorder.set_cursor_visible(true);
recorder.refresh().unwrap();
let mut animation = recorder.record_animated_png(60);
animation
.animate_cursor_to(
Point::new(Px::new(160), Px::new(120)),
Duration::from_millis(250),
EaseInOutSine,
)
.unwrap();
animation.wait_for(Duration::from_millis(500)).unwrap();
animation
.animate_cursor_to(initial_point, Duration::from_millis(250), EaseInOutSine)
.unwrap();
animation.wait_for(Duration::from_millis(500)).unwrap();
animation.write_to("examples/offscreen-apng.png").unwrap();
}

30
examples/offscreen.rs Normal file
View file

@ -0,0 +1,30 @@
use cushy::widget::MakeWidget;
use figures::Size;
fn ui() -> impl MakeWidget {
"Hello World".into_button().centered()
}
fn main() {
// The default recorder generated solid, rgb images.
let recorder = ui()
.build_recorder()
.size(Size::new(320, 240))
.finish()
.unwrap();
recorder.image().save("examples/offscreen.png").unwrap();
// Creating a recorder with alpha makes the virtual window transparent.
let recorder = ui()
.build_recorder()
.with_alpha()
.size(Size::new(320, 240))
.finish()
.unwrap();
recorder.image().save("examples/offscreen.png").unwrap();
}
#[test]
fn runs() {
main();
}

View file

@ -213,28 +213,26 @@ fn optional_editor(label: &str, color: &Dynamic<ColorSource>) -> (Dynamic<bool>,
} }
fn color_editor(color: &Dynamic<ColorSource>) -> impl MakeWidget { fn color_editor(color: &Dynamic<ColorSource>) -> impl MakeWidget {
let hue = color.map_each(|color| color.hue.into_positive_degrees()); let hue = color.map_each_cloned(|color| color.hue.into_positive_degrees());
hue.for_each_cloned({ hue.for_each_cloned({
let color = color.clone(); let color = color.clone();
move |hue| { move |hue| {
if let Ok(mut source) = color.try_get() { let mut source = color.get();
source.hue = OklabHue::new(hue); source.hue = OklabHue::new(hue);
color.set(source); color.set(source);
}
} }
}) })
.persist(); .persist();
let hue_text = hue.linked_string(); let hue_text = hue.linked_string();
let saturation = color.map_each(|color| color.saturation); let saturation = color.map_each_cloned(|color| color.saturation);
saturation saturation
.for_each_cloned({ .for_each_cloned({
let color = color.clone(); let color = color.clone();
move |saturation| { move |saturation| {
if let Ok(mut source) = color.try_get() { let mut source = color.get();
source.saturation = saturation; source.saturation = saturation;
color.set(source); color.set(source);
}
} }
}) })
.persist(); .persist();

1
guide/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
book

6
guide/book.toml Normal file
View file

@ -0,0 +1,6 @@
[book]
authors = ["Jonathan Johnson"]
language = "en"
multilingual = false
src = "src"
title = "Cushy User's Guide"

View file

@ -0,0 +1,8 @@
[package]
name = "guide-examples"
version = "0.0.0"
edition = "2021"
publish = false
[dependencies]
cushy = { version = "0.2.0", path = "../../" }

View file

@ -0,0 +1,137 @@
use cushy::figures::units::{Lp, Px};
use cushy::figures::{Point, Size};
use cushy::styles::{Edges, ThemePair};
use cushy::widget::MakeWidget;
use cushy::widgets::Space;
use guide_examples::BookExample;
fn content() -> impl MakeWidget {
Space::primary().size(Size::squared(Px::new(32)))
}
fn main() {
BookExample::new(
"align-horizontal",
"Default Behavior"
.and(content())
.and("align_left()")
.and({
// ANCHOR: align-left
content().align_left()
// ANCHOR_END: align-left
})
.and("pad_by().align_left()")
.and({
// ANCHOR: align-left-pad
content()
.pad_by(Edges::default().with_left(Lp::inches(1)))
.align_left()
// ANCHOR_END: align-left-pad
})
.and("centered()")
.and({
// ANCHOR: centered
content().centered()
// ANCHOR_END: centered
})
.and("pad_by().align_right()")
.and({
// ANCHOR: align-right-pad
content()
.pad_by(Edges::default().with_right(Lp::inches(1)))
.align_right()
// ANCHOR_END: align-right-pad
})
.and("align_right()")
.and({
// ANCHOR: align-right
content().align_right()
// ANCHOR_END: align-right
})
.into_rows(),
)
.still_frame(|recorder| {
const LEFT: u32 = 40;
const PADDING: u32 = 96;
const RIGHT: u32 = 710;
const CENTER: u32 = 375;
let container_color = ThemePair::default().dark.surface.lowest_container;
let primary = ThemePair::default().dark.primary.color;
recorder.assert_pixel_color(Point::new(LEFT, 35), container_color, "surface");
// Default fills the entire space
recorder.assert_pixel_color(Point::new(LEFT, 70), primary, "default spacer");
recorder.assert_pixel_color(Point::new(CENTER, 70), primary, "default spacer");
recorder.assert_pixel_color(Point::new(RIGHT, 70), primary, "default spacer");
// align-left
recorder.assert_pixel_color(Point::new(LEFT, 140), primary, "align-left spacer");
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 140),
container_color,
"align-left empty",
);
// align-left-pad
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 215),
primary,
"align-left-pad spacer",
);
recorder.assert_pixel_color(
Point::new(LEFT, 215),
container_color,
"align-left-pad empty before",
);
recorder.assert_pixel_color(
Point::new(CENTER, 215),
container_color,
"align-left-pad empty after",
);
// centered
recorder.assert_pixel_color(Point::new(CENTER, 295), primary, "centered spacer");
recorder.assert_pixel_color(
Point::new(LEFT + PADDING, 295),
container_color,
"centered empty before",
);
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 295),
container_color,
"centered empty after",
);
// align-right-pad
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 360),
primary,
"align-right-pad spacer",
);
recorder.assert_pixel_color(
Point::new(CENTER, 360),
container_color,
"align-right-pad empty before",
);
recorder.assert_pixel_color(
Point::new(RIGHT, 360),
container_color,
"align-right-pad empty after",
);
// align-right
recorder.assert_pixel_color(Point::new(RIGHT, 435), primary, "align-right spacer");
recorder.assert_pixel_color(
Point::new(RIGHT - PADDING, 435),
container_color,
"align-right empty",
);
});
}
#[test]
fn runs() {
main();
}

View file

@ -0,0 +1,69 @@
use std::panic::AssertUnwindSafe;
use std::path::PathBuf;
use cushy::figures::units::Px;
use cushy::figures::Size;
use cushy::widget::MakeWidget;
use cushy::widgets::container::ContainerShadow;
use cushy::window::{Rgba8, VirtualRecorder, VirtualRecorderBuilder};
pub struct BookExample {
name: &'static str,
recorder: VirtualRecorderBuilder<Rgba8>,
}
fn target_dir() -> PathBuf {
let target_dir = std::env::current_dir()
.expect("missing current dir")
.parent()
.expect("missing guide folder")
.join("src")
.join("examples");
assert!(
target_dir.is_dir(),
"current directory is not guide-examples"
);
target_dir
}
impl BookExample {
pub fn new(name: &'static str, interface: impl MakeWidget) -> Self {
Self {
name,
recorder: interface
.contain()
.shadow(ContainerShadow::drop(Px::new(16), Px::new(32)))
.width(Px::new(750))
.build_recorder()
.with_alpha()
.resize_to_fit()
.size(Size::new(750, 432)),
}
}
pub fn still_frame<Test>(self, test: Test)
where
Test: FnOnce(&mut VirtualRecorder<Rgba8>),
{
let mut recorder = self.recorder.finish().unwrap();
let capture = std::env::var("CAPTURE").is_ok();
let errored = std::panic::catch_unwind(AssertUnwindSafe(|| test(&mut recorder))).is_err();
if errored || capture {
let path = target_dir().join(format!("{}.png", self.name));
recorder.image().save(&path).expect("error saving file");
println!("Wrote {}", path.display());
if errored {
std::process::exit(-1);
}
}
}
// pub fn animated<Test>(self, test: Test)
// where
// Test: FnOnce(&mut AnimationRecorder<'_, Rgb8>),
// {
// }
}

3
guide/src/SUMMARY.md Normal file
View file

@ -0,0 +1,3 @@
# Summary
- [Chapter 1](./chapter_1.md)

33
guide/src/chapter_1.md Normal file
View file

@ -0,0 +1,33 @@
# Aligning Widgets
![align.rs - horizontal-align](/examples/align-horizontal.png)
## Align a widget to the left
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-left}}
```
## Align a widget to the left, with padding
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-left-pad}}
```
## Align a widget to the center
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:centered}}
```
## Align a widget to the right, with padding
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-right-pad}}
```
## Align a widget to the right
```rust,no_run,no_playground
{{#include ../guide-examples/examples/align.rs:align-right}}
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View file

@ -49,7 +49,7 @@ use std::time::{Duration, Instant};
use alot::{LotId, Lots}; use alot::{LotId, Lots};
use figures::units::{Lp, Px, UPx}; use figures::units::{Lp, Px, UPx};
use figures::{Angle, Ranged, UnscaledUnit, Zero}; use figures::{Angle, Point, Ranged, Rect, Size, UnscaledUnit, Zero};
use intentional::Cast; use intentional::Cast;
use kempt::Set; use kempt::Set;
use kludgine::Color; use kludgine::Color;
@ -929,6 +929,42 @@ impl_unscaled_lerp!(Px);
impl_unscaled_lerp!(Lp); impl_unscaled_lerp!(Lp);
impl_unscaled_lerp!(UPx); impl_unscaled_lerp!(UPx);
impl<Unit> LinearInterpolate for Point<Unit>
where
Unit: LinearInterpolate,
{
fn lerp(&self, target: &Self, percent: f32) -> Self {
Self::new(
self.x.lerp(&target.x, percent),
self.y.lerp(&target.y, percent),
)
}
}
impl<Unit> LinearInterpolate for Size<Unit>
where
Unit: LinearInterpolate,
{
fn lerp(&self, target: &Self, percent: f32) -> Self {
Self::new(
self.width.lerp(&target.width, percent),
self.height.lerp(&target.height, percent),
)
}
}
impl<Unit> LinearInterpolate for Rect<Unit>
where
Unit: LinearInterpolate,
{
fn lerp(&self, target: &Self, percent: f32) -> Self {
Self::new(
self.origin.lerp(&target.origin, percent),
self.size.lerp(&target.size, percent),
)
}
}
#[test] #[test]
fn integer_lerps() { fn integer_lerps() {
#[track_caller] #[track_caller]

View file

@ -30,11 +30,7 @@ impl Default for PendingApp {
fn default() -> Self { fn default() -> Self {
Self { Self {
app: kludgine::app::PendingApp::default(), app: kludgine::app::PendingApp::default(),
cushy: Cushy { cushy: Cushy::new(),
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
},
} }
} }
} }
@ -52,6 +48,14 @@ pub struct Cushy {
} }
impl Cushy { impl Cushy {
pub(crate) fn new() -> Self {
Self {
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
}
}
/// Returns a locked mutex guard to the OS's clipboard, if one was able to be /// Returns a locked mutex guard to the OS's clipboard, if one was able to be
/// initialized when the window opened. /// initialized when the window opened.
#[must_use] #[must_use]
@ -77,7 +81,7 @@ impl Application for PendingApp {
fn as_app(&self) -> App { fn as_app(&self) -> App {
App { App {
app: self.app.as_app(), app: Some(self.app.as_app()),
cushy: self.cushy.clone(), cushy: self.cushy.clone(),
} }
} }
@ -86,7 +90,7 @@ impl Application for PendingApp {
/// A handle to a Cushy application. /// A handle to a Cushy application.
#[derive(Clone)] #[derive(Clone)]
pub struct App { pub struct App {
app: kludgine::app::App<WindowCommand>, app: Option<kludgine::app::App<WindowCommand>>,
cushy: Cushy, cushy: Cushy,
} }
@ -102,7 +106,10 @@ impl Application for App {
impl AsApplication<AppEvent<WindowCommand>> for App { impl AsApplication<AppEvent<WindowCommand>> for App {
fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> { fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> {
self.app.as_application() self.app
.as_ref()
.map(AsApplication::as_application)
.expect("no app")
} }
} }

View file

@ -4,9 +4,7 @@ use std::ops::{Deref, DerefMut};
use figures::units::{Lp, Px, UPx}; use figures::units::{Lp, Px, UPx};
use figures::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size, Zero}; use figures::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size, Zero};
use kludgine::app::winit::event::{ use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine, KludgineId}; use kludgine::{Color, Kludgine, KludgineId};
@ -21,16 +19,16 @@ use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::tree::Tree; use crate::tree::Tree;
use crate::value::{IntoValue, Source, Value}; use crate::value::{IntoValue, Source, Value};
use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance}; use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance};
use crate::window::{CursorState, RunningWindow, ThemeMode}; use crate::window::{CursorState, DeviceId, PlatformWindow, ThemeMode};
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A context to an event function. /// A context to an event function.
/// ///
/// This type is a combination of a reference to the rendering library, /// This type is a combination of a reference to the rendering library,
/// [`Kludgine`], and a [`WidgetContext`]. /// [`Kludgine`], and a [`WidgetContext`].
pub struct EventContext<'context, 'window> { pub struct EventContext<'context> {
/// The context for the widget receiving the event. /// The context for the widget receiving the event.
pub widget: WidgetContext<'context, 'window>, pub widget: WidgetContext<'context>,
/// The rendering library's state. /// The rendering library's state.
/// ///
/// This is useful for accessing the current [scale](Kludgine::scale) or /// This is useful for accessing the current [scale](Kludgine::scale) or
@ -38,13 +36,10 @@ pub struct EventContext<'context, 'window> {
pub kludgine: &'context mut Kludgine, pub kludgine: &'context mut Kludgine,
} }
impl<'context, 'window> EventContext<'context, 'window> { impl<'context> EventContext<'context> {
const MAX_PENDING_CHANGE_CYCLES: u8 = 100; const MAX_PENDING_CHANGE_CYCLES: u8 = 100;
pub(crate) fn new( pub(crate) fn new(widget: WidgetContext<'context>, kludgine: &'context mut Kludgine) -> Self {
widget: WidgetContext<'context, 'window>,
kludgine: &'context mut Kludgine,
) -> Self {
Self { widget, kludgine } Self { widget, kludgine }
} }
@ -58,10 +53,10 @@ impl<'context, 'window> EventContext<'context, 'window> {
pub fn for_other<'child, Widget>( pub fn for_other<'child, Widget>(
&'child mut self, &'child mut self,
widget: &Widget, widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<EventContext<'child, 'window>>>::Result ) -> <Widget::Managed as MapManagedWidget<EventContext<'child>>>::Result
where where
Widget: ManageWidget, Widget: ManageWidget,
Widget::Managed: MapManagedWidget<EventContext<'child, 'window>>, Widget::Managed: MapManagedWidget<EventContext<'child>>,
{ {
widget widget
.manage(self) .manage(self)
@ -177,7 +172,8 @@ impl<'context, 'window> EventContext<'context, 'window> {
cursor = widget_cursor; cursor = widget_cursor;
} }
} }
self.winit().set_cursor_icon(cursor.unwrap_or_default()); self.window_mut()
.set_cursor_icon(cursor.unwrap_or_default());
} }
pub(crate) fn clear_hover(&mut self) { pub(crate) fn clear_hover(&mut self) {
@ -189,7 +185,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
old_hover.lock().as_widget().unhover(&mut old_hover_context); old_hover.lock().as_widget().unhover(&mut old_hover_context);
} }
self.winit().set_cursor_icon(CursorIcon::Default); self.window_mut().set_cursor_icon(CursorIcon::Default);
} }
fn apply_pending_activation(&mut self) { fn apply_pending_activation(&mut self) {
@ -480,15 +476,15 @@ impl<'context, 'window> EventContext<'context, 'window> {
} }
} }
impl<'context, 'window> Deref for EventContext<'context, 'window> { impl<'context> Deref for EventContext<'context> {
type Target = WidgetContext<'context, 'window>; type Target = WidgetContext<'context>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.widget &self.widget
} }
} }
impl<'context, 'window> DerefMut for EventContext<'context, 'window> { impl<'context> DerefMut for EventContext<'context> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.widget &mut self.widget
} }
@ -523,18 +519,18 @@ impl<T> DerefMut for Exclusive<'_, T> {
} }
/// A context to a function that is rendering a widget. /// A context to a function that is rendering a widget.
pub struct GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> { pub struct GraphicsContext<'context, 'clip, 'gfx, 'pass> {
/// The context of the widget being rendered. /// The context of the widget being rendered.
pub widget: WidgetContext<'context, 'window>, pub widget: WidgetContext<'context>,
/// The graphics context clipped and offset to the area of the widget being /// The graphics context clipped and offset to the area of the widget being
/// rendered. Drawing at 0,0 will draw at the top-left pixel of the laid-out /// rendered. Drawing at 0,0 will draw at the top-left pixel of the laid-out
/// widget region. /// widget region.
pub gfx: Exclusive<'context, Graphics<'clip, 'gfx, 'pass>>, pub gfx: Exclusive<'context, Graphics<'clip, 'gfx, 'pass>>,
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> { impl<'context, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'clip, 'gfx, 'pass> {
/// Returns a new instance that borrows from `self`. /// Returns a new instance that borrows from `self`.
pub fn borrowed(&mut self) -> GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass> { pub fn borrowed(&mut self) -> GraphicsContext<'_, 'clip, 'gfx, 'pass> {
GraphicsContext { GraphicsContext {
widget: self.widget.borrowed(), widget: self.widget.borrowed(),
gfx: Exclusive::Borrowed(&mut self.gfx), gfx: Exclusive::Borrowed(&mut self.gfx),
@ -546,12 +542,10 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
pub fn for_other<'child, Widget>( pub fn for_other<'child, Widget>(
&'child mut self, &'child mut self,
widget: &Widget, widget: &Widget,
) -> <Widget::Managed as MapManagedWidget< ) -> <Widget::Managed as MapManagedWidget<GraphicsContext<'child, 'child, 'gfx, 'pass>>>::Result
GraphicsContext<'child, 'window, 'child, 'gfx, 'pass>,
>>::Result
where where
Widget: ManageWidget, Widget: ManageWidget,
Widget::Managed: MapManagedWidget<GraphicsContext<'child, 'window, 'child, 'gfx, 'pass>>, Widget::Managed: MapManagedWidget<GraphicsContext<'child, 'child, 'gfx, 'pass>>,
{ {
let opacity = self.get(&Opacity); let opacity = self.get(&Opacity);
widget.manage(self).map(|widget| { widget.manage(self).map(|widget| {
@ -577,7 +571,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
} }
/// Returns a new graphics context that renders to the `clip` rectangle. /// Returns a new graphics context that renders to the `clip` rectangle.
pub fn clipped_to(&mut self, clip: Rect<Px>) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> { pub fn clipped_to(&mut self, clip: Rect<Px>) -> GraphicsContext<'_, '_, 'gfx, 'pass> {
GraphicsContext { GraphicsContext {
widget: self.widget.borrowed(), widget: self.widget.borrowed(),
gfx: Exclusive::Owned(self.gfx.clipped_to(clip)), gfx: Exclusive::Owned(self.gfx.clipped_to(clip)),
@ -675,7 +669,7 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, '
} }
} }
impl Drop for GraphicsContext<'_, '_, '_, '_, '_> { impl Drop for GraphicsContext<'_, '_, '_, '_> {
fn drop(&mut self) { fn drop(&mut self) {
if matches!(self.widget.pending_state, PendingState::Owned(_)) { if matches!(self.widget.pending_state, PendingState::Owned(_)) {
self.as_event_context().apply_pending_state(); self.as_event_context().apply_pending_state();
@ -683,36 +677,30 @@ impl Drop for GraphicsContext<'_, '_, '_, '_, '_> {
} }
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref impl<'context, 'clip, 'gfx, 'pass> Deref for GraphicsContext<'context, 'clip, 'gfx, 'pass> {
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass> type Target = WidgetContext<'context>;
{
type Target = WidgetContext<'context, 'window>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.widget &self.widget
} }
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut impl<'context, 'clip, 'gfx, 'pass> DerefMut for GraphicsContext<'context, 'clip, 'gfx, 'pass> {
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>
{
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.widget &mut self.widget
} }
} }
/// A context to a function that is rendering a widget. /// A context to a function that is rendering a widget.
pub struct LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> { pub struct LayoutContext<'context, 'clip, 'gfx, 'pass> {
/// The graphics context that this layout operation is being performed /// The graphics context that this layout operation is being performed
/// within. /// within.
pub graphics: GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, pub graphics: GraphicsContext<'context, 'clip, 'gfx, 'pass>,
persist_layout: bool, persist_layout: bool,
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> { impl<'context, 'clip, 'gfx, 'pass> LayoutContext<'context, 'clip, 'gfx, 'pass> {
pub(crate) fn new( pub(crate) fn new(graphics: &'context mut GraphicsContext<'_, 'clip, 'gfx, 'pass>) -> Self {
graphics: &'context mut GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass>,
) -> Self {
Self { Self {
graphics: graphics.borrowed(), graphics: graphics.borrowed(),
persist_layout: true, persist_layout: true,
@ -736,10 +724,10 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl
pub fn for_other<'child, Widget>( pub fn for_other<'child, Widget>(
&'child mut self, &'child mut self,
widget: &Widget, widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<LayoutContext<'child, 'window, 'child, 'gfx, 'pass>>>::Result ) -> <Widget::Managed as MapManagedWidget<LayoutContext<'child, 'child, 'gfx, 'pass>>>::Result
where where
Widget: ManageWidget, Widget: ManageWidget,
Widget::Managed: MapManagedWidget<LayoutContext<'child, 'window, 'child, 'gfx, 'pass>>, Widget::Managed: MapManagedWidget<LayoutContext<'child, 'child, 'gfx, 'pass>>,
{ {
widget.manage(self).map(|widget| LayoutContext { widget.manage(self).map(|widget| LayoutContext {
graphics: self.graphics.for_other(&widget), graphics: self.graphics.for_other(&widget),
@ -782,36 +770,30 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl
} }
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> AsEventContext<'window> impl<'context, 'clip, 'gfx, 'pass> AsEventContext for LayoutContext<'context, 'clip, 'gfx, 'pass> {
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> fn as_event_context(&mut self) -> EventContext<'_> {
{
fn as_event_context(&mut self) -> EventContext<'_, 'window> {
self.graphics.as_event_context() self.graphics.as_event_context()
} }
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref impl<'context, 'clip, 'gfx, 'pass> Deref for LayoutContext<'context, 'clip, 'gfx, 'pass> {
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> type Target = GraphicsContext<'context, 'clip, 'gfx, 'pass>;
{
type Target = GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
&self.graphics &self.graphics
} }
} }
impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut impl<'context, 'clip, 'gfx, 'pass> DerefMut for LayoutContext<'context, 'clip, 'gfx, 'pass> {
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>
{
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.graphics &mut self.graphics
} }
} }
/// Converts from one context to an [`EventContext`]. /// Converts from one context to an [`EventContext`].
pub trait AsEventContext<'window> { pub trait AsEventContext {
/// Returns this context as an [`EventContext`]. /// Returns this context as an [`EventContext`].
fn as_event_context(&mut self) -> EventContext<'_, 'window>; fn as_event_context(&mut self) -> EventContext<'_>;
/// Pushes a new child widget into the widget hierarchy beneathq the /// Pushes a new child widget into the widget hierarchy beneathq the
/// context's widget. /// context's widget.
@ -837,14 +819,14 @@ pub trait AsEventContext<'window> {
} }
} }
impl<'window> AsEventContext<'window> for EventContext<'_, 'window> { impl AsEventContext for EventContext<'_> {
fn as_event_context(&mut self) -> EventContext<'_, 'window> { fn as_event_context(&mut self) -> EventContext<'_> {
EventContext::new(self.widget.borrowed(), self.kludgine) EventContext::new(self.widget.borrowed(), self.kludgine)
} }
} }
impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> { impl AsEventContext for GraphicsContext<'_, '_, '_, '_> {
fn as_event_context(&mut self) -> EventContext<'_, 'window> { fn as_event_context(&mut self) -> EventContext<'_> {
EventContext::new(self.widget.borrowed(), &mut self.gfx) EventContext::new(self.widget.borrowed(), &mut self.gfx)
} }
} }
@ -853,10 +835,10 @@ impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '
/// ///
/// This type provides access to the widget hierarchy from the perspective of a /// This type provides access to the widget hierarchy from the perspective of a
/// specific widget. /// specific widget.
pub struct WidgetContext<'context, 'window> { pub struct WidgetContext<'context> {
current_node: MountedWidget, current_node: MountedWidget,
pub(crate) tree: Tree, pub(crate) tree: Tree,
window: &'context mut RunningWindow<'window>, window: &'context mut dyn PlatformWindow,
theme: Cow<'context, ThemePair>, theme: Cow<'context, ThemePair>,
cursor: &'context mut CursorState, cursor: &'context mut CursorState,
pending_state: PendingState<'context>, pending_state: PendingState<'context>,
@ -864,11 +846,11 @@ pub struct WidgetContext<'context, 'window> {
cache: WidgetCacheKey, cache: WidgetCacheKey,
} }
impl<'context, 'window> WidgetContext<'context, 'window> { impl<'context> WidgetContext<'context> {
pub(crate) fn new( pub(crate) fn new(
current_node: MountedWidget, current_node: MountedWidget,
theme: &'context ThemePair, theme: &'context ThemePair,
window: &'context mut RunningWindow<'window>, window: &'context mut dyn PlatformWindow,
theme_mode: ThemeMode, theme_mode: ThemeMode,
cursor: &'context mut CursorState, cursor: &'context mut CursorState,
) -> Self { ) -> Self {
@ -899,7 +881,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
} }
/// Returns a new instance that borrows from `self`. /// Returns a new instance that borrows from `self`.
pub fn borrowed(&mut self) -> WidgetContext<'_, 'window> { pub fn borrowed(&mut self) -> WidgetContext<'_> {
WidgetContext { WidgetContext {
tree: self.tree.clone(), tree: self.tree.clone(),
current_node: self.current_node.clone(), current_node: self.current_node.clone(),
@ -916,10 +898,10 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
pub fn for_other<'child, Widget>( pub fn for_other<'child, Widget>(
&'child mut self, &'child mut self,
widget: &Widget, widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<WidgetContext<'child, 'window>>>::Result ) -> <Widget::Managed as MapManagedWidget<WidgetContext<'child>>>::Result
where where
Widget: ManageWidget, Widget: ManageWidget,
Widget::Managed: MapManagedWidget<WidgetContext<'child, 'window>>, Widget::Managed: MapManagedWidget<WidgetContext<'child>>,
{ {
widget.manage(self).map(|current_node| { widget.manage(self).map(|current_node| {
let (effective_styles, theme, theme_mode) = current_node.overidden_theme(); let (effective_styles, theme, theme_mode) = current_node.overidden_theme();
@ -1159,13 +1141,13 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
/// Returns the window containing this widget. /// Returns the window containing this widget.
#[must_use] #[must_use]
pub fn window(&self) -> &RunningWindow<'window> { pub fn window(&self) -> &dyn PlatformWindow {
self.window self.window
} }
/// Returns an exclusive reference to the window containing this widget. /// Returns an exclusive reference to the window containing this widget.
#[must_use] #[must_use]
pub fn window_mut(&mut self) -> &mut RunningWindow<'window> { pub fn window_mut(&mut self) -> &mut dyn PlatformWindow {
self.window self.window
} }
@ -1201,9 +1183,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
} }
} }
impl dyn AsEventContext<'_> {} impl Drop for EventContext<'_> {
impl Drop for EventContext<'_, '_> {
fn drop(&mut self) { fn drop(&mut self) {
if matches!(self.widget.pending_state, PendingState::Owned(_)) { if matches!(self.widget.pending_state, PendingState::Owned(_)) {
self.apply_pending_state(); self.apply_pending_state();
@ -1211,17 +1191,17 @@ impl Drop for EventContext<'_, '_> {
} }
} }
impl<'window> Deref for WidgetContext<'_, 'window> { impl<'context> Deref for WidgetContext<'context> {
type Target = RunningWindow<'window>; type Target = &'context mut dyn PlatformWindow;
fn deref(&self) -> &Self::Target { fn deref(&self) -> &Self::Target {
self.window &self.window
} }
} }
impl<'window> DerefMut for WidgetContext<'_, 'window> { impl DerefMut for WidgetContext<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { fn deref_mut(&mut self) -> &mut Self::Target {
self.window &mut self.window
} }
} }
@ -1270,13 +1250,13 @@ pub trait ManageWidget {
type Managed: MapManagedWidget<MountedWidget>; type Managed: MapManagedWidget<MountedWidget>;
/// Resolve `self` into a [`MountedWidget`]. /// Resolve `self` into a [`MountedWidget`].
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed; fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed;
} }
impl ManageWidget for WidgetId { impl ManageWidget for WidgetId {
type Managed = Option<MountedWidget>; type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
context.tree.widget(*self) context.tree.widget(*self)
} }
} }
@ -1284,7 +1264,7 @@ impl ManageWidget for WidgetId {
impl ManageWidget for WidgetInstance { impl ManageWidget for WidgetInstance {
type Managed = Option<MountedWidget>; type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
context.tree.widget(self.id()) context.tree.widget(self.id())
} }
} }
@ -1292,7 +1272,7 @@ impl ManageWidget for WidgetInstance {
impl ManageWidget for MountedWidget { impl ManageWidget for MountedWidget {
type Managed = Self; type Managed = Self;
fn manage(&self, _context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, _context: &WidgetContext<'_>) -> Self::Managed {
self.clone() self.clone()
} }
} }
@ -1348,7 +1328,7 @@ pub trait Trackable: sealed::Trackable {
/// Marks the widget for redraw when this value is updated. /// Marks the widget for redraw when this value is updated.
/// ///
/// This function has no effect if the value is constant. /// This function has no effect if the value is constant.
fn redraw_when_changed(&self, context: &WidgetContext<'_, '_>) fn redraw_when_changed(&self, context: &WidgetContext<'_>)
where where
Self: Sized, Self: Sized,
{ {
@ -1358,7 +1338,7 @@ pub trait Trackable: sealed::Trackable {
/// Marks the widget for redraw when this value is updated. /// Marks the widget for redraw when this value is updated.
/// ///
/// This function has no effect if the value is constant. /// This function has no effect if the value is constant.
fn invalidate_when_changed(&self, context: &WidgetContext<'_, '_>) fn invalidate_when_changed(&self, context: &WidgetContext<'_>)
where where
Self: Sized, Self: Sized,
{ {

View file

@ -8,7 +8,7 @@ use crate::value::{Dynamic, DynamicReader, ForEach, Source, WeakDynamic};
use crate::widget::{Children, MakeWidget, WidgetInstance}; use crate::widget::{Children, MakeWidget, WidgetInstance};
use crate::widgets::grid::{Grid, GridWidgets}; use crate::widgets::grid::{Grid, GridWidgets};
use crate::window::Window; use crate::window::Window;
use crate::Open; use crate::{Open, PendingApp};
/// A widget that can provide extra information when debugging. /// A widget that can provide extra information when debugging.
#[derive(Clone, Default)] #[derive(Clone, Default)]
@ -53,7 +53,7 @@ impl DebugContext {
section.values.lock().push(Box::new(RegisteredValue { section.values.lock().push(Box::new(RegisteredValue {
label: label.into(), label: label.into(),
value: reader.clone(), value: reader.clone(),
widget: make_observer(value.clone()).make_widget(), widget: make_observer(value.weak_clone()).make_widget(),
})) }))
}); });
let this = self.clone(); let this = self.clone();
@ -122,7 +122,7 @@ impl Open for DebugContext {
self.into_window().open(app) self.into_window().open(app)
} }
fn run_in(self, app: crate::PendingApp) -> crate::Result { fn run_in(self, app: PendingApp) -> crate::Result {
self.into_window().run_in(app) self.into_window().run_in(app)
} }
} }

View file

@ -114,7 +114,7 @@ impl Styles {
&self, &self,
component: &impl NamedComponent, component: &impl NamedComponent,
fallback: &Fallback, fallback: &Fallback,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
) -> Fallback::ComponentType ) -> Fallback::ComponentType
where where
Fallback: ComponentDefinition + ?Sized, Fallback: ComponentDefinition + ?Sized,
@ -127,10 +127,7 @@ impl Styles {
.unwrap_or_else(|| fallback.default_value(context)) .unwrap_or_else(|| fallback.default_value(context))
} }
fn resolve_component<T>( fn resolve_component<T>(component: &Value<Component>, context: &WidgetContext<'_>) -> Option<T>
component: &Value<Component>,
context: &WidgetContext<'_, '_>,
) -> Option<T>
where where
T: ComponentType, T: ComponentType,
{ {
@ -162,7 +159,7 @@ impl Styles {
pub fn try_get<Named>( pub fn try_get<Named>(
&self, &self,
component: &Named, component: &Named,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
) -> Option<Named::ComponentType> ) -> Option<Named::ComponentType>
where where
Named: ComponentDefinition + ?Sized, Named: ComponentDefinition + ?Sized,
@ -176,11 +173,7 @@ impl Styles {
/// Returns the component associated with the given name, or if not found, /// Returns the component associated with the given name, or if not found,
/// returns the default value provided by the definition. /// returns the default value provided by the definition.
#[must_use] #[must_use]
pub fn get<Named>( pub fn get<Named>(&self, component: &Named, context: &WidgetContext<'_>) -> Named::ComponentType
&self,
component: &Named,
context: &WidgetContext<'_, '_>,
) -> Named::ComponentType
where where
Named: ComponentDefinition + ?Sized, Named: ComponentDefinition + ?Sized,
{ {
@ -440,10 +433,8 @@ impl Component {
#[must_use] #[must_use]
pub fn dynamic<T, Func>(resolve: Func) -> Self pub fn dynamic<T, Func>(resolve: Func) -> Self
where where
Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<T> Func:
+ Send for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option<T> + Send + Sync + 'static,
+ Sync
+ 'static,
T: ComponentType, T: ComponentType,
{ {
Self::Dynamic(DynamicComponent::new(move |context| { Self::Dynamic(DynamicComponent::new(move |context| {
@ -1099,7 +1090,7 @@ pub trait ComponentDefinition: NamedComponent {
type ComponentType: ComponentType; type ComponentType: ComponentType;
/// Returns the default value to use for this component. /// Returns the default value to use for this component.
fn default_value(&self, context: &WidgetContext<'_, '_>) -> Self::ComponentType; fn default_value(&self, context: &WidgetContext<'_>) -> Self::ComponentType;
} }
/// Describes whether a type should invalidate a widget. /// Describes whether a type should invalidate a widget.
@ -1751,7 +1742,7 @@ impl FixedTheme {
/// ///
/// The goal of this type is to allow various tones of a given hue/saturation to /// The goal of this type is to allow various tones of a given hue/saturation to
/// be generated easily. /// be generated easily.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug)]
pub struct ColorSource { pub struct ColorSource {
/// A measurement of hue, in degees, from -180 to 180. /// A measurement of hue, in degees, from -180 to 180.
/// ///
@ -1769,6 +1760,13 @@ pub struct ColorSource {
pub saturation: ZeroToOne, pub saturation: ZeroToOne,
} }
impl PartialEq for ColorSource {
fn eq(&self, other: &Self) -> bool {
(self.hue.into_degrees() - other.hue.into_degrees()).abs() < f32::EPSILON
&& self.saturation == other.saturation
}
}
impl ColorSource { impl ColorSource {
/// Returns a new source with the given hue (in degrees) and saturation (0.0 /// Returns a new source with the given hue (in degrees) and saturation (0.0
/// - 1.0). /// - 1.0).
@ -2535,19 +2533,19 @@ impl PartialEq for DynamicComponent {
/// A type that resolves to a [`Component`] at runtime. /// A type that resolves to a [`Component`] at runtime.
pub trait DynamicComponentResolver: Send + Sync + 'static { pub trait DynamicComponentResolver: Send + Sync + 'static {
/// Returns the effective component, if one should be applied. /// Returns the effective component, if one should be applied.
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component>; fn resolve_component(&self, context: &WidgetContext<'_>) -> Option<Component>;
} }
struct DynamicFunctionWrapper<F>(F); struct DynamicFunctionWrapper<F>(F);
impl<T> DynamicComponentResolver for DynamicFunctionWrapper<T> impl<T> DynamicComponentResolver for DynamicFunctionWrapper<T>
where where
T: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<Component> T: for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option<Component>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
{ {
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component> { fn resolve_component(&self, context: &WidgetContext<'_>) -> Option<Component> {
self.0(context) self.0(context)
} }
} }
@ -2556,7 +2554,7 @@ impl<T> DynamicComponentResolver for T
where where
T: ComponentDefinition + Clone + Send + Sync + 'static, T: ComponentDefinition + Clone + Send + Sync + 'static,
{ {
fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option<Component> { fn resolve_component(&self, context: &WidgetContext<'_>) -> Option<Component> {
Some(context.get(self).into_component()) Some(context.get(self).into_component())
} }
} }
@ -2576,7 +2574,7 @@ impl DynamicComponent {
#[must_use] #[must_use]
pub fn new<Func>(resolve: Func) -> Self pub fn new<Func>(resolve: Func) -> Self
where where
Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option<Component> Func: for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option<Component>
+ Send + Send
+ Sync + Sync
+ 'static, + 'static,
@ -2587,7 +2585,7 @@ impl DynamicComponent {
/// Invokes the resolver function, optionally returning a resolved /// Invokes the resolver function, optionally returning a resolved
/// component. /// component.
#[must_use] #[must_use]
pub fn resolve(&self, context: &WidgetContext<'_, '_>) -> Option<Component> { pub fn resolve(&self, context: &WidgetContext<'_>) -> Option<Component> {
self.0.resolve_component(context) self.0.resolve_component(context)
} }
} }

View file

@ -69,7 +69,7 @@ macro_rules! define_components {
define_components!($type, |context| context.theme().$($path)*); define_components!($type, |context| context.theme().$($path)*);
}; };
($type:ty, |$context:ident| $($expr:tt)*) => { ($type:ty, |$context:ident| $($expr:tt)*) => {
fn default_value(&self, $context: &WidgetContext<'_, '_>) -> $type { fn default_value(&self, $context: &WidgetContext<'_>) -> $type {
$($expr)* $($expr)*
} }
}; };

View file

@ -25,7 +25,7 @@ pub struct Tick {
impl Tick { impl Tick {
/// Signals that this widget has been redrawn. /// Signals that this widget has been redrawn.
pub fn rendered(&self, context: &WidgetContext<'_, '_>) { pub fn rendered(&self, context: &WidgetContext<'_>) {
context.redraw_when_changed(&self.data.tick_number); context.redraw_when_changed(&self.data.tick_number);
self.data.sync.notify_one(); self.data.sync.notify_one();

View file

@ -103,7 +103,7 @@ pub trait Source<T> {
/// This function panics if this value is already locked by the current /// This function panics if this value is already locked by the current
/// thread. /// thread.
#[must_use] #[must_use]
fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T fn get_tracking_redraw(&self, context: &WidgetContext<'_>) -> T
where where
T: Clone, T: Clone,
Self: Trackable + Sized, Self: Trackable + Sized,
@ -121,7 +121,7 @@ pub trait Source<T> {
/// This function panics if this value is already locked by the current /// This function panics if this value is already locked by the current
/// thread. /// thread.
#[must_use] #[must_use]
fn get_tracking_invalidate(&self, context: &WidgetContext<'_, '_>) -> T fn get_tracking_invalidate(&self, context: &WidgetContext<'_>) -> T
where where
T: Clone, T: Clone,
Self: Trackable + Sized, Self: Trackable + Sized,
@ -290,6 +290,30 @@ pub trait Source<T> {
mapped mapped
} }
/// Returns a new [`Dynamic`] that contains a clone of each value from
/// `self`.
///
/// The returned dynamic does not hold a strong reference to `self`,
/// ensuring that `self` can be cleaned up even if the returned dynamic
/// still exists.
fn weak_clone(&self) -> Dynamic<T>
where
T: Clone + Send + 'static,
{
let mapped = Dynamic::new(self.get());
let mapped_weak = mapped.downgrade();
mapped.set_source(
self.for_each_cloned_try(move |value| {
let mapped = mapped_weak.upgrade().ok_or(CallbackDisconnected)?;
*mapped.lock() = value.clone();
Ok(())
})
.weak(),
);
mapped
}
/// Returns a new dynamic that is updated using `U::from(T.clone())` each /// Returns a new dynamic that is updated using `U::from(T.clone())` each
/// time `self` is updated. /// time `self` is updated.
#[must_use] #[must_use]
@ -510,7 +534,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
+ 'static, + 'static,
{ {
let this = WeakDynamic(Arc::downgrade(self)); let this = WeakDynamic(Arc::downgrade(self));
DynamicData::for_each(self, move || { dynamic_for_each(self, move || {
let this = this.upgrade().ok_or(CallbackDisconnected)?; let this = this.upgrade().ok_or(CallbackDisconnected)?;
this.map_generational(&mut for_each)?; this.map_generational(&mut for_each)?;
Ok(()) Ok(())
@ -523,7 +547,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
F: FnMut(GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static, F: FnMut(GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static,
{ {
let this = WeakDynamic(Arc::downgrade(self)); let this = WeakDynamic(Arc::downgrade(self));
DynamicData::for_each(self, move || { dynamic_for_each(self, move || {
let this = this.upgrade().ok_or(CallbackDisconnected)?; let this = this.upgrade().ok_or(CallbackDisconnected)?;
if let Ok(value) = this.try_map_generational(GenerationalValue::clone) { if let Ok(value) = this.try_map_generational(GenerationalValue::clone) {
@ -726,6 +750,7 @@ impl<T> Source<T> for Owned<T> {
let mut callbacks = self.callbacks.active.lock().ignore_poison(); let mut callbacks = self.callbacks.active.lock().ignore_poison();
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData { CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
id: Some(callbacks.push(Box::new(for_each))), id: Some(callbacks.push(Box::new(for_each))),
owner: None,
callbacks: self.callbacks.clone(), callbacks: self.callbacks.clone(),
})) }))
} }
@ -944,9 +969,10 @@ impl<T> Dynamic<T> {
Ok(()) Ok(())
})); }));
let t_weak = self.downgrade(); // The linked dynamic holds a reference to the original, since it's
// being created from the original.
let t = self.clone();
self.set_source(r.for_each_try(move |r| { self.set_source(r.for_each_try(move |r| {
let t = t_weak.upgrade().ok_or(CallbackDisconnected)?;
if let Some(update) = r_into_t(r).into() { if let Some(update) = r_into_t(r).into() {
let _result = t.replace(update); let _result = t.replace(update);
} }
@ -1388,18 +1414,20 @@ impl<T> DynamicData<T> {
Ok(old) Ok(old)
} }
}
pub fn for_each<F>(&self, map: F) -> CallbackHandle fn dynamic_for_each<T, F>(this: &Arc<DynamicData<T>>, map: F) -> CallbackHandle
where where
F: for<'a> FnMut() -> Result<(), CallbackDisconnected> + Send + 'static, F: for<'a> FnMut() -> Result<(), CallbackDisconnected> + Send + 'static,
{ T: Send + 'static,
let state = self.state().expect("deadlocked"); {
let mut data = state.callbacks.callbacks.lock().ignore_poison(); let state = this.state().expect("deadlocked");
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData { let mut data = state.callbacks.callbacks.lock().ignore_poison();
id: Some(data.callbacks.push(Box::new(map))), CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
callbacks: state.callbacks.clone(), id: Some(data.callbacks.push(Box::new(map))),
})) owner: Some(this.clone()),
} callbacks: state.callbacks.clone(),
}))
} }
/// A callback function is no longer connected to its source. /// A callback function is no longer connected to its source.
@ -1467,8 +1495,12 @@ enum CallbackHandleInner {
Multi(Vec<CallbackHandleData>), Multi(Vec<CallbackHandleData>),
} }
trait ReferencedDynamic: Sync + Send + 'static {}
impl<T> ReferencedDynamic for T where T: Sync + Send + 'static {}
struct CallbackHandleData { struct CallbackHandleData {
id: Option<LotId>, id: Option<LotId>,
owner: Option<Arc<dyn ReferencedDynamic>>,
callbacks: Arc<dyn CallbackCollection>, callbacks: Arc<dyn CallbackCollection>,
} }
@ -1508,6 +1540,33 @@ impl CallbackHandle {
} }
} }
} }
/// Drops any references to owning [`Dynamic`]s associated with this
/// callback.
///
/// This enables creating weak connections between callback graphs.
pub fn forget_owners(&mut self) {
match &mut self.0 {
CallbackHandleInner::None => {}
CallbackHandleInner::Single(handle) => {
handle.owner = None;
}
CallbackHandleInner::Multi(handles) => {
for handle in handles {
handle.owner = None;
}
}
}
}
/// Drops any references to owning [`Dynamic`]s associated with this
/// callback, and returns self.
///
/// This uses [`Self::forget_owners()`].
pub fn weak(mut self) -> Self {
self.forget_owners();
self
}
} }
impl Eq for CallbackHandle {} impl Eq for CallbackHandle {}
@ -1539,6 +1598,7 @@ impl Drop for CallbackHandleData {
} }
} }
} }
impl PartialEq for CallbackHandleData { impl PartialEq for CallbackHandleData {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.id == other.id && Arc::ptr_eq(&self.callbacks, &other.callbacks) self.id == other.id && Arc::ptr_eq(&self.callbacks, &other.callbacks)
@ -2508,7 +2568,7 @@ impl<T> Value<T> {
/// updated. /// updated.
pub fn map_tracking_redraw<R>( pub fn map_tracking_redraw<R>(
&self, &self,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
map: impl FnOnce(&T) -> R, map: impl FnOnce(&T) -> R,
) -> R { ) -> R {
match self { match self {
@ -2526,7 +2586,7 @@ impl<T> Value<T> {
/// updated. /// updated.
pub fn map_tracking_invalidate<R>( pub fn map_tracking_invalidate<R>(
&self, &self,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
map: impl FnOnce(&T) -> R, map: impl FnOnce(&T) -> R,
) -> R { ) -> R {
match self { match self {
@ -2573,7 +2633,7 @@ impl<T> Value<T> {
/// ///
/// If `self` is a dynamic, `context` will be refreshed when the value is /// If `self` is a dynamic, `context` will be refreshed when the value is
/// updated. /// updated.
pub fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T pub fn get_tracking_redraw(&self, context: &WidgetContext<'_>) -> T
where where
T: Clone, T: Clone,
{ {
@ -2584,7 +2644,7 @@ impl<T> Value<T> {
/// ///
/// If `self` is a dynamic, `context` will be invalidated when the value is /// If `self` is a dynamic, `context` will be invalidated when the value is
/// updated. /// updated.
pub fn get_tracking_invalidate(&self, context: &WidgetContext<'_, '_>) -> T pub fn get_tracking_invalidate(&self, context: &WidgetContext<'_>) -> T
where where
T: Clone, T: Clone,
{ {
@ -2932,7 +2992,7 @@ macro_rules! impl_tuple_for_each_cloned {
} }
}; };
($self:ident $for_each:ident $handles:ident [] [$type:ident $field:tt $var:ident]) => { ($self:ident $for_each:ident $handles:ident [] [$type:ident $field:tt $var:ident]) => {
$handles += $self.$field.for_each(move |field: &$type| $for_each((field.clone(),))); $handles += $self.$field.for_each_cloned(move |field| $for_each((field,)));
}; };
($self:ident $for_each:ident $handles:ident [] [$($type:ident $field:tt $var:ident),+]) => { ($self:ident $for_each:ident $handles:ident [] [$($type:ident $field:tt $var:ident),+]) => {
let $for_each = Arc::new(Mutex::new($for_each)); let $for_each = Arc::new(Mutex::new($for_each));

View file

@ -12,9 +12,7 @@ use alot::LotId;
use figures::units::{Px, UPx}; use figures::units::{Px, UPx};
use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size}; use figures::{IntoSigned, IntoUnsigned, Point, Rect, Size};
use intentional::Assert; use intentional::Assert;
use kludgine::app::winit::event::{ use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::Color; use kludgine::Color;
@ -45,7 +43,11 @@ use crate::widgets::{
Align, Button, Checkbox, Collapse, Container, Disclose, Expand, Layers, Resize, Scroll, Space, Align, Button, Checkbox, Collapse, Container, Disclose, Expand, Layers, Resize, Scroll, Space,
Stack, Style, Themed, ThemedMode, Validated, Wrap, Stack, Style, Themed, ThemedMode, Validated, Wrap,
}; };
use crate::window::{RunningWindow, ThemeMode, Window, WindowBehavior, WindowHandle, WindowLocal}; use crate::window::sealed::WindowCommand;
use crate::window::{
CushyWindowBuilder, DeviceId, Rgb8, RunningWindow, ThemeMode, VirtualRecorderBuilder, Window,
WindowBehavior, WindowHandle, WindowLocal,
};
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A type that makes up a graphical user interface. /// A type that makes up a graphical user interface.
@ -264,7 +266,7 @@ use crate::ConstraintLimit;
/// [repo]: https://github.com/khonsulabs/cushy /// [repo]: https://github.com/khonsulabs/cushy
pub trait Widget: Send + Debug + 'static { pub trait Widget: Send + Debug + 'static {
/// Redraw the contents of this widget. /// Redraw the contents of this widget.
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>);
/// Writes a summary of this widget into `fmt`. /// Writes a summary of this widget into `fmt`.
/// ///
@ -297,50 +299,46 @@ pub trait Widget: Send + Debug + 'static {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
available_space.map(ConstraintLimit::min) available_space.map(ConstraintLimit::min)
} }
/// The widget has been mounted into a parent widget. /// The widget has been mounted into a parent widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {} fn mounted(&mut self, context: &mut EventContext<'_>) {}
/// The widget has been removed from its parent widget. /// The widget has been removed from its parent widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {} fn unmounted(&mut self, context: &mut EventContext<'_>) {}
/// Returns true if this widget should respond to mouse input at `location`. /// Returns true if this widget should respond to mouse input at `location`.
#[allow(unused_variables)] #[allow(unused_variables)]
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
false false
} }
/// The widget is currently has a cursor hovering it at `location`. /// The widget is currently has a cursor hovering it at `location`.
#[allow(unused_variables)] #[allow(unused_variables)]
fn hover( fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
None None
} }
/// The widget is no longer being hovered. /// The widget is no longer being hovered.
#[allow(unused_variables)] #[allow(unused_variables)]
fn unhover(&mut self, context: &mut EventContext<'_, '_>) {} fn unhover(&mut self, context: &mut EventContext<'_>) {}
/// This widget has been targeted to be focused. If this function returns /// This widget has been targeted to be focused. If this function returns
/// true, the widget will be focused. If false, Cushy will continue /// true, the widget will be focused. If false, Cushy will continue
/// searching for another focus target. /// searching for another focus target.
#[allow(unused_variables)] #[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false false
} }
/// The widget has received focus for user input. /// The widget has received focus for user input.
#[allow(unused_variables)] #[allow(unused_variables)]
fn focus(&mut self, context: &mut EventContext<'_, '_>) {} fn focus(&mut self, context: &mut EventContext<'_>) {}
/// The widget should switch to the next focusable area within this widget, /// The widget should switch to the next focusable area within this widget,
/// honoring `direction` in a consistent manner. Returning `HANDLED` will /// honoring `direction` in a consistent manner. Returning `HANDLED` will
@ -349,7 +347,7 @@ pub trait Widget: Send + Debug + 'static {
fn advance_focus( fn advance_focus(
&mut self, &mut self,
direction: VisualOrder, direction: VisualOrder,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -357,21 +355,21 @@ pub trait Widget: Send + Debug + 'static {
/// The widget is about to lose focus. Returning true allows the focus to /// The widget is about to lose focus. Returning true allows the focus to
/// switch away from this widget. /// switch away from this widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true true
} }
/// The widget is no longer focused for user input. /// The widget is no longer focused for user input.
#[allow(unused_variables)] #[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_, '_>) {} fn blur(&mut self, context: &mut EventContext<'_>) {}
/// The widget has become the active widget. /// The widget has become the active widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_, '_>) {} fn activate(&mut self, context: &mut EventContext<'_>) {}
/// The widget is no longer active. /// The widget is no longer active.
#[allow(unused_variables)] #[allow(unused_variables)]
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {} fn deactivate(&mut self, context: &mut EventContext<'_>) {}
/// A mouse button event has occurred at `location`. Returns whether the /// A mouse button event has occurred at `location`. Returns whether the
/// event has been handled or not. /// event has been handled or not.
@ -384,7 +382,7 @@ pub trait Widget: Send + Debug + 'static {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -397,7 +395,7 @@ pub trait Widget: Send + Debug + 'static {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
} }
@ -408,7 +406,7 @@ pub trait Widget: Send + Debug + 'static {
location: Option<Point<Px>>, location: Option<Point<Px>>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
} }
@ -420,7 +418,7 @@ pub trait Widget: Send + Debug + 'static {
device_id: DeviceId, device_id: DeviceId,
input: KeyEvent, input: KeyEvent,
is_synthetic: bool, is_synthetic: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -428,7 +426,7 @@ pub trait Widget: Send + Debug + 'static {
/// An input manager event has been sent to this widget. Returns whether the /// An input manager event has been sent to this widget. Returns whether the
/// event has been handled or not. /// event has been handled or not.
#[allow(unused_variables)] #[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED IGNORED
} }
@ -440,7 +438,7 @@ pub trait Widget: Send + Debug + 'static {
device_id: DeviceId, device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
phase: TouchPhase, phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -451,7 +449,7 @@ pub trait Widget: Send + Debug + 'static {
#[allow(unused_variables)] #[allow(unused_variables)]
fn root_behavior( fn root_behavior(
&mut self, &mut self,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> { ) -> Option<(RootBehavior, WidgetInstance)> {
None None
} }
@ -544,7 +542,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// Returns the behavior this widget should apply when positioned at the /// Returns the behavior this widget should apply when positioned at the
/// root of the window. /// root of the window.
#[allow(unused_variables)] #[allow(unused_variables)]
fn root_behavior(&mut self, context: &mut EventContext<'_, '_>) -> Option<RootBehavior> { fn root_behavior(&mut self, context: &mut EventContext<'_>) -> Option<RootBehavior> {
None None
} }
@ -552,13 +550,13 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// ///
/// This is invoked before the wrapped widget is drawn. /// This is invoked before the wrapped widget is drawn.
#[allow(unused_variables)] #[allow(unused_variables)]
fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {} fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {}
/// Draws the foreground of the widget. /// Draws the foreground of the widget.
/// ///
/// This is invoked after the wrapped widget is drawn. /// This is invoked after the wrapped widget is drawn.
#[allow(unused_variables)] #[allow(unused_variables)]
fn redraw_foreground(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {} fn redraw_foreground(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {}
/// Returns the rectangle that the child widget should occupy given /// Returns the rectangle that the child widget should occupy given
/// `available_space`. /// `available_space`.
@ -566,7 +564,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
fn layout_child( fn layout_child(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
let adjusted_space = self.adjust_child_constraints(available_space, context); let adjusted_space = self.adjust_child_constraints(available_space, context);
let child = self.child_mut().mounted(&mut context.as_event_context()); let child = self.child_mut().mounted(&mut context.as_event_context());
@ -584,7 +582,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
fn adjust_child_constraints( fn adjust_child_constraints(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> { ) -> Size<ConstraintLimit> {
available_space available_space
} }
@ -596,7 +594,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
&mut self, &mut self,
size: Size<Px>, size: Size<Px>,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
Size::new( Size::new(
available_space available_space
@ -612,7 +610,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// Returns the background color to render behind the wrapped widget. /// Returns the background color to render behind the wrapped widget.
#[allow(unused_variables)] #[allow(unused_variables)]
#[must_use] #[must_use]
fn background_color(&mut self, context: &WidgetContext<'_, '_>) -> Option<Color> { fn background_color(&mut self, context: &WidgetContext<'_>) -> Option<Color> {
// WidgetBackground is already filled, so we don't need to do anything // WidgetBackground is already filled, so we don't need to do anything
// else by default. // else by default.
None None
@ -620,39 +618,35 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// The widget has been mounted into a parent widget. /// The widget has been mounted into a parent widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {} fn mounted(&mut self, context: &mut EventContext<'_>) {}
/// The widget has been removed from its parent widget. /// The widget has been removed from its parent widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.child_mut().unmount_in(context); self.child_mut().unmount_in(context);
} }
/// Returns true if this widget should respond to mouse input at `location`. /// Returns true if this widget should respond to mouse input at `location`.
#[allow(unused_variables)] #[allow(unused_variables)]
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
false false
} }
/// The widget is currently has a cursor hovering it at `location`. /// The widget is currently has a cursor hovering it at `location`.
#[allow(unused_variables)] #[allow(unused_variables)]
fn hover( fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
None None
} }
/// The widget is no longer being hovered. /// The widget is no longer being hovered.
#[allow(unused_variables)] #[allow(unused_variables)]
fn unhover(&mut self, context: &mut EventContext<'_, '_>) {} fn unhover(&mut self, context: &mut EventContext<'_>) {}
/// This widget has been targeted to be focused. If this function returns /// This widget has been targeted to be focused. If this function returns
/// true, the widget will be focused. If false, Cushy will continue /// true, the widget will be focused. If false, Cushy will continue
/// searching for another focus target. /// searching for another focus target.
#[allow(unused_variables)] #[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false false
} }
@ -663,33 +657,33 @@ pub trait WrapperWidget: Debug + Send + 'static {
fn advance_focus( fn advance_focus(
&mut self, &mut self,
direction: VisualOrder, direction: VisualOrder,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
/// The widget has received focus for user input. /// The widget has received focus for user input.
#[allow(unused_variables)] #[allow(unused_variables)]
fn focus(&mut self, context: &mut EventContext<'_, '_>) {} fn focus(&mut self, context: &mut EventContext<'_>) {}
/// The widget is about to lose focus. Returning true allows the focus to /// The widget is about to lose focus. Returning true allows the focus to
/// switch away from this widget. /// switch away from this widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true true
} }
/// The widget is no longer focused for user input. /// The widget is no longer focused for user input.
#[allow(unused_variables)] #[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_, '_>) {} fn blur(&mut self, context: &mut EventContext<'_>) {}
/// The widget has become the active widget. /// The widget has become the active widget.
#[allow(unused_variables)] #[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_, '_>) {} fn activate(&mut self, context: &mut EventContext<'_>) {}
/// The widget is no longer active. /// The widget is no longer active.
#[allow(unused_variables)] #[allow(unused_variables)]
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {} fn deactivate(&mut self, context: &mut EventContext<'_>) {}
/// A mouse button event has occurred at `location`. Returns whether the /// A mouse button event has occurred at `location`. Returns whether the
/// event has been handled or not. /// event has been handled or not.
@ -702,7 +696,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -715,7 +709,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
} }
@ -726,7 +720,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Option<Point<Px>>, location: Option<Point<Px>>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
} }
@ -738,7 +732,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
device_id: DeviceId, device_id: DeviceId,
input: KeyEvent, input: KeyEvent,
is_synthetic: bool, is_synthetic: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -746,7 +740,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// An input manager event has been sent to this widget. Returns whether the /// An input manager event has been sent to this widget. Returns whether the
/// event has been handled or not. /// event has been handled or not.
#[allow(unused_variables)] #[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED IGNORED
} }
@ -758,7 +752,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
device_id: DeviceId, device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
phase: TouchPhase, phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
IGNORED IGNORED
} }
@ -770,13 +764,13 @@ where
{ {
fn root_behavior( fn root_behavior(
&mut self, &mut self,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> { ) -> Option<(RootBehavior, WidgetInstance)> {
T::root_behavior(self, context) T::root_behavior(self, context)
.map(|behavior| (behavior, T::child_mut(self).widget().clone())) .map(|behavior| (behavior, T::child_mut(self).widget().clone()))
} }
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let background_color = self.background_color(context); let background_color = self.background_color(context);
if let Some(color) = background_color { if let Some(color) = background_color {
context.fill(color); context.fill(color);
@ -793,7 +787,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let layout = self.layout_child(available_space, context); let layout = self.layout_child(available_space, context);
let child = self.child_mut().mounted(&mut context.as_event_context()); let child = self.child_mut().mounted(&mut context.as_event_context());
@ -801,47 +795,43 @@ where
layout.size layout.size
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
T::mounted(self, context); T::mounted(self, context);
} }
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
T::unmounted(self, context); T::unmounted(self, context);
} }
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
T::hit_test(self, location, context) T::hit_test(self, location, context)
} }
fn hover( fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
T::hover(self, location, context) T::hover(self, location, context)
} }
fn unhover(&mut self, context: &mut EventContext<'_, '_>) { fn unhover(&mut self, context: &mut EventContext<'_>) {
T::unhover(self, context); T::unhover(self, context);
} }
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
T::accept_focus(self, context) T::accept_focus(self, context)
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
T::focus(self, context); T::focus(self, context);
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
T::blur(self, context); T::blur(self, context);
} }
fn activate(&mut self, context: &mut EventContext<'_, '_>) { fn activate(&mut self, context: &mut EventContext<'_>) {
T::activate(self, context); T::activate(self, context);
} }
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) { fn deactivate(&mut self, context: &mut EventContext<'_>) {
T::deactivate(self, context); T::deactivate(self, context);
} }
@ -850,7 +840,7 @@ where
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
T::mouse_down(self, location, device_id, button, context) T::mouse_down(self, location, device_id, button, context)
} }
@ -860,7 +850,7 @@ where
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
T::mouse_drag(self, location, device_id, button, context); T::mouse_drag(self, location, device_id, button, context);
} }
@ -870,7 +860,7 @@ where
location: Option<Point<Px>>, location: Option<Point<Px>>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
T::mouse_up(self, location, device_id, button, context); T::mouse_up(self, location, device_id, button, context);
} }
@ -880,12 +870,12 @@ where
device_id: DeviceId, device_id: DeviceId,
input: KeyEvent, input: KeyEvent,
is_synthetic: bool, is_synthetic: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
T::keyboard_input(self, device_id, input, is_synthetic, context) T::keyboard_input(self, device_id, input, is_synthetic, context)
} }
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
T::ime(self, ime, context) T::ime(self, ime, context)
} }
@ -894,7 +884,7 @@ where
device_id: DeviceId, device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
phase: TouchPhase, phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
T::mouse_wheel(self, device_id, delta, phase, context) T::mouse_wheel(self, device_id, delta, phase, context)
} }
@ -902,12 +892,12 @@ where
fn advance_focus( fn advance_focus(
&mut self, &mut self,
direction: VisualOrder, direction: VisualOrder,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
T::advance_focus(self, direction, context) T::advance_focus(self, direction, context)
} }
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
T::allow_blur(self, context) T::allow_blur(self, context)
} }
@ -926,6 +916,16 @@ pub trait MakeWidget: Sized {
Window::new(self.make_widget()) Window::new(self.make_widget())
} }
/// Returns a builder for a [`VirtualWindow`](crate::window::VirtualWindow).
fn build_virtual_window(self) -> CushyWindowBuilder {
CushyWindowBuilder::new(self)
}
/// Returns a builder for a [`VirtualRecorder`](crate::window::VirtualRecorder)
fn build_recorder(self) -> VirtualRecorderBuilder<Rgb8> {
VirtualRecorderBuilder::new(self)
}
/// Associates `styles` with this widget. /// Associates `styles` with this widget.
/// ///
/// This is equivalent to `Style::new(styles, self)`. /// This is equivalent to `Style::new(styles, self)`.
@ -1589,7 +1589,10 @@ impl PartialEq for WidgetInstance {
impl WindowBehavior for WidgetInstance { impl WindowBehavior for WidgetInstance {
type Context = Self; type Context = Self;
fn initialize(_window: &mut RunningWindow<'_>, context: Self::Context) -> Self { fn initialize(
_window: &mut RunningWindow<kludgine::app::Window<'_, WindowCommand>>,
context: Self::Context,
) -> Self {
context context
} }
@ -2181,11 +2184,7 @@ where
T: MountableChild, T: MountableChild,
{ {
/// Mounts and unmounts all children needed to be in sync with `children`. /// Mounts and unmounts all children needed to be in sync with `children`.
pub fn synchronize_with( pub fn synchronize_with(&mut self, children: &Value<Children>, context: &mut EventContext<'_>) {
&mut self,
children: &Value<Children>,
context: &mut EventContext<'_, '_>,
) {
let current_generation = children.generation(); let current_generation = children.generation();
if current_generation.map_or_else( if current_generation.map_or_else(
|| children.map(Children::len) != self.children.len(), || children.map(Children::len) != self.children.len(),
@ -2284,10 +2283,7 @@ impl WidgetRef {
} }
/// Returns this child, mounting it in the process if necessary. /// Returns this child, mounting it in the process if necessary.
fn mounted_for_context<'window>( fn mounted_for_context(&mut self, context: &mut impl AsEventContext) -> &MountedWidget {
&mut self,
context: &mut impl AsEventContext<'window>,
) -> &MountedWidget {
let mut context = context.as_event_context(); let mut context = context.as_event_context();
self.mounted self.mounted
.entry(&context) .entry(&context)
@ -2295,21 +2291,18 @@ impl WidgetRef {
} }
/// Returns this child, mounting it in the process if necessary. /// Returns this child, mounting it in the process if necessary.
pub fn mount_if_needed<'window>(&mut self, context: &mut impl AsEventContext<'window>) { pub fn mount_if_needed(&mut self, context: &mut impl AsEventContext) {
self.mounted_for_context(context); self.mounted_for_context(context);
} }
/// Returns this child, mounting it in the process if necessary. /// Returns this child, mounting it in the process if necessary.
pub fn mounted<'window>( pub fn mounted(&mut self, context: &mut impl AsEventContext) -> MountedWidget {
&mut self,
context: &mut impl AsEventContext<'window>,
) -> MountedWidget {
self.mounted_for_context(context).clone() self.mounted_for_context(context).clone()
} }
/// Returns this child, mounting it in the process if necessary. /// Returns this child, mounting it in the process if necessary.
#[must_use] #[must_use]
pub fn as_mounted(&self, context: &WidgetContext<'_, '_>) -> Option<&MountedWidget> { pub fn as_mounted(&self, context: &WidgetContext<'_>) -> Option<&MountedWidget> {
self.mounted.get(context) self.mounted.get(context)
} }
@ -2320,7 +2313,7 @@ impl WidgetRef {
} }
/// Unmounts this widget from the window belonging to `context`, if needed. /// Unmounts this widget from the window belonging to `context`, if needed.
pub fn unmount_in<'window>(&mut self, context: &mut impl AsEventContext<'window>) { pub fn unmount_in(&mut self, context: &mut impl AsEventContext) {
let mut context = context.as_event_context(); let mut context = context.as_event_context();
if let Some(mounted) = self.mounted.clear_for(&context) { if let Some(mounted) = self.mounted.clear_for(&context) {
context.remove_child(&mounted); context.remove_child(&mounted);
@ -2351,7 +2344,7 @@ impl PartialEq for WidgetRef {
impl ManageWidget for WidgetRef { impl ManageWidget for WidgetRef {
type Managed = Option<MountedWidget>; type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
self.mounted self.mounted
.get(context) .get(context)
.cloned() .cloned()
@ -2374,7 +2367,7 @@ impl WidgetId {
/// Finds this widget mounted in this window, if present. /// Finds this widget mounted in this window, if present.
#[must_use] #[must_use]
pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<MountedWidget> { pub fn find_in(self, context: &WidgetContext<'_>) -> Option<MountedWidget> {
context.tree.widget(self) context.tree.widget(self)
} }
} }

View file

@ -87,7 +87,7 @@ impl Align {
fn measure( fn measure(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Layout { ) -> Layout {
let margin = self.edges.get(); let margin = self.edges.get();
let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom); let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom);
@ -179,14 +179,14 @@ impl WrapperWidget for Align {
&mut self.child &mut self.child
} }
fn root_behavior(&mut self, _context: &mut EventContext<'_, '_>) -> Option<RootBehavior> { fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option<RootBehavior> {
Some(RootBehavior::Align) Some(RootBehavior::Align)
} }
fn layout_child( fn layout_child(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
let layout = self.measure(available_space, context); let layout = self.measure(available_space, context);

View file

@ -3,7 +3,7 @@ use std::time::Duration;
use figures::units::{Lp, Px, UPx}; use figures::units::{Lp, Px, UPx};
use figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size}; use figures::{IntoSigned, Point, Rect, Round, ScreenScale, Size};
use kludgine::app::winit::event::{DeviceId, MouseButton}; use kludgine::app::winit::event::MouseButton;
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::Color; use kludgine::Color;
@ -21,7 +21,7 @@ use crate::styles::components::{
use crate::styles::{ColorExt, Styles}; use crate::styles::{ColorExt, Styles};
use crate::value::{Destination, Dynamic, IntoValue, Source, Value}; use crate::value::{Destination, Dynamic, IntoValue, Source, Value};
use crate::widget::{Callback, EventHandling, MakeWidget, Widget, WidgetRef, HANDLED}; use crate::widget::{Callback, EventHandling, MakeWidget, Widget, WidgetRef, HANDLED};
use crate::window::WindowLocal; use crate::window::{DeviceId, WindowLocal};
use crate::FitMeasuredSize; use crate::FitMeasuredSize;
/// A clickable button. /// A clickable button.
@ -71,7 +71,7 @@ impl ButtonKind {
pub fn colors_for_default( pub fn colors_for_default(
self, self,
visual_state: VisualState, visual_state: VisualState,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
) -> ButtonColors { ) -> ButtonColors {
match self { match self {
ButtonKind::Solid => match visual_state { ButtonKind::Solid => match visual_state {
@ -171,7 +171,7 @@ impl Button {
self self
} }
fn invoke_on_click(&mut self, context: &WidgetContext<'_, '_>) { fn invoke_on_click(&mut self, context: &WidgetContext<'_>) {
if context.enabled() { if context.enabled() {
if let Some(on_click) = self.on_click.as_mut() { if let Some(on_click) = self.on_click.as_mut() {
on_click.invoke(()); on_click.invoke(());
@ -179,7 +179,7 @@ impl Button {
} }
} }
fn visual_style(context: &WidgetContext<'_, '_>) -> VisualState { fn visual_style(context: &WidgetContext<'_>) -> VisualState {
if !context.enabled() { if !context.enabled() {
VisualState::Disabled VisualState::Disabled
} else if context.active() { } else if context.active() {
@ -195,7 +195,7 @@ impl Button {
#[must_use] #[must_use]
pub fn colors_for_transparent( pub fn colors_for_transparent(
visual_state: VisualState, visual_state: VisualState,
context: &WidgetContext<'_, '_>, context: &WidgetContext<'_>,
) -> ButtonColors { ) -> ButtonColors {
match visual_state { match visual_state {
VisualState::Normal => ButtonColors { VisualState::Normal => ButtonColors {
@ -225,7 +225,7 @@ impl Button {
} }
} }
fn determine_stateful_colors(&mut self, context: &mut WidgetContext<'_, '_>) -> ButtonColors { fn determine_stateful_colors(&mut self, context: &mut WidgetContext<'_>) -> ButtonColors {
let kind = self.kind.get_tracking_redraw(context); let kind = self.kind.get_tracking_redraw(context);
let visual_state = Self::visual_style(context); let visual_state = Self::visual_style(context);
@ -247,7 +247,7 @@ impl Button {
} }
} }
fn update_colors(&mut self, context: &mut WidgetContext<'_, '_>, immediate: bool) { fn update_colors(&mut self, context: &mut WidgetContext<'_>, immediate: bool) {
let new_style = self.determine_stateful_colors(context); let new_style = self.determine_stateful_colors(context);
let window_local = self.per_window.entry(context).or_default(); let window_local = self.per_window.entry(context).or_default();
@ -271,7 +271,7 @@ impl Button {
} }
} }
fn current_style(&mut self, context: &mut WidgetContext<'_, '_>) -> ButtonColors { fn current_style(&mut self, context: &mut WidgetContext<'_>) -> ButtonColors {
if self if self
.per_window .per_window
.entry(context) .entry(context)
@ -318,7 +318,7 @@ impl VisualState {
/// Returns the colors to apply to a [`ButtonKind::Solid`] [`Button`] or /// Returns the colors to apply to a [`ButtonKind::Solid`] [`Button`] or
/// button-like widget. /// button-like widget.
#[must_use] #[must_use]
pub fn solid_colors(self, context: &WidgetContext<'_, '_>) -> ButtonColors { pub fn solid_colors(self, context: &WidgetContext<'_>) -> ButtonColors {
match self { match self {
VisualState::Normal => ButtonColors { VisualState::Normal => ButtonColors {
background: context.get(&ButtonBackground), background: context.get(&ButtonBackground),
@ -346,7 +346,7 @@ impl VisualState {
/// Returns the colors to apply to a [`ButtonKind::Outline`] [`Button`] or /// Returns the colors to apply to a [`ButtonKind::Outline`] [`Button`] or
/// button-like widget. /// button-like widget.
#[must_use] #[must_use]
pub fn outline_colors(self, context: &WidgetContext<'_, '_>) -> ButtonColors { pub fn outline_colors(self, context: &WidgetContext<'_>) -> ButtonColors {
let solid = self.solid_colors(context); let solid = self.solid_colors(context);
ButtonColors { ButtonColors {
background: solid.outline, background: solid.outline,
@ -364,7 +364,7 @@ impl Widget for Button {
.finish() .finish()
} }
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
#![allow(clippy::similar_names)] #![allow(clippy::similar_names)]
let current_style = self.kind.get_tracking_redraw(context); let current_style = self.kind.get_tracking_redraw(context);
@ -417,11 +417,11 @@ impl Widget for Button {
context.for_other(&content).redraw(); context.for_other(&content).redraw();
} }
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
self.focusable && context.enabled() && context.get(&AutoFocusableControls).is_all() self.focusable && context.enabled() && context.get(&AutoFocusableControls).is_all()
} }
@ -430,7 +430,7 @@ impl Widget for Button {
_location: Point<Px>, _location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
self.per_window.entry(context).or_default().buttons_pressed += 1; self.per_window.entry(context).or_default().buttons_pressed += 1;
context.activate(); context.activate();
@ -442,7 +442,7 @@ impl Widget for Button {
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
let changed = if Rect::from(context.last_layout().expect("must have been rendered").size) let changed = if Rect::from(context.last_layout().expect("must have been rendered").size)
.contains(location) .contains(location)
@ -462,7 +462,7 @@ impl Widget for Button {
location: Option<Point<Px>>, location: Option<Point<Px>>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
let window_local = self.per_window.entry(context).or_default(); let window_local = self.per_window.entry(context).or_default();
window_local.buttons_pressed -= 1; window_local.buttons_pressed -= 1;
@ -484,7 +484,7 @@ impl Widget for Button {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<crate::ConstraintLimit>, available_space: Size<crate::ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let padding = context let padding = context
.get(&IntrinsicPadding) .get(&IntrinsicPadding)
@ -502,14 +502,14 @@ impl Widget for Button {
size + double_padding size + double_padding
} }
fn unhover(&mut self, context: &mut EventContext<'_, '_>) { fn unhover(&mut self, context: &mut EventContext<'_>) {
self.update_colors(context, false); self.update_colors(context, false);
} }
fn hover( fn hover(
&mut self, &mut self,
_location: Point<Px>, _location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<CursorIcon> { ) -> Option<CursorIcon> {
self.update_colors(context, false); self.update_colors(context, false);
@ -520,15 +520,15 @@ impl Widget for Button {
} }
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
context.set_needs_redraw(); context.set_needs_redraw();
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
context.set_needs_redraw(); context.set_needs_redraw();
} }
fn activate(&mut self, context: &mut EventContext<'_, '_>) { fn activate(&mut self, context: &mut EventContext<'_>) {
let window_local = self.per_window.entry(context).or_default(); let window_local = self.per_window.entry(context).or_default();
// If we have no buttons pressed, the event should fire on activate not // If we have no buttons pressed, the event should fire on activate not
// on deactivate. // on deactivate.
@ -538,11 +538,11 @@ impl Widget for Button {
self.update_colors(context, true); self.update_colors(context, true);
} }
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) { fn deactivate(&mut self, context: &mut EventContext<'_>) {
self.update_colors(context, false); self.update_colors(context, false);
} }
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.content.unmount_in(context); self.content.unmount_in(context);
} }
} }

View file

@ -20,8 +20,8 @@ impl Canvas {
/// Returns a new canvas that draws its contents by invoking `render`. /// Returns a new canvas that draws its contents by invoking `render`.
pub fn new<F>(render: F) -> Self pub fn new<F>(render: F) -> Self
where where
F: for<'clip, 'gfx, 'pass, 'context, 'window> FnMut( F: for<'clip, 'gfx, 'pass, 'context> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut GraphicsContext<'context, 'clip, 'gfx, 'pass>,
) + Send ) + Send
+ 'static, + 'static,
{ {
@ -40,7 +40,7 @@ impl Canvas {
} }
impl Widget for Canvas { impl Widget for Canvas {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
context.redraw_when_changed(&self.redraw); context.redraw_when_changed(&self.redraw);
self.render.render(context); self.render.render(context);
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
@ -51,7 +51,7 @@ impl Widget for Canvas {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<crate::ConstraintLimit>, available_space: Size<crate::ConstraintLimit>,
_context: &mut LayoutContext<'_, '_, '_, '_, '_>, _context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
available_space.map(ConstraintLimit::max) available_space.map(ConstraintLimit::max)
} }
@ -64,17 +64,16 @@ impl Debug for Canvas {
} }
trait RenderFunction: Send + 'static { trait RenderFunction: Send + 'static {
fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>);
} }
impl<F> RenderFunction for F impl<F> RenderFunction for F
where where
F: for<'clip, 'gfx, 'pass, 'context, 'window> FnMut( F: for<'clip, 'gfx, 'pass, 'context> FnMut(&mut GraphicsContext<'context, 'clip, 'gfx, 'pass>)
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, + Send
) + Send
+ 'static, + 'static,
{ {
fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn render(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
self(context); self(context);
} }
} }

View file

@ -175,7 +175,7 @@ struct CheckboxOrnament {
} }
impl Widget for CheckboxOrnament { impl Widget for CheckboxOrnament {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let checkbox_size = context let checkbox_size = context
.gfx .gfx
.region() .region()
@ -244,7 +244,7 @@ impl Widget for CheckboxOrnament {
fn layout( fn layout(
&mut self, &mut self,
_available_space: Size<ConstraintLimit>, _available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<figures::units::UPx> { ) -> Size<figures::units::UPx> {
let checkbox_size = context.get(&CheckboxSize).into_upx(context.gfx.scale()); let checkbox_size = context.get(&CheckboxSize).into_upx(context.gfx.scale());
Size::squared(checkbox_size) Size::squared(checkbox_size)

View file

@ -51,7 +51,7 @@ impl Collapse {
} }
} }
fn note_child_size(&mut self, size: Px, context: &mut LayoutContext<'_, '_, '_, '_, '_>) { fn note_child_size(&mut self, size: Px, context: &mut LayoutContext<'_, '_, '_, '_>) {
let (easing, target) = if self.collapse.get_tracking_invalidate(context) { let (easing, target) = if self.collapse.get_tracking_invalidate(context) {
(EasingFunction::from(EaseOutQuadradic), Px::ZERO) (EasingFunction::from(EaseOutQuadradic), Px::ZERO)
} else { } else {
@ -90,7 +90,7 @@ impl WrapperWidget for Collapse {
&mut self, &mut self,
size: Size<Px>, size: Size<Px>,
_available_space: Size<ConstraintLimit>, _available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
let clip_size = self.size.get_tracking_invalidate(context); let clip_size = self.size.get_tracking_invalidate(context);
if self.vertical { if self.vertical {

View file

@ -4,7 +4,7 @@ use std::ops::Range;
use figures::units::{Lp, Px}; use figures::units::{Lp, Px};
use figures::{FloatConversion, Point, Rect, Round, ScreenScale, Zero}; use figures::{FloatConversion, Point, Rect, Round, ScreenScale, Zero};
use intentional::Cast; use intentional::Cast;
use kludgine::app::winit::event::{DeviceId, MouseButton}; use kludgine::app::winit::event::MouseButton;
use kludgine::shapes::{self, FillOptions, PathBuilder, Shape, StrokeOptions}; use kludgine::shapes::{self, FillOptions, PathBuilder, Shape, StrokeOptions};
use kludgine::{Color, DrawableExt, Origin}; use kludgine::{Color, DrawableExt, Origin};
@ -14,6 +14,7 @@ use crate::styles::components::{HighlightColor, OutlineColor, TextColor};
use crate::styles::{ColorExt, ColorSource}; use crate::styles::{ColorExt, ColorSource};
use crate::value::{Destination, Dynamic, IntoValue, Source, Value}; use crate::value::{Destination, Dynamic, IntoValue, Source, Value};
use crate::widget::{EventHandling, Widget, HANDLED}; use crate::widget::{EventHandling, Widget, HANDLED};
use crate::window::DeviceId;
/// A widget that selects a [`ColorSource`]. /// A widget that selects a [`ColorSource`].
#[derive(Debug)] #[derive(Debug)]
@ -69,7 +70,7 @@ impl ColorSourcePicker {
} }
impl Widget for ColorSourcePicker { impl Widget for ColorSourcePicker {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let loupe_size = Lp::mm(3).into_px(context.gfx.scale()); let loupe_size = Lp::mm(3).into_px(context.gfx.scale());
let size = context.gfx.region().size; let size = context.gfx.region().size;
@ -148,7 +149,7 @@ impl Widget for ColorSourcePicker {
); );
} }
fn hit_test(&mut self, location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
self.visible_rect.contains(location) self.visible_rect.contains(location)
} }
@ -157,7 +158,7 @@ impl Widget for ColorSourcePicker {
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
self.update_from_mouse(location); self.update_from_mouse(location);
HANDLED HANDLED
@ -168,7 +169,7 @@ impl Widget for ColorSourcePicker {
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) { ) {
self.update_from_mouse(location); self.update_from_mouse(location);
} }
@ -180,7 +181,7 @@ fn draw_gradient_segment(
height: Px, height: Px,
hue: Range<f32>, hue: Range<f32>,
lightness: ZeroToOne, lightness: ZeroToOne,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
) { ) {
let mid_left = ( let mid_left = (
Point::new(start.x, start.y + height / 2), Point::new(start.x, start.y + height / 2),

View file

@ -158,7 +158,7 @@ impl Container {
self self
} }
fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_, '_>) -> Edges<Px> { fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_>) -> Edges<Px> {
match &self.padding { match &self.padding {
Some(padding) => padding.get(), Some(padding) => padding.get(),
None => Edges::from(context.get(&IntrinsicPadding)), None => Edges::from(context.get(&IntrinsicPadding)),
@ -166,7 +166,7 @@ impl Container {
.map(|dim| dim.into_px(context.gfx.scale()).round()) .map(|dim| dim.into_px(context.gfx.scale()).round())
} }
fn effective_background_color(&mut self, context: &WidgetContext<'_, '_>) -> kludgine::Color { fn effective_background_color(&mut self, context: &WidgetContext<'_>) -> kludgine::Color {
let background = match self.background.get() { let background = match self.background.get() {
ContainerBackground::Color(color) => EffectiveBackground::Color(color), ContainerBackground::Color(color) => EffectiveBackground::Color(color),
ContainerBackground::Level(level) => EffectiveBackground::Level(level), ContainerBackground::Level(level) => EffectiveBackground::Level(level),
@ -206,7 +206,7 @@ impl Widget for Container {
.finish() .finish()
} }
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.child.unmount_in(context); self.child.unmount_in(context);
} }
@ -215,7 +215,7 @@ impl Widget for Container {
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let opacity = context.get(&Opacity); let opacity = context.get(&Opacity);
let background = self.effective_background_color(context); let background = self.effective_background_color(context);
@ -251,7 +251,7 @@ impl Widget for Container {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let child = self.child.mounted(context); let child = self.child.mounted(context);
@ -308,7 +308,7 @@ impl Widget for Container {
fn root_behavior( fn root_behavior(
&mut self, &mut self,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> { ) -> Option<(RootBehavior, WidgetInstance)> {
let mut padding = self let mut padding = self
.padding .padding
@ -345,7 +345,7 @@ fn render_shadow(
mut corner_radii: CornerRadii<Px>, mut corner_radii: CornerRadii<Px>,
shadow: &ContainerShadow<Px>, shadow: &ContainerShadow<Px>,
background: Color, background: Color,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
) { ) {
let shadow_color = shadow.color.unwrap_or_else(|| context.theme_pair().shadow); let shadow_color = shadow.color.unwrap_or_else(|| context.theme_pair().shadow);
let shadow_color = let shadow_color =
@ -588,7 +588,7 @@ fn shadow_arc(
solid_color: Color, solid_color: Color,
transparent_color: Color, transparent_color: Color,
start_angle: Angle, start_angle: Angle,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
) { ) {
let full_radius = radius + gradient; let full_radius = radius + gradient;
let mut current_outer_arc = origin + Point::new(full_radius, Px::ZERO).rotate_by(start_angle); let mut current_outer_arc = origin + Point::new(full_radius, Px::ZERO).rotate_by(start_angle);

View file

@ -2,9 +2,7 @@ use std::fmt::Debug;
use figures::units::Px; use figures::units::Px;
use figures::{Point, Size}; use figures::{Point, Size};
use kludgine::app::winit::event::{ use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::Color; use kludgine::Color;
@ -13,6 +11,7 @@ use crate::styles::VisualOrder;
use crate::value::{IntoValue, Value}; use crate::value::{IntoValue, Value};
use crate::widget::{EventHandling, MakeWidget, WidgetRef, WrappedLayout, WrapperWidget, IGNORED}; use crate::widget::{EventHandling, MakeWidget, WidgetRef, WrappedLayout, WrapperWidget, IGNORED};
use crate::widgets::Space; use crate::widgets::Space;
use crate::window::DeviceId;
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A callback-based custom widget. /// A callback-based custom widget.
@ -121,8 +120,8 @@ impl Custom {
where where
Redraw: Send Redraw: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut GraphicsContext<'context, 'clip, 'gfx, 'pass>,
), ),
{ {
self.redraw_background = Some(Box::new(redraw)); self.redraw_background = Some(Box::new(redraw));
@ -143,8 +142,8 @@ impl Custom {
where where
Redraw: Send Redraw: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut GraphicsContext<'context, 'clip, 'gfx, 'pass>,
), ),
{ {
self.redraw_foreground = Some(Box::new(redraw)); self.redraw_foreground = Some(Box::new(redraw));
@ -156,8 +155,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::mounted`]. /// This callback corresponds to [`WrapperWidget::mounted`].
pub fn on_mounted<Mounted>(mut self, mounted: Mounted) -> Self pub fn on_mounted<Mounted>(mut self, mounted: Mounted) -> Self
where where
Mounted: Mounted: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.mounted = Some(Box::new(mounted)); self.mounted = Some(Box::new(mounted));
self self
@ -169,8 +167,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::unmounted`]. /// This callback corresponds to [`WrapperWidget::unmounted`].
pub fn on_unmounted<Mounted>(mut self, mounted: Mounted) -> Self pub fn on_unmounted<Mounted>(mut self, mounted: Mounted) -> Self
where where
Mounted: Mounted: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.unmounted = Some(Box::new(mounted)); self.unmounted = Some(Box::new(mounted));
self self
@ -181,8 +178,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::unhover`]. /// This callback corresponds to [`WrapperWidget::unhover`].
pub fn on_unhover<Unhover>(mut self, unhovered: Unhover) -> Self pub fn on_unhover<Unhover>(mut self, unhovered: Unhover) -> Self
where where
Unhover: Unhover: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.unhover = Some(Box::new(unhovered)); self.unhover = Some(Box::new(unhovered));
self self
@ -193,8 +189,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::focus`]. /// This callback corresponds to [`WrapperWidget::focus`].
pub fn on_focus<Focused>(mut self, focus: Focused) -> Self pub fn on_focus<Focused>(mut self, focus: Focused) -> Self
where where
Focused: Focused: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.focus = Some(Box::new(focus)); self.focus = Some(Box::new(focus));
self self
@ -205,7 +200,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::blur`]. /// This callback corresponds to [`WrapperWidget::blur`].
pub fn on_blur<Blur>(mut self, blur: Blur) -> Self pub fn on_blur<Blur>(mut self, blur: Blur) -> Self
where where
Blur: Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>), Blur: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{ {
self.blur = Some(Box::new(blur)); self.blur = Some(Box::new(blur));
self self
@ -216,8 +211,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::activate`]. /// This callback corresponds to [`WrapperWidget::activate`].
pub fn on_activate<Activated>(mut self, activated: Activated) -> Self pub fn on_activate<Activated>(mut self, activated: Activated) -> Self
where where
Activated: Activated: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.activate = Some(Box::new(activated)); self.activate = Some(Box::new(activated));
self self
@ -228,8 +222,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::deactivate`]. /// This callback corresponds to [`WrapperWidget::deactivate`].
pub fn on_deactivate<Deactivated>(mut self, deactivated: Deactivated) -> Self pub fn on_deactivate<Deactivated>(mut self, deactivated: Deactivated) -> Self
where where
Deactivated: Deactivated: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
{ {
self.deactivate = Some(Box::new(deactivated)); self.deactivate = Some(Box::new(deactivated));
self self
@ -241,9 +234,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::accept_focus`]. /// This callback corresponds to [`WrapperWidget::accept_focus`].
pub fn on_accept_focus<AcceptFocus>(mut self, accept: AcceptFocus) -> Self pub fn on_accept_focus<AcceptFocus>(mut self, accept: AcceptFocus) -> Self
where where
AcceptFocus: Send AcceptFocus: Send + 'static + for<'context> FnMut(&mut EventContext<'context>) -> bool,
+ 'static
+ for<'context, 'window> FnMut(&mut EventContext<'context, 'window>) -> bool,
{ {
self.accept_focus = Some(Box::new(accept)); self.accept_focus = Some(Box::new(accept));
self self
@ -256,9 +247,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::allow_blur`]. /// This callback corresponds to [`WrapperWidget::allow_blur`].
pub fn on_allow_blur<AllowBlur>(mut self, allow_blur: AllowBlur) -> Self pub fn on_allow_blur<AllowBlur>(mut self, allow_blur: AllowBlur) -> Self
where where
AllowBlur: Send AllowBlur: Send + 'static + for<'context> FnMut(&mut EventContext<'context>) -> bool,
+ 'static
+ for<'context, 'window> FnMut(&mut EventContext<'context, 'window>) -> bool,
{ {
self.allow_blur = Some(Box::new(allow_blur)); self.allow_blur = Some(Box::new(allow_blur));
self self
@ -274,10 +263,7 @@ impl Custom {
where where
AdvanceFocus: Send AdvanceFocus: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(VisualOrder, &mut EventContext<'context>) -> EventHandling,
VisualOrder,
&mut EventContext<'context, 'window>,
) -> EventHandling,
{ {
self.advance_focus = Some(Box::new(advance_focus)); self.advance_focus = Some(Box::new(advance_focus));
self self
@ -295,9 +281,9 @@ impl Custom {
where where
AdjustChildConstraints: Send AdjustChildConstraints: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<ConstraintLimit>, Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> Size<ConstraintLimit>, ) -> Size<ConstraintLimit>,
{ {
self.adjust_child = Some(Box::new(adjust_child_constraints)); self.adjust_child = Some(Box::new(adjust_child_constraints));
@ -311,10 +297,10 @@ impl Custom {
where where
PositionChild: Send PositionChild: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<Px>, Size<Px>,
Size<ConstraintLimit>, Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> WrappedLayout, ) -> WrappedLayout,
{ {
self.position_child = Some(Box::new(position_child)); self.position_child = Some(Box::new(position_child));
@ -327,9 +313,8 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::hit_test`]. /// This callback corresponds to [`WrapperWidget::hit_test`].
pub fn on_hit_test<HitTest>(mut self, hit_test: HitTest) -> Self pub fn on_hit_test<HitTest>(mut self, hit_test: HitTest) -> Self
where where
HitTest: Send HitTest:
+ 'static Send + 'static + for<'context> FnMut(Point<Px>, &mut EventContext<'context>) -> bool,
+ for<'context, 'window> FnMut(Point<Px>, &mut EventContext<'context, 'window>) -> bool,
{ {
self.hit_test = Some(Box::new(hit_test)); self.hit_test = Some(Box::new(hit_test));
self self
@ -342,10 +327,7 @@ impl Custom {
where where
Hover: Send Hover: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(Point<Px>, &mut EventContext<'context>) -> Option<CursorIcon>,
Point<Px>,
&mut EventContext<'context, 'window>,
) -> Option<CursorIcon>,
{ {
self.hover = Some(Box::new(hover)); self.hover = Some(Box::new(hover));
self self
@ -364,11 +346,11 @@ impl Custom {
where where
MouseDown: Send MouseDown: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(
Point<Px>, Point<Px>,
DeviceId, DeviceId,
MouseButton, MouseButton,
&mut EventContext<'context, 'window>, &mut EventContext<'context>,
) -> EventHandling, ) -> EventHandling,
{ {
self.mouse_down = Some(Box::new(mouse_down)); self.mouse_down = Some(Box::new(mouse_down));
@ -383,12 +365,7 @@ impl Custom {
where where
MouseDrag: Send MouseDrag: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(Point<Px>, DeviceId, MouseButton, &mut EventContext<'context>),
Point<Px>,
DeviceId,
MouseButton,
&mut EventContext<'context, 'window>,
),
{ {
self.mouse_drag = Some(Box::new(mouse_drag)); self.mouse_drag = Some(Box::new(mouse_drag));
self self
@ -401,11 +378,11 @@ impl Custom {
where where
MouseUp: Send MouseUp: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(
Option<Point<Px>>, Option<Point<Px>>,
DeviceId, DeviceId,
MouseButton, MouseButton,
&mut EventContext<'context, 'window>, &mut EventContext<'context>,
), ),
{ {
self.mouse_up = Some(Box::new(mouse_up)); self.mouse_up = Some(Box::new(mouse_up));
@ -417,9 +394,8 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::ime`]. /// This callback corresponds to [`WrapperWidget::ime`].
pub fn on_ime<OnIme>(mut self, ime: OnIme) -> Self pub fn on_ime<OnIme>(mut self, ime: OnIme) -> Self
where where
OnIme: Send OnIme:
+ 'static Send + 'static + for<'context> FnMut(Ime, &mut EventContext<'context>) -> EventHandling,
+ for<'context, 'window> FnMut(Ime, &mut EventContext<'context, 'window>) -> EventHandling,
{ {
self.ime = Some(Box::new(ime)); self.ime = Some(Box::new(ime));
self self
@ -432,11 +408,11 @@ impl Custom {
where where
KeyboardInput: Send KeyboardInput: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(
DeviceId, DeviceId,
KeyEvent, KeyEvent,
bool, bool,
&mut EventContext<'context, 'window>, &mut EventContext<'context>,
) -> EventHandling, ) -> EventHandling,
{ {
self.keyboard_input = Some(Box::new(keyboard_input)); self.keyboard_input = Some(Box::new(keyboard_input));
@ -450,11 +426,11 @@ impl Custom {
where where
MouseWheel: Send MouseWheel: Send
+ 'static + 'static
+ for<'context, 'window> FnMut( + for<'context> FnMut(
DeviceId, DeviceId,
MouseScrollDelta, MouseScrollDelta,
TouchPhase, TouchPhase,
&mut EventContext<'context, 'window>, &mut EventContext<'context>,
) -> EventHandling, ) -> EventHandling,
{ {
self.mouse_wheel = Some(Box::new(mouse_wheel)); self.mouse_wheel = Some(Box::new(mouse_wheel));
@ -467,13 +443,13 @@ impl WrapperWidget for Custom {
&mut self.child &mut self.child
} }
fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw_background(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
if let Some(redraw) = &mut self.redraw_background { if let Some(redraw) = &mut self.redraw_background {
redraw.invoke(context); redraw.invoke(context);
} }
} }
fn redraw_foreground(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw_foreground(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
if let Some(redraw) = &mut self.redraw_foreground { if let Some(redraw) = &mut self.redraw_foreground {
redraw.invoke(context); redraw.invoke(context);
} }
@ -482,7 +458,7 @@ impl WrapperWidget for Custom {
fn adjust_child_constraints( fn adjust_child_constraints(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> { ) -> Size<ConstraintLimit> {
if let Some(adjust_child) = &mut self.adjust_child { if let Some(adjust_child) = &mut self.adjust_child {
adjust_child.invoke(available_space, context) adjust_child.invoke(available_space, context)
@ -495,7 +471,7 @@ impl WrapperWidget for Custom {
&mut self, &mut self,
size: Size<Px>, size: Size<Px>,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
if let Some(position_child) = &mut self.position_child { if let Some(position_child) = &mut self.position_child {
position_child.invoke(size, available_space, context) position_child.invoke(size, available_space, context)
@ -512,19 +488,19 @@ impl WrapperWidget for Custom {
} }
} }
fn background_color(&mut self, context: &WidgetContext<'_, '_>) -> Option<Color> { fn background_color(&mut self, context: &WidgetContext<'_>) -> Option<Color> {
self.background self.background
.as_ref() .as_ref()
.map(|bg| bg.get_tracking_redraw(context)) .map(|bg| bg.get_tracking_redraw(context))
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
if let Some(mounted) = &mut self.mounted { if let Some(mounted) = &mut self.mounted {
mounted.invoke(context); mounted.invoke(context);
} }
} }
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
if let Some(unmounted) = &mut self.unmounted { if let Some(unmounted) = &mut self.unmounted {
unmounted.invoke(context); unmounted.invoke(context);
} else { } else {
@ -532,7 +508,7 @@ impl WrapperWidget for Custom {
} }
} }
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
if let Some(hit_test) = &mut self.hit_test { if let Some(hit_test) = &mut self.hit_test {
hit_test.invoke(location, context) hit_test.invoke(location, context)
} else { } else {
@ -540,22 +516,18 @@ impl WrapperWidget for Custom {
} }
} }
fn hover( fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
let hover = self.hover.as_mut()?; let hover = self.hover.as_mut()?;
hover.invoke(location, context) hover.invoke(location, context)
} }
fn unhover(&mut self, context: &mut EventContext<'_, '_>) { fn unhover(&mut self, context: &mut EventContext<'_>) {
if let Some(unhover) = &mut self.unhover { if let Some(unhover) = &mut self.unhover {
unhover.invoke(context); unhover.invoke(context);
} }
} }
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
if let Some(accept_focus) = &mut self.accept_focus { if let Some(accept_focus) = &mut self.accept_focus {
accept_focus.invoke(context) accept_focus.invoke(context)
} else { } else {
@ -563,25 +535,25 @@ impl WrapperWidget for Custom {
} }
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
if let Some(focus) = &mut self.focus { if let Some(focus) = &mut self.focus {
focus.invoke(context); focus.invoke(context);
} }
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
if let Some(blur) = &mut self.blur { if let Some(blur) = &mut self.blur {
blur.invoke(context); blur.invoke(context);
} }
} }
fn activate(&mut self, context: &mut EventContext<'_, '_>) { fn activate(&mut self, context: &mut EventContext<'_>) {
if let Some(activate) = &mut self.activate { if let Some(activate) = &mut self.activate {
activate.invoke(context); activate.invoke(context);
} }
} }
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) { fn deactivate(&mut self, context: &mut EventContext<'_>) {
if let Some(deactivate) = &mut self.deactivate { if let Some(deactivate) = &mut self.deactivate {
deactivate.invoke(context); deactivate.invoke(context);
} }
@ -592,7 +564,7 @@ impl WrapperWidget for Custom {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(mouse_down) = &mut self.mouse_down { if let Some(mouse_down) = &mut self.mouse_down {
mouse_down.invoke(location, device_id, button, context) mouse_down.invoke(location, device_id, button, context)
@ -606,7 +578,7 @@ impl WrapperWidget for Custom {
location: Point<Px>, location: Point<Px>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
if let Some(mouse_drag) = &mut self.mouse_drag { if let Some(mouse_drag) = &mut self.mouse_drag {
mouse_drag.invoke(location, device_id, button, context); mouse_drag.invoke(location, device_id, button, context);
@ -618,7 +590,7 @@ impl WrapperWidget for Custom {
location: Option<Point<Px>>, location: Option<Point<Px>>,
device_id: DeviceId, device_id: DeviceId,
button: MouseButton, button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
if let Some(mouse_up) = &mut self.mouse_up { if let Some(mouse_up) = &mut self.mouse_up {
mouse_up.invoke(location, device_id, button, context); mouse_up.invoke(location, device_id, button, context);
@ -630,7 +602,7 @@ impl WrapperWidget for Custom {
device_id: DeviceId, device_id: DeviceId,
input: KeyEvent, input: KeyEvent,
is_synthetic: bool, is_synthetic: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(keyboard_input) = &mut self.keyboard_input { if let Some(keyboard_input) = &mut self.keyboard_input {
keyboard_input.invoke(device_id, input, is_synthetic, context) keyboard_input.invoke(device_id, input, is_synthetic, context)
@ -639,7 +611,7 @@ impl WrapperWidget for Custom {
} }
} }
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
if let Some(f) = &mut self.ime { if let Some(f) = &mut self.ime {
f.invoke(ime, context) f.invoke(ime, context)
} else { } else {
@ -652,7 +624,7 @@ impl WrapperWidget for Custom {
device_id: DeviceId, device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
phase: TouchPhase, phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(mouse_wheel) = &mut self.mouse_wheel { if let Some(mouse_wheel) = &mut self.mouse_wheel {
mouse_wheel.invoke(device_id, delta, phase, context) mouse_wheel.invoke(device_id, delta, phase, context)
@ -664,7 +636,7 @@ impl WrapperWidget for Custom {
fn advance_focus( fn advance_focus(
&mut self, &mut self,
direction: VisualOrder, direction: VisualOrder,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(advance_focus) = &mut self.advance_focus { if let Some(advance_focus) = &mut self.advance_focus {
advance_focus.invoke(direction, context) advance_focus.invoke(direction, context)
@ -673,7 +645,7 @@ impl WrapperWidget for Custom {
} }
} }
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
if let Some(allow_blur) = &mut self.allow_blur { if let Some(allow_blur) = &mut self.allow_blur {
allow_blur.invoke(context) allow_blur.invoke(context)
} else { } else {
@ -683,18 +655,16 @@ impl WrapperWidget for Custom {
} }
trait RedrawFunc: Send { trait RedrawFunc: Send {
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>);
} }
impl<Func> RedrawFunc for Func impl<Func> RedrawFunc for Func
where where
Func: Send Func: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(&mut GraphicsContext<'context, 'clip, 'gfx, 'pass>),
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
),
{ {
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
self(context); self(context);
} }
} }
@ -703,7 +673,7 @@ trait AdjustChildConstraintsFunc: Send {
fn invoke( fn invoke(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit>; ) -> Size<ConstraintLimit>;
} }
@ -711,15 +681,15 @@ impl<Func> AdjustChildConstraintsFunc for Func
where where
Func: Send Func: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<ConstraintLimit>, Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> Size<ConstraintLimit>, ) -> Size<ConstraintLimit>,
{ {
fn invoke( fn invoke(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> { ) -> Size<ConstraintLimit> {
self(available_space, context) self(available_space, context)
} }
@ -730,7 +700,7 @@ trait PositionChildFunc: Send {
&mut self, &mut self,
size: Size<Px>, size: Size<Px>,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout; ) -> WrappedLayout;
} }
@ -738,45 +708,44 @@ impl<Func> PositionChildFunc for Func
where where
Func: Send Func: Send
+ 'static + 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<Px>, Size<Px>,
Size<ConstraintLimit>, Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, &mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> WrappedLayout, ) -> WrappedLayout,
{ {
fn invoke( fn invoke(
&mut self, &mut self,
size: Size<Px>, size: Size<Px>,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
self(size, available_space, context) self(size, available_space, context)
} }
} }
trait EventFunc<R = ()>: Send { trait EventFunc<R = ()>: Send {
fn invoke(&mut self, context: &mut EventContext<'_, '_>) -> R; fn invoke(&mut self, context: &mut EventContext<'_>) -> R;
} }
impl<R, Func> EventFunc<R> for Func impl<R, Func> EventFunc<R> for Func
where where
Func: Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>) -> R, Func: Send + 'static + for<'context> FnMut(&mut EventContext<'context>) -> R,
{ {
fn invoke(&mut self, context: &mut EventContext<'_, '_>) -> R { fn invoke(&mut self, context: &mut EventContext<'_>) -> R {
self(context) self(context)
} }
} }
trait OneParamEventFunc<P, R = ()>: Send { trait OneParamEventFunc<P, R = ()>: Send {
fn invoke(&mut self, param: P, context: &mut EventContext<'_, '_>) -> R; fn invoke(&mut self, param: P, context: &mut EventContext<'_>) -> R;
} }
impl<P, R, Func> OneParamEventFunc<P, R> for Func impl<P, R, Func> OneParamEventFunc<P, R> for Func
where where
Func: Func: Send + 'static + for<'context> FnMut(P, &mut EventContext<'context>) -> R,
Send + 'static + for<'context, 'window> FnMut(P, &mut EventContext<'context, 'window>) -> R,
{ {
fn invoke(&mut self, location: P, context: &mut EventContext<'_, '_>) -> R { fn invoke(&mut self, location: P, context: &mut EventContext<'_>) -> R {
self(location, context) self(location, context)
} }
} }
@ -787,7 +756,7 @@ trait ThreeParamEventFunc<P1, P2, P3, R = ()>: Send {
location: P1, location: P1,
device_id: P2, device_id: P2,
button: P3, button: P3,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> R; ) -> R;
} }
@ -795,16 +764,14 @@ type MouseUpFunc = dyn ThreeParamEventFunc<Option<Point<Px>>, DeviceId, MouseBut
impl<P1, P2, P3, R, Func> ThreeParamEventFunc<P1, P2, P3, R> for Func impl<P1, P2, P3, R, Func> ThreeParamEventFunc<P1, P2, P3, R> for Func
where where
Func: Send Func: Send + 'static + for<'context> FnMut(P1, P2, P3, &mut EventContext<'context>) -> R,
+ 'static
+ for<'context, 'window> FnMut(P1, P2, P3, &mut EventContext<'context, 'window>) -> R,
{ {
fn invoke( fn invoke(
&mut self, &mut self,
location: P1, location: P1,
device_id: P2, device_id: P2,
button: P3, button: P3,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> R { ) -> R {
self(location, device_id, button, context) self(location, device_id, button, context)
} }

View file

@ -18,6 +18,7 @@ use crate::widget::{
EventHandling, MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetRef, WidgetTag, EventHandling, MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetRef, WidgetTag,
HANDLED, IGNORED, HANDLED, IGNORED,
}; };
use crate::window::DeviceId;
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A widget that hides and shows another widget. /// A widget that hides and shows another widget.
@ -122,7 +123,7 @@ impl DiscloseIndicator {
fn effective_colors( fn effective_colors(
&mut self, &mut self,
context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>,
) -> (Color, Color) { ) -> (Color, Color) {
let current_color = if context.active() { let current_color = if context.active() {
context.get(&ButtonActiveBackground) context.get(&ButtonActiveBackground)
@ -161,14 +162,14 @@ impl DiscloseIndicator {
} }
impl Widget for DiscloseIndicator { impl Widget for DiscloseIndicator {
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
if let Some(label) = &mut self.label { if let Some(label) = &mut self.label {
label.unmount_in(context); label.unmount_in(context);
} }
self.contents.unmount_in(context); self.contents.unmount_in(context);
} }
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
let angle = self.angle.get_tracking_redraw(context); let angle = self.angle.get_tracking_redraw(context);
let (color, stroke_color) = self.effective_colors(context); let (color, stroke_color) = self.effective_colors(context);
let size = context let size = context
@ -217,7 +218,7 @@ impl Widget for DiscloseIndicator {
fn layout( fn layout(
&mut self, &mut self,
mut available_space: Size<ConstraintLimit>, mut available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let indicator_size = context let indicator_size = context
.get(&IndicatorSize) .get(&IndicatorSize)
@ -270,19 +271,19 @@ impl Widget for DiscloseIndicator {
) )
} }
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
context.set_needs_redraw(); context.set_needs_redraw();
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
context.set_needs_redraw(); context.set_needs_redraw();
} }
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
let size = context let size = context
.get(&IndicatorSize) .get(&IndicatorSize)
.into_px(context.kludgine.scale()) .into_px(context.kludgine.scale())
@ -296,11 +297,7 @@ impl Widget for DiscloseIndicator {
} }
} }
fn hover( fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
let hovering = self.hit_test(location, context); let hovering = self.hit_test(location, context);
if self.hovering_indicator != hovering { if self.hovering_indicator != hovering {
context.set_needs_redraw(); context.set_needs_redraw();
@ -310,7 +307,7 @@ impl Widget for DiscloseIndicator {
hovering.then_some(CursorIcon::Pointer) hovering.then_some(CursorIcon::Pointer)
} }
fn unhover(&mut self, context: &mut EventContext<'_, '_>) { fn unhover(&mut self, context: &mut EventContext<'_>) {
if self.hovering_indicator { if self.hovering_indicator {
self.hovering_indicator = false; self.hovering_indicator = false;
context.set_needs_redraw(); context.set_needs_redraw();
@ -320,9 +317,9 @@ impl Widget for DiscloseIndicator {
fn mouse_down( fn mouse_down(
&mut self, &mut self,
location: Point<Px>, location: Point<Px>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if self.hit_test(location, context) { if self.hit_test(location, context) {
self.mouse_buttons_pressed += 1; self.mouse_buttons_pressed += 1;
@ -337,9 +334,9 @@ impl Widget for DiscloseIndicator {
fn mouse_up( fn mouse_up(
&mut self, &mut self,
_location: Option<Point<Px>>, _location: Option<Point<Px>>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
self.mouse_buttons_pressed -= 1; self.mouse_buttons_pressed -= 1;
if self.mouse_buttons_pressed == 0 { if self.mouse_buttons_pressed == 0 {
@ -348,7 +345,7 @@ impl Widget for DiscloseIndicator {
} }
} }
fn activate(&mut self, _context: &mut EventContext<'_, '_>) { fn activate(&mut self, _context: &mut EventContext<'_>) {
if self.mouse_buttons_pressed == 0 { if self.mouse_buttons_pressed == 0 {
self.collapsed.toggle(); self.collapsed.toggle();
} }

View file

@ -98,14 +98,14 @@ impl WrapperWidget for Expand {
&mut self.child &mut self.child
} }
fn root_behavior(&mut self, _context: &mut EventContext<'_, '_>) -> Option<RootBehavior> { fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option<RootBehavior> {
Some(RootBehavior::Expand) Some(RootBehavior::Expand)
} }
fn layout_child( fn layout_child(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
let available_space = available_space.map(|lim| ConstraintLimit::Fill(lim.max())); let available_space = available_space.map(|lim| ConstraintLimit::Fill(lim.max()));
let child = self.child.mounted(&mut context.as_event_context()); let child = self.child.mounted(&mut context.as_event_context());

View file

@ -67,7 +67,7 @@ impl<const ELEMENTS: usize> Grid<ELEMENTS> {
self self
} }
fn synchronize_specs(&mut self, context: &mut EventContext<'_, '_>) { fn synchronize_specs(&mut self, context: &mut EventContext<'_>) {
let current_generation = self.columns.generation(); let current_generation = self.columns.generation();
let count_changed = self.layout.children.len() != ELEMENTS; let count_changed = self.layout.children.len() != ELEMENTS;
if count_changed if count_changed
@ -84,7 +84,7 @@ impl<const ELEMENTS: usize> Grid<ELEMENTS> {
} }
} }
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) { fn synchronize_children(&mut self, context: &mut EventContext<'_>) {
self.synchronize_specs(context); self.synchronize_specs(context);
let current_generation = self.rows.generation(); let current_generation = self.rows.generation();
self.rows.invalidate_when_changed(context); self.rows.invalidate_when_changed(context);
@ -132,7 +132,7 @@ impl<const ELEMENTS: usize> Grid<ELEMENTS> {
} }
impl<const COLUMNS: usize> Widget for Grid<COLUMNS> { impl<const COLUMNS: usize> Widget for Grid<COLUMNS> {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
for (row, widgets) in self.live_rows.iter_mut().enumerate() { for (row, widgets) in self.live_rows.iter_mut().enumerate() {
if self.layout.others[row] > 0 { if self.layout.others[row] > 0 {
for (column, cell) in widgets.iter().enumerate() { for (column, cell) in widgets.iter().enumerate() {
@ -147,7 +147,7 @@ impl<const COLUMNS: usize> Widget for Grid<COLUMNS> {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
self.synchronize_children(&mut context.as_event_context()); self.synchronize_children(&mut context.as_event_context());

View file

@ -112,7 +112,7 @@ impl Image {
} }
impl Widget for Image { impl Widget for Image {
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
self.contents.map(|texture| { self.contents.map(|texture| {
let size = texture.size().into_signed(); let size = texture.size().into_signed();
let rect = match self.scaling.get() { let rect = match self.scaling.get() {
@ -147,7 +147,7 @@ impl Widget for Image {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
match self.scaling.get_tracking_invalidate(context) { match self.scaling.get_tracking_invalidate(context) {
ImageScaling::Aspect { .. } | ImageScaling::Stretch => { ImageScaling::Aspect { .. } | ImageScaling::Stretch => {

View file

@ -166,7 +166,7 @@ where
}); });
} }
fn forward_delete(&mut self, context: &mut EventContext<'_, '_>) { fn forward_delete(&mut self, context: &mut EventContext<'_>) {
if !context.enabled() { if !context.enabled() {
return; return;
} }
@ -200,7 +200,7 @@ where
}); });
} }
fn delete(&mut self, context: &mut EventContext<'_, '_>) { fn delete(&mut self, context: &mut EventContext<'_>) {
if !context.enabled() { if !context.enabled() {
return; return;
} }
@ -231,7 +231,7 @@ where
&mut self, &mut self,
direction: Affinity, direction: Affinity,
mode: CursorNavigationMode, mode: CursorNavigationMode,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
if !matches!(mode, CursorNavigationMode::Line) { if !matches!(mode, CursorNavigationMode::Line) {
self.line_navigation_x_target = None; self.line_navigation_x_target = None;
@ -315,11 +315,7 @@ where
} }
} }
fn move_cursor_by_line_extent( fn move_cursor_by_line_extent(&mut self, affinity: Affinity, context: &mut EventContext<'_>) {
&mut self,
affinity: Affinity,
context: &mut EventContext<'_, '_>,
) {
let Some(cache) = self.cache.as_ref() else { let Some(cache) = self.cache.as_ref() else {
return; return;
}; };
@ -339,7 +335,7 @@ where
self.selection.cursor = self.cursor_from_point(position, context); self.selection.cursor = self.cursor_from_point(position, context);
} }
fn move_cursor_by_line(&mut self, affinity: Affinity, context: &mut EventContext<'_, '_>) { fn move_cursor_by_line(&mut self, affinity: Affinity, context: &mut EventContext<'_>) {
let Some(cache) = self.cache.as_ref() else { let Some(cache) = self.cache.as_ref() else {
return; return;
}; };
@ -406,13 +402,13 @@ where
self.mask_symbol.map(|mask| !mask.is_empty()) self.mask_symbol.map(|mask| !mask.is_empty())
} }
fn copy_selection_to_clipboard(&mut self, context: &mut EventContext<'_, '_>) { fn copy_selection_to_clipboard(&mut self, context: &mut EventContext<'_>) {
if self.is_masked() { if self.is_masked() {
return; return;
} }
self.map_selected_text(|text| { self.map_selected_text(|text| {
if let Some(mut clipboard) = context.clipboard_guard() { if let Some(mut clipboard) = context.cushy().clipboard_guard() {
match clipboard.set_text(text) { match clipboard.set_text(text) {
Ok(()) => {} Ok(()) => {}
Err(err) => tracing::error!("error copying to clipboard: {err}"), Err(err) => tracing::error!("error copying to clipboard: {err}"),
@ -421,7 +417,7 @@ where
}); });
} }
fn replace_selection(&mut self, new_text: &str, context: &mut EventContext<'_, '_>) { fn replace_selection(&mut self, new_text: &str, context: &mut EventContext<'_>) {
if !context.enabled() { if !context.enabled() {
return; return;
} }
@ -444,12 +440,13 @@ where
}; };
} }
fn paste_from_clipboard(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn paste_from_clipboard(&mut self, context: &mut EventContext<'_>) -> bool {
if !context.enabled() { if !context.enabled() {
return false; return false;
} }
match context match context
.cushy()
.clipboard_guard() .clipboard_guard()
.map(|mut clipboard| clipboard.get_text()) .map(|mut clipboard| clipboard.get_text())
{ {
@ -465,7 +462,7 @@ where
} }
} }
fn handle_key(&mut self, input: KeyEvent, context: &mut EventContext<'_, '_>) -> EventHandling { fn handle_key(&mut self, input: KeyEvent, context: &mut EventContext<'_>) -> EventHandling {
match (input.state, input.logical_key, input.text.as_deref()) { match (input.state, input.logical_key, input.text.as_deref()) {
(ElementState::Pressed, Key::Named(key @ (NamedKey::Backspace| NamedKey::Delete)), _) => { (ElementState::Pressed, Key::Named(key @ (NamedKey::Backspace| NamedKey::Delete)), _) => {
match key { match key {
@ -547,11 +544,7 @@ where
} }
} }
fn layout_text( fn layout_text(&mut self, width: Option<Px>, context: &mut GraphicsContext<'_, '_, '_, '_>) {
&mut self,
width: Option<Px>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) {
context.invalidate_when_changed(&self.value); context.invalidate_when_changed(&self.value);
let mut key = { let mut key = {
@ -808,11 +801,7 @@ where
} }
} }
fn cursor_from_point( fn cursor_from_point(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Cursor {
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Cursor {
let mut cursor = self.cached_cursor_from_point(location, context); let mut cursor = self.cached_cursor_from_point(location, context);
if let Some(symbol) = self.mask.graphemes(true).next() { if let Some(symbol) = self.mask.graphemes(true).next() {
let grapheme_offset = cursor.offset / symbol.len(); let grapheme_offset = cursor.offset / symbol.len();
@ -831,7 +820,7 @@ where
fn cached_cursor_from_point( fn cached_cursor_from_point(
&mut self, &mut self,
location: Point<Px>, location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Cursor { ) -> Cursor {
let Some(cache) = &self.cache else { let Some(cache) = &self.cache else {
return Cursor::default(); return Cursor::default();
@ -973,20 +962,20 @@ impl<Storage> Widget for Input<Storage>
where where
Storage: InputStorage + Debug, Storage: InputStorage + Debug,
{ {
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn mouse_down( fn mouse_down(
&mut self, &mut self,
location: Point<Px>, location: Point<Px>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
self.mouse_buttons_down += 1; self.mouse_buttons_down += 1;
context.focus(); context.focus();
@ -1000,7 +989,7 @@ where
fn hover( fn hover(
&mut self, &mut self,
_location: Point<Px>, _location: Point<Px>,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) -> Option<CursorIcon> { ) -> Option<CursorIcon> {
Some(CursorIcon::Text) Some(CursorIcon::Text)
} }
@ -1008,9 +997,9 @@ where
fn mouse_drag( fn mouse_drag(
&mut self, &mut self,
location: Point<Px>, location: Point<Px>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
let cursor_location = self.cursor_from_point(location, context); let cursor_location = self.cursor_from_point(location, context);
if self.selection.cursor != cursor_location { if self.selection.cursor != cursor_location {
@ -1023,15 +1012,15 @@ where
fn mouse_up( fn mouse_up(
&mut self, &mut self,
_location: Option<Point<Px>>, _location: Option<Point<Px>>,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) { ) {
self.mouse_buttons_down -= 1; self.mouse_buttons_down -= 1;
} }
#[allow(clippy::too_many_lines)] #[allow(clippy::too_many_lines)]
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
if self.needs_to_select_all { if self.needs_to_select_all {
self.needs_to_select_all = false; self.needs_to_select_all = false;
self.select_all(); self.select_all();
@ -1177,7 +1166,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let padding = context let padding = context
.get(&IntrinsicPadding) .get(&IntrinsicPadding)
@ -1199,10 +1188,10 @@ where
fn keyboard_input( fn keyboard_input(
&mut self, &mut self,
_device_id: kludgine::app::winit::event::DeviceId, _device_id: crate::window::DeviceId,
input: kludgine::app::winit::event::KeyEvent, input: kludgine::app::winit::event::KeyEvent,
_is_synthetic: bool, _is_synthetic: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(on_key) = &mut self.on_key { if let Some(on_key) = &mut self.on_key {
on_key.invoke(input.clone())?; on_key.invoke(input.clone())?;
@ -1219,7 +1208,7 @@ where
handled handled
} }
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling { fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
match ime { match ime {
Ime::Enabled | Ime::Disabled => {} Ime::Enabled | Ime::Disabled => {}
Ime::Preedit(text, cursor) => { Ime::Preedit(text, cursor) => {
@ -1234,7 +1223,7 @@ where
HANDLED HANDLED
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
if self.mouse_buttons_down == 0 { if self.mouse_buttons_down == 0 {
self.needs_to_select_all = true; self.needs_to_select_all = true;
} }
@ -1248,7 +1237,7 @@ where
context.set_needs_redraw(); context.set_needs_redraw();
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
context.set_ime_allowed(false); context.set_ime_allowed(false);
context.set_needs_redraw(); context.set_needs_redraw();
} }

View file

@ -38,7 +38,7 @@ where
fn prepared_text( fn prepared_text(
&mut self, &mut self,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
color: Color, color: Color,
width: Px, width: Px,
) -> &MeasuredText<Px> { ) -> &MeasuredText<Px> {
@ -76,7 +76,7 @@ impl<T> Widget for Label<T>
where where
T: std::fmt::Debug + std::fmt::Display + Send + 'static, T: std::fmt::Debug + std::fmt::Display + Send + 'static,
{ {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
self.display.invalidate_when_changed(context); self.display.invalidate_when_changed(context);
let size = context.gfx.region().size; let size = context.gfx.region().size;
@ -94,7 +94,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let color = context.get(&TextColor); let color = context.get(&TextColor);
let width = available_space.width.max().try_into().unwrap_or(Px::MAX); let width = available_space.width.max().try_into().unwrap_or(Px::MAX);
@ -107,7 +107,7 @@ where
fmt.debug_tuple("Label").field(&self.display).finish() fmt.debug_tuple("Label").field(&self.display).finish()
} }
fn unmounted(&mut self, context: &mut crate::context::EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut crate::context::EventContext<'_>) {
self.prepared_text.clear_for(context); self.prepared_text.clear_for(context);
} }
} }

View file

@ -39,14 +39,14 @@ impl Layers {
} }
} }
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) { fn synchronize_children(&mut self, context: &mut EventContext<'_>) {
self.children.invalidate_when_changed(context); self.children.invalidate_when_changed(context);
self.mounted.synchronize_with(&self.children, context); self.mounted.synchronize_with(&self.children, context);
} }
} }
impl Widget for Layers { impl Widget for Layers {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
self.synchronize_children(&mut context.as_event_context()); self.synchronize_children(&mut context.as_event_context());
for mounted in self.mounted.children() { for mounted in self.mounted.children() {
@ -68,7 +68,7 @@ impl Widget for Layers {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
self.synchronize_children(&mut context.as_event_context()); self.synchronize_children(&mut context.as_event_context());
@ -104,11 +104,11 @@ impl Widget for Layers {
size size
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
self.synchronize_children(context); self.synchronize_children(context);
} }
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
for child in self.mounted.drain() { for child in self.mounted.drain() {
context.remove_child(&child); context.remove_child(&child);
} }
@ -116,7 +116,7 @@ impl Widget for Layers {
fn root_behavior( fn root_behavior(
&mut self, &mut self,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> { ) -> Option<(RootBehavior, WidgetInstance)> {
self.synchronize_children(context); self.synchronize_children(context);
@ -174,7 +174,7 @@ impl OverlayLayer {
} }
impl Widget for OverlayLayer { impl Widget for OverlayLayer {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let state = self.state.lock(); let state = self.state.lock();
for child in &state.overlays { for child in &state.overlays {
@ -192,7 +192,7 @@ impl Widget for OverlayLayer {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.prevent_notifications(); state.prevent_notifications();
@ -233,7 +233,7 @@ impl Widget for OverlayLayer {
Size::ZERO Size::ZERO
} }
fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> bool {
let state = self.state.lock(); let state = self.state.lock();
state.test_point(location, false, context).is_some() state.test_point(location, false, context).is_some()
} }
@ -241,7 +241,7 @@ impl Widget for OverlayLayer {
fn hover( fn hover(
&mut self, &mut self,
location: Point<Px>, location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<kludgine::app::winit::window::CursorIcon> { ) -> Option<kludgine::app::winit::window::CursorIcon> {
let mut state = self.state.lock(); let mut state = self.state.lock();
@ -259,7 +259,7 @@ impl Widget for OverlayLayer {
None None
} }
fn unhover(&mut self, _context: &mut EventContext<'_, '_>) { fn unhover(&mut self, _context: &mut EventContext<'_>) {
let mut state = self.state.lock(); let mut state = self.state.lock();
state.hovering = None; state.hovering = None;
@ -301,7 +301,7 @@ impl OverlayState {
&self, &self,
location: Point<Px>, location: Point<Px>,
check_original_relative: bool, check_original_relative: bool,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<usize> { ) -> Option<usize> {
for (index, overlay) in self.overlays.iter().enumerate() { for (index, overlay) in self.overlays.iter().enumerate() {
if overlay.requires_hover if overlay.requires_hover
@ -326,7 +326,7 @@ impl OverlayState {
fn point_is_in_root_relative( fn point_is_in_root_relative(
&self, &self,
location: Point<Px>, location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> bool { ) -> bool {
if let Some(relative_to) = self if let Some(relative_to) = self
.overlays .overlays
@ -342,7 +342,7 @@ impl OverlayState {
false false
} }
fn process_new_overlays(&mut self, context: &mut EventContext<'_, '_>) { fn process_new_overlays(&mut self, context: &mut EventContext<'_>) {
while self.new_overlays > 0 { while self.new_overlays > 0 {
let new_index = self.overlays.len() - self.new_overlays; let new_index = self.overlays.len() - self.new_overlays;
self.new_overlays -= 1; self.new_overlays -= 1;
@ -378,7 +378,7 @@ impl OverlayState {
index: usize, index: usize,
widget: &MountedWidget, widget: &MountedWidget,
available_space: Size<UPx>, available_space: Size<UPx>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
relative_to: WidgetId, relative_to: WidgetId,
) -> Option<Rect<Px>> { ) -> Option<Rect<Px>> {
let direction = self.overlays[index].direction; let direction = self.overlays[index].direction;
@ -465,7 +465,7 @@ impl OverlayState {
&self, &self,
checking_index: usize, checking_index: usize,
layout: &Rect<Px>, layout: &Rect<Px>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> bool { ) -> bool {
for index in (0..self.overlays.len()).filter(|&i| i != checking_index) { for index in (0..self.overlays.len()).filter(|&i| i != checking_index) {
if self.overlays[index] if self.overlays[index]
@ -497,7 +497,7 @@ impl OverlayState {
index: usize, index: usize,
widget: &MountedWidget, widget: &MountedWidget,
available_space: Size<UPx>, available_space: Size<UPx>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Option<Rect<Px>> { ) -> Option<Rect<Px>> {
if let Some(relative_to) = self.overlays[index].relative_to { if let Some(relative_to) = self.overlays[index].relative_to {
self.layout_overlay_relative(index, widget, available_space, context, relative_to) self.layout_overlay_relative(index, widget, available_space, context, relative_to)
@ -747,7 +747,7 @@ impl WrapperWidget for Tooltipped {
fn hover( fn hover(
&mut self, &mut self,
_location: Point<Px>, _location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<kludgine::app::winit::window::CursorIcon> { ) -> Option<kludgine::app::winit::window::CursorIcon> {
let background_color = context.theme().surface.highest_container; let background_color = context.theme().surface.highest_container;
@ -779,7 +779,7 @@ impl WrapperWidget for Tooltipped {
None None
} }
fn unhover(&mut self, _context: &mut EventContext<'_, '_>) { fn unhover(&mut self, _context: &mut EventContext<'_>) {
self.show_animation = None; self.show_animation = None;
self.data.shown_tooltip.set(None); self.data.shown_tooltip.set(None);
} }

View file

@ -25,7 +25,7 @@ impl WrapperWidget for ThemedMode {
&mut self.child &mut self.child
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
context.attach_theme_mode(self.mode.clone()); context.attach_theme_mode(self.mode.clone());
} }
} }

View file

@ -318,7 +318,7 @@ impl Spinner {
start: ZeroToOne, start: ZeroToOne,
sweep: ZeroToOne, sweep: ZeroToOne,
color: Color, color: Color,
context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>,
) { ) {
if sweep > 0. { if sweep > 0. {
context.gfx.draw_shape( context.gfx.draw_shape(
@ -335,7 +335,7 @@ impl Spinner {
} }
impl Widget for Spinner { impl Widget for Spinner {
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
let track_size = context.get(&TrackSize).into_px(context.gfx.scale()); let track_size = context.get(&TrackSize).into_px(context.gfx.scale());
let start = self.start.get_tracking_redraw(context); let start = self.start.get_tracking_redraw(context);
let end = self.end.get_tracking_redraw(context); let end = self.end.get_tracking_redraw(context);
@ -384,7 +384,7 @@ impl Widget for Spinner {
fn layout( fn layout(
&mut self, &mut self,
available_space: figures::Size<crate::ConstraintLimit>, available_space: figures::Size<crate::ConstraintLimit>,
context: &mut crate::context::LayoutContext<'_, '_, '_, '_, '_>, context: &mut crate::context::LayoutContext<'_, '_, '_, '_>,
) -> figures::Size<figures::units::UPx> { ) -> figures::Size<figures::units::UPx> {
let track_size = context.get(&TrackSize).into_px(context.gfx.scale()); let track_size = context.get(&TrackSize).into_px(context.gfx.scale());
let minimum_size = track_size * 4; let minimum_size = track_size * 4;

View file

@ -80,7 +80,7 @@ impl<T> Widget for RadioOrnament<T>
where where
T: Debug + Eq + Send + 'static, T: Debug + Eq + Send + 'static,
{ {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let radio_size = context let radio_size = context
.gfx .gfx
.region() .region()
@ -118,7 +118,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
_available_space: Size<ConstraintLimit>, _available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<figures::units::UPx> { ) -> Size<figures::units::UPx> {
let radio_size = context.get(&RadioSize).into_upx(context.gfx.scale()); let radio_size = context.get(&RadioSize).into_upx(context.gfx.scale());
Size::squared(radio_size) Size::squared(radio_size)

View file

@ -89,14 +89,14 @@ impl WrapperWidget for Resize {
&mut self.child &mut self.child
} }
fn root_behavior(&mut self, _context: &mut EventContext<'_, '_>) -> Option<RootBehavior> { fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option<RootBehavior> {
Some(RootBehavior::Resize(Size::new(self.width, self.height))) Some(RootBehavior::Resize(Size::new(self.width, self.height)))
} }
fn layout_child( fn layout_child(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout { ) -> WrappedLayout {
let child = self.child.mounted(&mut context.as_event_context()); let child = self.child.mounted(&mut context.as_event_context());
let size = if let (Some(width), Some(height)) = let size = if let (Some(width), Some(height)) =

View file

@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
use figures::units::{Lp, Px, UPx}; use figures::units::{Lp, Px, UPx};
use figures::{FloatConversion, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size, Zero}; use figures::{FloatConversion, IntoSigned, IntoUnsigned, Point, Rect, ScreenScale, Size, Zero};
use intentional::Cast; use intentional::Cast;
use kludgine::app::winit::event::{DeviceId, MouseScrollDelta, TouchPhase}; use kludgine::app::winit::event::{MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::Shape; use kludgine::shapes::Shape;
use kludgine::Color; use kludgine::Color;
@ -15,6 +15,7 @@ use crate::styles::components::{EasingIn, EasingOut, LineHeight};
use crate::styles::Dimension; use crate::styles::Dimension;
use crate::value::{Destination, Dynamic, Source}; use crate::value::{Destination, Dynamic, Source};
use crate::widget::{EventHandling, MakeWidget, Widget, WidgetRef, HANDLED, IGNORED}; use crate::widget::{EventHandling, MakeWidget, Widget, WidgetRef, HANDLED, IGNORED};
use crate::window::DeviceId;
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A widget that supports scrolling its contents. /// A widget that supports scrolling its contents.
@ -97,7 +98,7 @@ impl Scroll {
(clamped, max_scroll) (clamped, max_scroll)
} }
fn show_scrollbars(&mut self, context: &mut EventContext<'_, '_>) { fn show_scrollbars(&mut self, context: &mut EventContext<'_>) {
let should_hide = self.drag.mouse_buttons_down == 0; let should_hide = self.drag.mouse_buttons_down == 0;
if should_hide != self.scrollbar_opacity_animation.will_hide if should_hide != self.scrollbar_opacity_animation.will_hide
|| self.scrollbar_opacity_animation.handle.is_complete() || self.scrollbar_opacity_animation.handle.is_complete()
@ -130,7 +131,7 @@ impl Scroll {
} }
} }
fn hide_scrollbars(&mut self, context: &mut EventContext<'_, '_>) { fn hide_scrollbars(&mut self, context: &mut EventContext<'_>) {
if self.drag.mouse_buttons_down == 0 && !self.scrollbar_opacity_animation.will_hide { if self.drag.mouse_buttons_down == 0 && !self.scrollbar_opacity_animation.will_hide {
self.scrollbar_opacity_animation.will_hide = true; self.scrollbar_opacity_animation.will_hide = true;
self.scrollbar_opacity_animation.handle = self self.scrollbar_opacity_animation.handle = self
@ -144,29 +145,29 @@ impl Scroll {
} }
impl Widget for Scroll { impl Widget for Scroll {
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) { fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.contents.unmount_in(context); self.contents.unmount_in(context);
} }
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn hover( fn hover(
&mut self, &mut self,
_location: Point<Px>, _location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<CursorIcon> { ) -> Option<CursorIcon> {
self.show_scrollbars(context); self.show_scrollbars(context);
None None
} }
fn unhover(&mut self, context: &mut EventContext<'_, '_>) { fn unhover(&mut self, context: &mut EventContext<'_>) {
self.hide_scrollbars(context); self.hide_scrollbars(context);
} }
fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
context.redraw_when_changed(&self.scrollbar_opacity); context.redraw_when_changed(&self.scrollbar_opacity);
let managed = self.contents.mounted(&mut context.as_event_context()); let managed = self.contents.mounted(&mut context.as_event_context());
@ -198,7 +199,7 @@ impl Widget for Scroll {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
self.bar_width = context self.bar_width = context
.get(&ScrollBarThickness) .get(&ScrollBarThickness)
@ -302,7 +303,7 @@ impl Widget for Scroll {
_device_id: DeviceId, _device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
_phase: TouchPhase, _phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let amount = match delta { let amount = match delta {
MouseScrollDelta::LineDelta(x, y) => Point::new(x, y) * self.line_height.into_float(), MouseScrollDelta::LineDelta(x, y) => Point::new(x, y) * self.line_height.into_float(),
@ -330,7 +331,7 @@ impl Widget for Scroll {
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let relative_x = (self.control_size.width - location.x).max(Px::ZERO); let relative_x = (self.control_size.width - location.x).max(Px::ZERO);
let in_vertical_area = self.enabled.y && relative_x <= self.bar_width; let in_vertical_area = self.enabled.y && relative_x <= self.bar_width;
@ -380,7 +381,7 @@ impl Widget for Scroll {
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) { ) {
self.drag.update( self.drag.update(
location, location,
@ -397,7 +398,7 @@ impl Widget for Scroll {
location: Option<Point<Px>>, location: Option<Point<Px>>,
_device_id: DeviceId, _device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton, _button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
self.drag.mouse_buttons_down -= 1; self.drag.mouse_buttons_down -= 1;

View file

@ -6,7 +6,7 @@ use std::ops::RangeInclusive;
use figures::units::{Lp, Px, UPx}; use figures::units::{Lp, Px, UPx};
use figures::{FloatConversion, IntoSigned, Point, Ranged, Rect, Round, ScreenScale, Size}; use figures::{FloatConversion, IntoSigned, Point, Ranged, Rect, Round, ScreenScale, Size};
use intentional::{Assert, Cast as _}; use intentional::{Assert, Cast as _};
use kludgine::app::winit::event::{DeviceId, MouseButton, MouseScrollDelta, TouchPhase}; use kludgine::app::winit::event::{MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::keyboard::{Key, NamedKey}; use kludgine::app::winit::keyboard::{Key, NamedKey};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::{Shape, StrokeOptions}; use kludgine::shapes::{Shape, StrokeOptions};
@ -21,6 +21,7 @@ use crate::styles::components::{
use crate::styles::{Dimension, HorizontalOrder, VerticalOrder, VisualOrder}; use crate::styles::{Dimension, HorizontalOrder, VerticalOrder, VisualOrder};
use crate::value::{Destination, Dynamic, IntoDynamic, IntoValue, Source, Value}; use crate::value::{Destination, Dynamic, IntoDynamic, IntoValue, Source, Value};
use crate::widget::{EventHandling, Widget, HANDLED, IGNORED}; use crate::widget::{EventHandling, Widget, HANDLED, IGNORED};
use crate::window::DeviceId;
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A widget that allows sliding between two values. /// A widget that allows sliding between two values.
@ -144,7 +145,7 @@ where
self self
} }
fn draw_track(&mut self, spec: &TrackSpec, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn draw_track(&mut self, spec: &TrackSpec, context: &mut GraphicsContext<'_, '_, '_, '_>) {
if self.horizontal { if self.horizontal {
self.rendered_size = spec.size.width; self.rendered_size = spec.size.width;
} else { } else {
@ -238,7 +239,7 @@ where
focus: Option<Knob>, focus: Option<Knob>,
focus_ring_width: Px, focus_ring_width: Px,
spec: &TrackSpec, spec: &TrackSpec,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
) { ) {
let (a, a_is_focused, b) = match (start_knob, focus) { let (a, a_is_focused, b) = match (start_knob, focus) {
(Some(start_knob), Some(Knob::Start)) => (end_knob, false, Some((start_knob, true))), (Some(start_knob), Some(Knob::Start)) => (end_knob, false, Some((start_knob, true))),
@ -257,7 +258,7 @@ where
is_focused: bool, is_focused: bool,
focus_ring_width: Px, focus_ring_width: Px,
spec: &TrackSpec, spec: &TrackSpec,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>, context: &mut GraphicsContext<'_, '_, '_, '_>,
) { ) {
context.gfx.draw_shape( context.gfx.draw_shape(
Shape::filled_circle(spec.half_knob, spec.knob_color, Origin::Center) Shape::filled_circle(spec.half_knob, spec.knob_color, Origin::Center)
@ -417,7 +418,7 @@ impl<T> Widget for Slider<T>
where where
T: SliderValue, T: SliderValue,
{ {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let (track_color, inactive_track_color, knob_color) = if context.enabled() { let (track_color, inactive_track_color, knob_color) = if context.enabled() {
( (
context.get(&TrackColor), context.get(&TrackColor),
@ -500,7 +501,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
self.knob_size = if self.knob_visible { self.knob_size = if self.knob_visible {
context.get(&KnobSize).into_upx(context.gfx.scale()) context.get(&KnobSize).into_upx(context.gfx.scale())
@ -552,14 +553,14 @@ where
} }
} }
fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_, '_>) -> bool { fn hit_test(&mut self, _location: Point<Px>, _context: &mut EventContext<'_>) -> bool {
self.interactive self.interactive
} }
fn hover( fn hover(
&mut self, &mut self,
_location: Point<Px>, _location: Point<Px>,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> Option<CursorIcon> { ) -> Option<CursorIcon> {
(self.interactive && self.knob_visible).then_some({ (self.interactive && self.knob_visible).then_some({
if context.enabled() { if context.enabled() {
@ -574,14 +575,14 @@ where
}) })
} }
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
context.enabled() context.enabled()
&& self.interactive && self.interactive
&& self.knob_visible && self.knob_visible
&& context.get(&AutoFocusableControls).is_all() && context.get(&AutoFocusableControls).is_all()
} }
fn focus(&mut self, context: &mut EventContext<'_, '_>) { fn focus(&mut self, context: &mut EventContext<'_>) {
if self.mouse_buttons_down == 0 { if self.mouse_buttons_down == 0 {
self.focused_knob = Some(if T::RANGED && !context.focus_is_advancing() { self.focused_knob = Some(if T::RANGED && !context.focus_is_advancing() {
Knob::End Knob::End
@ -595,7 +596,7 @@ where
fn advance_focus( fn advance_focus(
&mut self, &mut self,
direction: VisualOrder, direction: VisualOrder,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let (true, Some(focused)) = (T::RANGED, self.focused_knob) else { let (true, Some(focused)) = (T::RANGED, self.focused_knob) else {
return IGNORED; return IGNORED;
@ -619,7 +620,7 @@ where
HANDLED HANDLED
} }
fn blur(&mut self, context: &mut EventContext<'_, '_>) { fn blur(&mut self, context: &mut EventContext<'_>) {
self.previous_focus = self.focused_knob.take(); self.previous_focus = self.focused_knob.take();
context.set_needs_redraw(); context.set_needs_redraw();
} }
@ -629,7 +630,7 @@ where
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let true = self.interactive else { let true = self.interactive else {
return IGNORED; return IGNORED;
@ -652,7 +653,7 @@ where
location: Point<Px>, location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) { ) {
if context.enabled() { if context.enabled() {
self.update_from_click(location, None); self.update_from_click(location, None);
@ -664,7 +665,7 @@ where
_location: Option<Point<Px>>, _location: Option<Point<Px>>,
_device_id: DeviceId, _device_id: DeviceId,
_button: MouseButton, _button: MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) { ) {
self.mouse_buttons_down -= 1; self.mouse_buttons_down -= 1;
} }
@ -674,7 +675,7 @@ where
_device_id: DeviceId, _device_id: DeviceId,
input: kludgine::app::winit::event::KeyEvent, input: kludgine::app::winit::event::KeyEvent,
_is_synthetic: bool, _is_synthetic: bool,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let true = self.interactive else { let true = self.interactive else {
return IGNORED; return IGNORED;
@ -699,7 +700,7 @@ where
_device_id: DeviceId, _device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
_phase: TouchPhase, _phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let true = self.interactive else { let true = self.interactive else {
return IGNORED; return IGNORED;

View file

@ -3,6 +3,8 @@ use figures::Size;
use kludgine::Color; use kludgine::Color;
use crate::context::{GraphicsContext, LayoutContext}; use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::PrimaryColor;
use crate::styles::{DynamicComponent, IntoDynamicComponentValue};
use crate::value::{IntoValue, Value}; use crate::value::{IntoValue, Value};
use crate::widget::Widget; use crate::widget::Widget;
use crate::ConstraintLimit; use crate::ConstraintLimit;
@ -10,7 +12,7 @@ use crate::ConstraintLimit;
/// A widget that occupies space, optionally filling it with a color. /// A widget that occupies space, optionally filling it with a color.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Space { pub struct Space {
color: Value<Color>, color: Value<ColorSource>,
} }
impl Default for Space { impl Default for Space {
@ -24,7 +26,7 @@ impl Space {
#[must_use] #[must_use]
pub const fn clear() -> Self { pub const fn clear() -> Self {
Self { Self {
color: Value::Constant(Color::CLEAR_BLACK), color: Value::Constant(ColorSource::Color(Color::CLEAR_BLACK)),
} }
} }
@ -32,22 +34,52 @@ impl Space {
#[must_use] #[must_use]
pub fn colored(color: impl IntoValue<Color>) -> Self { pub fn colored(color: impl IntoValue<Color>) -> Self {
Self { Self {
color: color.into_value(), color: color
.into_value()
.map_each(|color| ColorSource::Color(*color)),
} }
} }
/// Returns a spacer that fills itself with `dynamic`'s color.
pub fn dynamic(dynamic: impl IntoDynamicComponentValue) -> Self {
Self {
color: dynamic
.into_dynamic_component()
.map_each(|component| ColorSource::Dynamic(component.clone())),
}
}
/// Returns a spacer that fills itself with the value of [`PrimaryColor`].
#[must_use]
pub fn primary() -> Self {
Self::dynamic(PrimaryColor)
}
} }
impl Widget for Space { impl Widget for Space {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let color = self.color.get_tracking_redraw(context); let source = self.color.get_tracking_redraw(context);
let color = match source {
ColorSource::Color(color) => color,
ColorSource::Dynamic(component) => component
.resolve(context)
.and_then(|component| Color::try_from(component).ok())
.unwrap_or(Color::CLEAR_BLACK),
};
context.fill(color); context.fill(color);
} }
fn layout( fn layout(
&mut self, &mut self,
_available_space: Size<ConstraintLimit>, _available_space: Size<ConstraintLimit>,
_context: &mut LayoutContext<'_, '_, '_, '_, '_>, _context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
Size::default() Size::default()
} }
} }
#[derive(Debug, PartialEq, Clone)]
enum ColorSource {
Color(Color),
Dynamic(DynamicComponent),
}

View file

@ -56,7 +56,7 @@ impl Stack {
self self
} }
fn synchronize_children(&mut self, context: &mut EventContext<'_, '_>) { fn synchronize_children(&mut self, context: &mut EventContext<'_>) {
let current_generation = self.children.generation(); let current_generation = self.children.generation();
self.children.invalidate_when_changed(context); self.children.invalidate_when_changed(context);
if current_generation.map_or_else( if current_generation.map_or_else(
@ -118,7 +118,7 @@ impl Stack {
} }
impl Widget for Stack { impl Widget for Stack {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
for (layout, child) in self.layout.iter().zip(&self.synced_children) { for (layout, child) in self.layout.iter().zip(&self.synced_children) {
if layout.size > 0 { if layout.size > 0 {
context.for_other(child).redraw(); context.for_other(child).redraw();
@ -129,7 +129,7 @@ impl Widget for Stack {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
self.synchronize_children(&mut context.as_event_context()); self.synchronize_children(&mut context.as_event_context());

View file

@ -206,7 +206,7 @@ impl WrapperWidget for Style {
&mut self.child &mut self.child
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
context.attach_styles(self.styles.clone()); context.attach_styles(self.styles.clone());
} }
} }

View file

@ -52,7 +52,7 @@ impl WrapperWidget for Switcher {
fn adjust_child_constraints( fn adjust_child_constraints(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> { ) -> Size<ConstraintLimit> {
if self.source.has_updated() { if self.source.has_updated() {
self.child = WidgetRef::new(self.source.get()); self.child = WidgetRef::new(self.source.get());

View file

@ -25,7 +25,7 @@ impl WrapperWidget for Themed {
&mut self.child &mut self.child
} }
fn mounted(&mut self, context: &mut EventContext<'_, '_>) { fn mounted(&mut self, context: &mut EventContext<'_>) {
context.attach_theme(self.theme.clone()); context.attach_theme(self.theme.clone());
} }
} }

View file

@ -3,7 +3,7 @@ use std::fmt::Debug;
use figures::units::{Px, UPx}; use figures::units::{Px, UPx};
use figures::{Point, Size}; use figures::{Point, Size};
use intentional::Cast; use intentional::Cast;
use kludgine::app::winit::event::{DeviceId, ElementState, KeyEvent, MouseScrollDelta, TouchPhase}; use kludgine::app::winit::event::{ElementState, KeyEvent, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon; use kludgine::app::winit::window::CursorIcon;
use kludgine::tilemap; use kludgine::tilemap;
use kludgine::tilemap::TileMapFocus; use kludgine::tilemap::TileMapFocus;
@ -12,6 +12,7 @@ use crate::context::{EventContext, GraphicsContext, LayoutContext, Trackable};
use crate::tick::Tick; use crate::tick::Tick;
use crate::value::{Dynamic, IntoValue, Value}; use crate::value::{Dynamic, IntoValue, Value};
use crate::widget::{EventHandling, Widget, HANDLED, IGNORED}; use crate::widget::{EventHandling, Widget, HANDLED, IGNORED};
use crate::window::DeviceId;
use crate::ConstraintLimit; use crate::ConstraintLimit;
/// A layered tile-based 2d game surface. /// A layered tile-based 2d game surface.
@ -64,7 +65,7 @@ impl<Layers> Widget for TileMap<Layers>
where where
Layers: tilemap::Layers, Layers: tilemap::Layers,
{ {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let focus = self.focus.get(); let focus = self.focus.get();
// TODO this needs to be updated to support being placed in side of a scroll view. // TODO this needs to be updated to support being placed in side of a scroll view.
let redraw_after = match &mut self.layers { let redraw_after = match &mut self.layers {
@ -103,14 +104,14 @@ where
} }
} }
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool { fn accept_focus(&mut self, _context: &mut EventContext<'_>) -> bool {
true true
} }
fn hit_test( fn hit_test(
&mut self, &mut self,
_location: figures::Point<figures::units::Px>, _location: figures::Point<figures::units::Px>,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) -> bool { ) -> bool {
true true
} }
@ -118,7 +119,7 @@ where
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
_context: &mut LayoutContext<'_, '_, '_, '_, '_>, _context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
Size::new(available_space.width.max(), available_space.height.max()) Size::new(available_space.width.max(), available_space.height.max())
} }
@ -128,7 +129,7 @@ where
_device_id: DeviceId, _device_id: DeviceId,
delta: MouseScrollDelta, delta: MouseScrollDelta,
_phase: TouchPhase, _phase: TouchPhase,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
let amount = match delta { let amount = match delta {
MouseScrollDelta::LineDelta(_, lines) => lines, MouseScrollDelta::LineDelta(_, lines) => lines,
@ -141,11 +142,7 @@ where
HANDLED HANDLED
} }
fn hover( fn hover(&mut self, local: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
&mut self,
local: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
let Some(size) = context.last_layout().map(|rect| rect.size) else { let Some(size) = context.last_layout().map(|rect| rect.size) else {
return None; return None;
@ -163,7 +160,7 @@ where
None None
} }
fn unhover(&mut self, _context: &mut EventContext<'_, '_>) { fn unhover(&mut self, _context: &mut EventContext<'_>) {
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
tick.set_cursor_position(None); tick.set_cursor_position(None);
} }
@ -174,7 +171,7 @@ where
_device_id: DeviceId, _device_id: DeviceId,
input: KeyEvent, input: KeyEvent,
_is_synthetic: bool, _is_synthetic: bool,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
tick.key_input(&input)?; tick.key_input(&input)?;
@ -188,7 +185,7 @@ where
_location: Point<Px>, _location: Point<Px>,
_device_id: DeviceId, _device_id: DeviceId,
button: kludgine::app::winit::event::MouseButton, button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>, context: &mut EventContext<'_>,
) -> EventHandling { ) -> EventHandling {
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
tick.mouse_button(button, ElementState::Pressed); tick.mouse_button(button, ElementState::Pressed);
@ -204,7 +201,7 @@ where
_location: Option<Point<Px>>, _location: Option<Point<Px>>,
_device_id: DeviceId, _device_id: DeviceId,
button: kludgine::app::winit::event::MouseButton, button: kludgine::app::winit::event::MouseButton,
_context: &mut EventContext<'_, '_>, _context: &mut EventContext<'_>,
) { ) {
if let Some(tick) = &self.tick { if let Some(tick) = &self.tick {
tick.mouse_button(button, ElementState::Released); tick.mouse_button(button, ElementState::Released);

View file

@ -102,10 +102,7 @@ impl WrapperWidget for ValidatedWidget {
&mut self.contents &mut self.contents
} }
fn redraw_background( fn redraw_background(&mut self, context: &mut crate::context::GraphicsContext<'_, '_, '_, '_>) {
&mut self,
context: &mut crate::context::GraphicsContext<'_, '_, '_, '_, '_>,
) {
self.error_color.set(context.get(&InvalidTextColor)); self.error_color.set(context.get(&InvalidTextColor));
self.default_color.set(context.get(&HintTextColor)); self.default_color.set(context.get(&HintTextColor));
} }

View file

@ -97,7 +97,7 @@ impl Wrap {
} }
impl Widget for Wrap { impl Widget for Wrap {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
for child in self.mounted.children() { for child in self.mounted.children() {
context.for_other(child).redraw(); context.for_other(child).redraw();
} }
@ -107,7 +107,7 @@ impl Widget for Wrap {
fn layout( fn layout(
&mut self, &mut self,
available_space: Size<ConstraintLimit>, available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>, context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> { ) -> Size<UPx> {
struct RowChild { struct RowChild {
index: usize, index: usize,

File diff suppressed because it is too large Load diff