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:
test:
strategy:
fail-fast: false
matrix:
version: ["stable", "1.70.0"]
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
@ -16,10 +17,15 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
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 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
with:
@ -30,6 +36,13 @@ jobs:
run: |
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: |
cargo test --all-features --all-targets

3
.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
- 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
mounted state to be stored in a `WindowLocal`, ensuring `WidgetRef`s work
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
from. This ensures if a `WidgetInstance` moves or is shared between windows,
the cache is invalidated.
- All `Dynamic` mapping functions now utilize weak references, and clean up as
necessary if a value is not able to be upgraded.
- All `Dynamic` mapping functions now utilize weak references, and the
`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
`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.
### 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
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
@ -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
situations where it is known or likely that the clone being performed is
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
[120]: https://github.com/khonsulabs/cushy/issues/120

51
Cargo.lock generated
View file

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

View file

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

View file

@ -4,6 +4,7 @@
use cushy::value::{Destination, Dynamic, Source};
use cushy::widget::{MakeWidget, MakeWidgetWithTag, Widget, WidgetInstance, WidgetTag, HANDLED};
use cushy::widgets::Custom;
use cushy::window::DeviceId;
use cushy::Run;
use figures::units::{Lp, UPx};
use figures::{ScreenScale, Size};
@ -90,14 +91,14 @@ impl Default 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));
}
fn layout(
&mut self,
available_space: Size<cushy::ConstraintLimit>,
context: &mut cushy::context::LayoutContext<'_, '_, '_, '_, '_>,
context: &mut cushy::context::LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
Size::new(
available_space.width.min(),
@ -108,7 +109,7 @@ impl Widget for Toggle {
fn hit_test(
&mut self,
_location: figures::Point<figures::units::Px>,
_context: &mut cushy::context::EventContext<'_, '_>,
_context: &mut cushy::context::EventContext<'_>,
) -> bool {
true
}
@ -116,9 +117,9 @@ impl Widget for Toggle {
fn mouse_down(
&mut self,
_location: figures::Point<figures::units::Px>,
_device_id: kludgine::app::winit::event::DeviceId,
_device_id: DeviceId,
_button: kludgine::app::winit::event::MouseButton,
_context: &mut cushy::context::EventContext<'_, '_>,
_context: &mut cushy::context::EventContext<'_>,
) -> cushy::widget::EventHandling {
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 {
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({
let color = color.clone();
move |hue| {
if let Ok(mut source) = color.try_get() {
source.hue = OklabHue::new(hue);
color.set(source);
}
let mut source = color.get();
source.hue = OklabHue::new(hue);
color.set(source);
}
})
.persist();
let hue_text = hue.linked_string();
let saturation = color.map_each(|color| color.saturation);
let saturation = color.map_each_cloned(|color| color.saturation);
saturation
.for_each_cloned({
let color = color.clone();
move |saturation| {
if let Ok(mut source) = color.try_get() {
source.saturation = saturation;
color.set(source);
}
let mut source = color.get();
source.saturation = saturation;
color.set(source);
}
})
.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 figures::units::{Lp, Px, UPx};
use figures::{Angle, Ranged, UnscaledUnit, Zero};
use figures::{Angle, Point, Ranged, Rect, Size, UnscaledUnit, Zero};
use intentional::Cast;
use kempt::Set;
use kludgine::Color;
@ -929,6 +929,42 @@ impl_unscaled_lerp!(Px);
impl_unscaled_lerp!(Lp);
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]
fn integer_lerps() {
#[track_caller]

View file

@ -30,11 +30,7 @@ impl Default for PendingApp {
fn default() -> Self {
Self {
app: kludgine::app::PendingApp::default(),
cushy: Cushy {
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
},
cushy: Cushy::new(),
}
}
}
@ -52,6 +48,14 @@ pub struct 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
/// initialized when the window opened.
#[must_use]
@ -77,7 +81,7 @@ impl Application for PendingApp {
fn as_app(&self) -> App {
App {
app: self.app.as_app(),
app: Some(self.app.as_app()),
cushy: self.cushy.clone(),
}
}
@ -86,7 +90,7 @@ impl Application for PendingApp {
/// A handle to a Cushy application.
#[derive(Clone)]
pub struct App {
app: kludgine::app::App<WindowCommand>,
app: Option<kludgine::app::App<WindowCommand>>,
cushy: Cushy,
}
@ -102,7 +106,10 @@ impl Application for App {
impl AsApplication<AppEvent<WindowCommand>> for App {
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::{IntoSigned, Point, Px2D, Rect, Round, ScreenScale, Size, Zero};
use kludgine::app::winit::event::{
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon;
use kludgine::shapes::{Shape, StrokeOptions};
use kludgine::{Color, Kludgine, KludgineId};
@ -21,16 +19,16 @@ use crate::styles::{ComponentDefinition, Styles, Theme, ThemePair};
use crate::tree::Tree;
use crate::value::{IntoValue, Source, Value};
use crate::widget::{EventHandling, MountedWidget, RootBehavior, WidgetId, WidgetInstance};
use crate::window::{CursorState, RunningWindow, ThemeMode};
use crate::window::{CursorState, DeviceId, PlatformWindow, ThemeMode};
use crate::ConstraintLimit;
/// A context to an event function.
///
/// This type is a combination of a reference to the rendering library,
/// [`Kludgine`], and a [`WidgetContext`].
pub struct EventContext<'context, 'window> {
pub struct EventContext<'context> {
/// The context for the widget receiving the event.
pub widget: WidgetContext<'context, 'window>,
pub widget: WidgetContext<'context>,
/// The rendering library's state.
///
/// 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,
}
impl<'context, 'window> EventContext<'context, 'window> {
impl<'context> EventContext<'context> {
const MAX_PENDING_CHANGE_CYCLES: u8 = 100;
pub(crate) fn new(
widget: WidgetContext<'context, 'window>,
kludgine: &'context mut Kludgine,
) -> Self {
pub(crate) fn new(widget: WidgetContext<'context>, kludgine: &'context mut Kludgine) -> Self {
Self { widget, kludgine }
}
@ -58,10 +53,10 @@ impl<'context, 'window> EventContext<'context, 'window> {
pub fn for_other<'child, Widget>(
&'child mut self,
widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<EventContext<'child, 'window>>>::Result
) -> <Widget::Managed as MapManagedWidget<EventContext<'child>>>::Result
where
Widget: ManageWidget,
Widget::Managed: MapManagedWidget<EventContext<'child, 'window>>,
Widget::Managed: MapManagedWidget<EventContext<'child>>,
{
widget
.manage(self)
@ -177,7 +172,8 @@ impl<'context, 'window> EventContext<'context, 'window> {
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) {
@ -189,7 +185,7 @@ impl<'context, 'window> EventContext<'context, 'window> {
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) {
@ -480,15 +476,15 @@ impl<'context, 'window> EventContext<'context, 'window> {
}
}
impl<'context, 'window> Deref for EventContext<'context, 'window> {
type Target = WidgetContext<'context, 'window>;
impl<'context> Deref for EventContext<'context> {
type Target = WidgetContext<'context>;
fn deref(&self) -> &Self::Target {
&self.widget
}
}
impl<'context, 'window> DerefMut for EventContext<'context, 'window> {
impl<'context> DerefMut for EventContext<'context> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.widget
}
@ -523,18 +519,18 @@ impl<T> DerefMut for Exclusive<'_, T> {
}
/// 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.
pub widget: WidgetContext<'context, 'window>,
pub widget: WidgetContext<'context>,
/// 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
/// widget region.
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`.
pub fn borrowed(&mut self) -> GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass> {
pub fn borrowed(&mut self) -> GraphicsContext<'_, 'clip, 'gfx, 'pass> {
GraphicsContext {
widget: self.widget.borrowed(),
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>(
&'child mut self,
widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<
GraphicsContext<'child, 'window, 'child, 'gfx, 'pass>,
>>::Result
) -> <Widget::Managed as MapManagedWidget<GraphicsContext<'child, 'child, 'gfx, 'pass>>>::Result
where
Widget: ManageWidget,
Widget::Managed: MapManagedWidget<GraphicsContext<'child, 'window, 'child, 'gfx, 'pass>>,
Widget::Managed: MapManagedWidget<GraphicsContext<'child, 'child, 'gfx, 'pass>>,
{
let opacity = self.get(&Opacity);
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.
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 {
widget: self.widget.borrowed(),
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) {
if matches!(self.widget.pending_state, PendingState::Owned(_)) {
self.as_event_context().apply_pending_state();
@ -683,36 +677,30 @@ impl Drop for GraphicsContext<'_, '_, '_, '_, '_> {
}
}
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>
{
type Target = WidgetContext<'context, 'window>;
impl<'context, 'clip, 'gfx, 'pass> Deref for GraphicsContext<'context, 'clip, 'gfx, 'pass> {
type Target = WidgetContext<'context>;
fn deref(&self) -> &Self::Target {
&self.widget
}
}
impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut
for GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>
{
impl<'context, 'clip, 'gfx, 'pass> DerefMut for GraphicsContext<'context, 'clip, 'gfx, 'pass> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.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
/// within.
pub graphics: GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
pub graphics: GraphicsContext<'context, 'clip, 'gfx, 'pass>,
persist_layout: bool,
}
impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'clip, 'gfx, 'pass> {
pub(crate) fn new(
graphics: &'context mut GraphicsContext<'_, 'window, 'clip, 'gfx, 'pass>,
) -> Self {
impl<'context, 'clip, 'gfx, 'pass> LayoutContext<'context, 'clip, 'gfx, 'pass> {
pub(crate) fn new(graphics: &'context mut GraphicsContext<'_, 'clip, 'gfx, 'pass>) -> Self {
Self {
graphics: graphics.borrowed(),
persist_layout: true,
@ -736,10 +724,10 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl
pub fn for_other<'child, Widget>(
&'child mut self,
widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<LayoutContext<'child, 'window, 'child, 'gfx, 'pass>>>::Result
) -> <Widget::Managed as MapManagedWidget<LayoutContext<'child, 'child, 'gfx, 'pass>>>::Result
where
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 {
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>
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>
{
fn as_event_context(&mut self) -> EventContext<'_, 'window> {
impl<'context, 'clip, 'gfx, 'pass> AsEventContext for LayoutContext<'context, 'clip, 'gfx, 'pass> {
fn as_event_context(&mut self) -> EventContext<'_> {
self.graphics.as_event_context()
}
}
impl<'context, 'window, 'clip, 'gfx, 'pass> Deref
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>
{
type Target = GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>;
impl<'context, 'clip, 'gfx, 'pass> Deref for LayoutContext<'context, 'clip, 'gfx, 'pass> {
type Target = GraphicsContext<'context, 'clip, 'gfx, 'pass>;
fn deref(&self) -> &Self::Target {
&self.graphics
}
}
impl<'context, 'window, 'clip, 'gfx, 'pass> DerefMut
for LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>
{
impl<'context, 'clip, 'gfx, 'pass> DerefMut for LayoutContext<'context, 'clip, 'gfx, 'pass> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.graphics
}
}
/// Converts from one context to an [`EventContext`].
pub trait AsEventContext<'window> {
pub trait AsEventContext {
/// 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
/// context's widget.
@ -837,14 +819,14 @@ pub trait AsEventContext<'window> {
}
}
impl<'window> AsEventContext<'window> for EventContext<'_, 'window> {
fn as_event_context(&mut self) -> EventContext<'_, 'window> {
impl AsEventContext for EventContext<'_> {
fn as_event_context(&mut self) -> EventContext<'_> {
EventContext::new(self.widget.borrowed(), self.kludgine)
}
}
impl<'window> AsEventContext<'window> for GraphicsContext<'_, 'window, '_, '_, '_> {
fn as_event_context(&mut self) -> EventContext<'_, 'window> {
impl AsEventContext for GraphicsContext<'_, '_, '_, '_> {
fn as_event_context(&mut self) -> EventContext<'_> {
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
/// specific widget.
pub struct WidgetContext<'context, 'window> {
pub struct WidgetContext<'context> {
current_node: MountedWidget,
pub(crate) tree: Tree,
window: &'context mut RunningWindow<'window>,
window: &'context mut dyn PlatformWindow,
theme: Cow<'context, ThemePair>,
cursor: &'context mut CursorState,
pending_state: PendingState<'context>,
@ -864,11 +846,11 @@ pub struct WidgetContext<'context, 'window> {
cache: WidgetCacheKey,
}
impl<'context, 'window> WidgetContext<'context, 'window> {
impl<'context> WidgetContext<'context> {
pub(crate) fn new(
current_node: MountedWidget,
theme: &'context ThemePair,
window: &'context mut RunningWindow<'window>,
window: &'context mut dyn PlatformWindow,
theme_mode: ThemeMode,
cursor: &'context mut CursorState,
) -> Self {
@ -899,7 +881,7 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
}
/// Returns a new instance that borrows from `self`.
pub fn borrowed(&mut self) -> WidgetContext<'_, 'window> {
pub fn borrowed(&mut self) -> WidgetContext<'_> {
WidgetContext {
tree: self.tree.clone(),
current_node: self.current_node.clone(),
@ -916,10 +898,10 @@ impl<'context, 'window> WidgetContext<'context, 'window> {
pub fn for_other<'child, Widget>(
&'child mut self,
widget: &Widget,
) -> <Widget::Managed as MapManagedWidget<WidgetContext<'child, 'window>>>::Result
) -> <Widget::Managed as MapManagedWidget<WidgetContext<'child>>>::Result
where
Widget: ManageWidget,
Widget::Managed: MapManagedWidget<WidgetContext<'child, 'window>>,
Widget::Managed: MapManagedWidget<WidgetContext<'child>>,
{
widget.manage(self).map(|current_node| {
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.
#[must_use]
pub fn window(&self) -> &RunningWindow<'window> {
pub fn window(&self) -> &dyn PlatformWindow {
self.window
}
/// Returns an exclusive reference to the window containing this widget.
#[must_use]
pub fn window_mut(&mut self) -> &mut RunningWindow<'window> {
pub fn window_mut(&mut self) -> &mut dyn PlatformWindow {
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) {
if matches!(self.widget.pending_state, PendingState::Owned(_)) {
self.apply_pending_state();
@ -1211,17 +1191,17 @@ impl Drop for EventContext<'_, '_> {
}
}
impl<'window> Deref for WidgetContext<'_, 'window> {
type Target = RunningWindow<'window>;
impl<'context> Deref for WidgetContext<'context> {
type Target = &'context mut dyn PlatformWindow;
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 {
self.window
&mut self.window
}
}
@ -1270,13 +1250,13 @@ pub trait ManageWidget {
type Managed: MapManagedWidget<MountedWidget>;
/// Resolve `self` into a [`MountedWidget`].
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed;
fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed;
}
impl ManageWidget for WidgetId {
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
context.tree.widget(*self)
}
}
@ -1284,7 +1264,7 @@ impl ManageWidget for WidgetId {
impl ManageWidget for WidgetInstance {
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
context.tree.widget(self.id())
}
}
@ -1292,7 +1272,7 @@ impl ManageWidget for WidgetInstance {
impl ManageWidget for MountedWidget {
type Managed = Self;
fn manage(&self, _context: &WidgetContext<'_, '_>) -> Self::Managed {
fn manage(&self, _context: &WidgetContext<'_>) -> Self::Managed {
self.clone()
}
}
@ -1348,7 +1328,7 @@ pub trait Trackable: sealed::Trackable {
/// Marks the widget for redraw when this value is updated.
///
/// 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
Self: Sized,
{
@ -1358,7 +1338,7 @@ pub trait Trackable: sealed::Trackable {
/// Marks the widget for redraw when this value is updated.
///
/// 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
Self: Sized,
{

View file

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

View file

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

View file

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

View file

@ -25,7 +25,7 @@ pub struct Tick {
impl Tick {
/// 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);
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
/// thread.
#[must_use]
fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T
fn get_tracking_redraw(&self, context: &WidgetContext<'_>) -> T
where
T: Clone,
Self: Trackable + Sized,
@ -121,7 +121,7 @@ pub trait Source<T> {
/// This function panics if this value is already locked by the current
/// thread.
#[must_use]
fn get_tracking_invalidate(&self, context: &WidgetContext<'_, '_>) -> T
fn get_tracking_invalidate(&self, context: &WidgetContext<'_>) -> T
where
T: Clone,
Self: Trackable + Sized,
@ -290,6 +290,30 @@ pub trait Source<T> {
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
/// time `self` is updated.
#[must_use]
@ -510,7 +534,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
+ 'static,
{
let this = WeakDynamic(Arc::downgrade(self));
DynamicData::for_each(self, move || {
dynamic_for_each(self, move || {
let this = this.upgrade().ok_or(CallbackDisconnected)?;
this.map_generational(&mut for_each)?;
Ok(())
@ -523,7 +547,7 @@ impl<T> Source<T> for Arc<DynamicData<T>> {
F: FnMut(GenerationalValue<T>) -> Result<(), CallbackDisconnected> + Send + 'static,
{
let this = WeakDynamic(Arc::downgrade(self));
DynamicData::for_each(self, move || {
dynamic_for_each(self, move || {
let this = this.upgrade().ok_or(CallbackDisconnected)?;
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();
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
id: Some(callbacks.push(Box::new(for_each))),
owner: None,
callbacks: self.callbacks.clone(),
}))
}
@ -944,9 +969,10 @@ impl<T> Dynamic<T> {
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| {
let t = t_weak.upgrade().ok_or(CallbackDisconnected)?;
if let Some(update) = r_into_t(r).into() {
let _result = t.replace(update);
}
@ -1388,18 +1414,20 @@ impl<T> DynamicData<T> {
Ok(old)
}
}
pub fn for_each<F>(&self, map: F) -> CallbackHandle
where
F: for<'a> FnMut() -> Result<(), CallbackDisconnected> + Send + 'static,
{
let state = self.state().expect("deadlocked");
let mut data = state.callbacks.callbacks.lock().ignore_poison();
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
id: Some(data.callbacks.push(Box::new(map))),
callbacks: state.callbacks.clone(),
}))
}
fn dynamic_for_each<T, F>(this: &Arc<DynamicData<T>>, map: F) -> CallbackHandle
where
F: for<'a> FnMut() -> Result<(), CallbackDisconnected> + Send + 'static,
T: Send + 'static,
{
let state = this.state().expect("deadlocked");
let mut data = state.callbacks.callbacks.lock().ignore_poison();
CallbackHandle(CallbackHandleInner::Single(CallbackHandleData {
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.
@ -1467,8 +1495,12 @@ enum CallbackHandleInner {
Multi(Vec<CallbackHandleData>),
}
trait ReferencedDynamic: Sync + Send + 'static {}
impl<T> ReferencedDynamic for T where T: Sync + Send + 'static {}
struct CallbackHandleData {
id: Option<LotId>,
owner: Option<Arc<dyn ReferencedDynamic>>,
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 {}
@ -1539,6 +1598,7 @@ impl Drop for CallbackHandleData {
}
}
}
impl PartialEq for CallbackHandleData {
fn eq(&self, other: &Self) -> bool {
self.id == other.id && Arc::ptr_eq(&self.callbacks, &other.callbacks)
@ -2508,7 +2568,7 @@ impl<T> Value<T> {
/// updated.
pub fn map_tracking_redraw<R>(
&self,
context: &WidgetContext<'_, '_>,
context: &WidgetContext<'_>,
map: impl FnOnce(&T) -> R,
) -> R {
match self {
@ -2526,7 +2586,7 @@ impl<T> Value<T> {
/// updated.
pub fn map_tracking_invalidate<R>(
&self,
context: &WidgetContext<'_, '_>,
context: &WidgetContext<'_>,
map: impl FnOnce(&T) -> R,
) -> R {
match self {
@ -2573,7 +2633,7 @@ impl<T> Value<T> {
///
/// If `self` is a dynamic, `context` will be refreshed when the value is
/// updated.
pub fn get_tracking_redraw(&self, context: &WidgetContext<'_, '_>) -> T
pub fn get_tracking_redraw(&self, context: &WidgetContext<'_>) -> T
where
T: Clone,
{
@ -2584,7 +2644,7 @@ impl<T> Value<T> {
///
/// If `self` is a dynamic, `context` will be invalidated when the value is
/// updated.
pub fn get_tracking_invalidate(&self, context: &WidgetContext<'_, '_>) -> T
pub fn get_tracking_invalidate(&self, context: &WidgetContext<'_>) -> T
where
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]) => {
$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),+]) => {
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::{IntoSigned, IntoUnsigned, Point, Rect, Size};
use intentional::Assert;
use kludgine::app::winit::event::{
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon;
use kludgine::Color;
@ -45,7 +43,11 @@ use crate::widgets::{
Align, Button, Checkbox, Collapse, Container, Disclose, Expand, Layers, Resize, Scroll, Space,
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;
/// A type that makes up a graphical user interface.
@ -264,7 +266,7 @@ use crate::ConstraintLimit;
/// [repo]: https://github.com/khonsulabs/cushy
pub trait Widget: Send + Debug + 'static {
/// 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`.
///
@ -297,50 +299,46 @@ pub trait Widget: Send + Debug + 'static {
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
available_space.map(ConstraintLimit::min)
}
/// The widget has been mounted into a parent widget.
#[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.
#[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`.
#[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
}
/// The widget is currently has a cursor hovering it at `location`.
#[allow(unused_variables)]
fn hover(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
None
}
/// The widget is no longer being hovered.
#[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
/// true, the widget will be focused. If false, Cushy will continue
/// searching for another focus target.
#[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool {
fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false
}
/// The widget has received focus for user input.
#[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,
/// honoring `direction` in a consistent manner. Returning `HANDLED` will
@ -349,7 +347,7 @@ pub trait Widget: Send + Debug + 'static {
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -357,21 +355,21 @@ pub trait Widget: Send + Debug + 'static {
/// The widget is about to lose focus. Returning true allows the focus to
/// switch away from this widget.
#[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool {
fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true
}
/// The widget is no longer focused for user input.
#[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_, '_>) {}
fn blur(&mut self, context: &mut EventContext<'_>) {}
/// The widget has become the active widget.
#[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_, '_>) {}
fn activate(&mut self, context: &mut EventContext<'_>) {}
/// The widget is no longer active.
#[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
/// event has been handled or not.
@ -384,7 +382,7 @@ pub trait Widget: Send + Debug + 'static {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -397,7 +395,7 @@ pub trait Widget: Send + Debug + 'static {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
}
@ -408,7 +406,7 @@ pub trait Widget: Send + Debug + 'static {
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
}
@ -420,7 +418,7 @@ pub trait Widget: Send + Debug + 'static {
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -428,7 +426,7 @@ pub trait Widget: Send + Debug + 'static {
/// An input manager event has been sent to this widget. Returns whether the
/// event has been handled or not.
#[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling {
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED
}
@ -440,7 +438,7 @@ pub trait Widget: Send + Debug + 'static {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -451,7 +449,7 @@ pub trait Widget: Send + Debug + 'static {
#[allow(unused_variables)]
fn root_behavior(
&mut self,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> {
None
}
@ -544,7 +542,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// Returns the behavior this widget should apply when positioned at the
/// root of the window.
#[allow(unused_variables)]
fn root_behavior(&mut self, context: &mut EventContext<'_, '_>) -> Option<RootBehavior> {
fn root_behavior(&mut self, context: &mut EventContext<'_>) -> Option<RootBehavior> {
None
}
@ -552,13 +550,13 @@ pub trait WrapperWidget: Debug + Send + 'static {
///
/// This is invoked before the wrapped widget is drawn.
#[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.
///
/// This is invoked after the wrapped widget is drawn.
#[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
/// `available_space`.
@ -566,7 +564,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
fn layout_child(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
let adjusted_space = self.adjust_child_constraints(available_space, 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(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> {
available_space
}
@ -596,7 +594,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
&mut self,
size: Size<Px>,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
Size::new(
available_space
@ -612,7 +610,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// Returns the background color to render behind the wrapped widget.
#[allow(unused_variables)]
#[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
// else by default.
None
@ -620,39 +618,35 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// The widget has been mounted into a parent widget.
#[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.
#[allow(unused_variables)]
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.child_mut().unmount_in(context);
}
/// Returns true if this widget should respond to mouse input at `location`.
#[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
}
/// The widget is currently has a cursor hovering it at `location`.
#[allow(unused_variables)]
fn hover(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
None
}
/// The widget is no longer being hovered.
#[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
/// true, the widget will be focused. If false, Cushy will continue
/// searching for another focus target.
#[allow(unused_variables)]
fn accept_focus(&mut self, context: &mut EventContext<'_, '_>) -> bool {
fn accept_focus(&mut self, context: &mut EventContext<'_>) -> bool {
false
}
@ -663,33 +657,33 @@ pub trait WrapperWidget: Debug + Send + 'static {
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
/// The widget has received focus for user input.
#[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
/// switch away from this widget.
#[allow(unused_variables)]
fn allow_blur(&mut self, context: &mut EventContext<'_, '_>) -> bool {
fn allow_blur(&mut self, context: &mut EventContext<'_>) -> bool {
true
}
/// The widget is no longer focused for user input.
#[allow(unused_variables)]
fn blur(&mut self, context: &mut EventContext<'_, '_>) {}
fn blur(&mut self, context: &mut EventContext<'_>) {}
/// The widget has become the active widget.
#[allow(unused_variables)]
fn activate(&mut self, context: &mut EventContext<'_, '_>) {}
fn activate(&mut self, context: &mut EventContext<'_>) {}
/// The widget is no longer active.
#[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
/// event has been handled or not.
@ -702,7 +696,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -715,7 +709,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
}
@ -726,7 +720,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
}
@ -738,7 +732,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -746,7 +740,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
/// An input manager event has been sent to this widget. Returns whether the
/// event has been handled or not.
#[allow(unused_variables)]
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling {
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
IGNORED
}
@ -758,7 +752,7 @@ pub trait WrapperWidget: Debug + Send + 'static {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
IGNORED
}
@ -770,13 +764,13 @@ where
{
fn root_behavior(
&mut self,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> {
T::root_behavior(self, context)
.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);
if let Some(color) = background_color {
context.fill(color);
@ -793,7 +787,7 @@ where
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
let layout = self.layout_child(available_space, context);
let child = self.child_mut().mounted(&mut context.as_event_context());
@ -801,47 +795,43 @@ where
layout.size
}
fn mounted(&mut self, context: &mut EventContext<'_, '_>) {
fn mounted(&mut self, context: &mut EventContext<'_>) {
T::mounted(self, context);
}
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
fn unmounted(&mut self, context: &mut EventContext<'_>) {
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)
}
fn hover(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
T::hover(self, location, context)
}
fn unhover(&mut self, context: &mut EventContext<'_, '_>) {
fn unhover(&mut self, context: &mut EventContext<'_>) {
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)
}
fn focus(&mut self, context: &mut EventContext<'_, '_>) {
fn focus(&mut self, context: &mut EventContext<'_>) {
T::focus(self, context);
}
fn blur(&mut self, context: &mut EventContext<'_, '_>) {
fn blur(&mut self, context: &mut EventContext<'_>) {
T::blur(self, context);
}
fn activate(&mut self, context: &mut EventContext<'_, '_>) {
fn activate(&mut self, context: &mut EventContext<'_>) {
T::activate(self, context);
}
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {
fn deactivate(&mut self, context: &mut EventContext<'_>) {
T::deactivate(self, context);
}
@ -850,7 +840,7 @@ where
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
T::mouse_down(self, location, device_id, button, context)
}
@ -860,7 +850,7 @@ where
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
T::mouse_drag(self, location, device_id, button, context);
}
@ -870,7 +860,7 @@ where
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
T::mouse_up(self, location, device_id, button, context);
}
@ -880,12 +870,12 @@ where
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
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)
}
@ -894,7 +884,7 @@ where
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
T::mouse_wheel(self, device_id, delta, phase, context)
}
@ -902,12 +892,12 @@ where
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
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)
}
@ -926,6 +916,16 @@ pub trait MakeWidget: Sized {
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.
///
/// This is equivalent to `Style::new(styles, self)`.
@ -1589,7 +1589,10 @@ impl PartialEq for WidgetInstance {
impl WindowBehavior for WidgetInstance {
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
}
@ -2181,11 +2184,7 @@ where
T: MountableChild,
{
/// Mounts and unmounts all children needed to be in sync with `children`.
pub fn synchronize_with(
&mut self,
children: &Value<Children>,
context: &mut EventContext<'_, '_>,
) {
pub fn synchronize_with(&mut self, children: &Value<Children>, context: &mut EventContext<'_>) {
let current_generation = children.generation();
if current_generation.map_or_else(
|| children.map(Children::len) != self.children.len(),
@ -2284,10 +2283,7 @@ impl WidgetRef {
}
/// Returns this child, mounting it in the process if necessary.
fn mounted_for_context<'window>(
&mut self,
context: &mut impl AsEventContext<'window>,
) -> &MountedWidget {
fn mounted_for_context(&mut self, context: &mut impl AsEventContext) -> &MountedWidget {
let mut context = context.as_event_context();
self.mounted
.entry(&context)
@ -2295,21 +2291,18 @@ impl WidgetRef {
}
/// 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);
}
/// Returns this child, mounting it in the process if necessary.
pub fn mounted<'window>(
&mut self,
context: &mut impl AsEventContext<'window>,
) -> MountedWidget {
pub fn mounted(&mut self, context: &mut impl AsEventContext) -> MountedWidget {
self.mounted_for_context(context).clone()
}
/// Returns this child, mounting it in the process if necessary.
#[must_use]
pub fn as_mounted(&self, context: &WidgetContext<'_, '_>) -> Option<&MountedWidget> {
pub fn as_mounted(&self, context: &WidgetContext<'_>) -> Option<&MountedWidget> {
self.mounted.get(context)
}
@ -2320,7 +2313,7 @@ impl WidgetRef {
}
/// 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();
if let Some(mounted) = self.mounted.clear_for(&context) {
context.remove_child(&mounted);
@ -2351,7 +2344,7 @@ impl PartialEq for WidgetRef {
impl ManageWidget for WidgetRef {
type Managed = Option<MountedWidget>;
fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed {
fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed {
self.mounted
.get(context)
.cloned()
@ -2374,7 +2367,7 @@ impl WidgetId {
/// Finds this widget mounted in this window, if present.
#[must_use]
pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option<MountedWidget> {
pub fn find_in(self, context: &WidgetContext<'_>) -> Option<MountedWidget> {
context.tree.widget(self)
}
}

View file

@ -87,7 +87,7 @@ impl Align {
fn measure(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Layout {
let margin = self.edges.get();
let vertical = FrameInfo::new(context.gfx.scale(), margin.top, margin.bottom);
@ -179,14 +179,14 @@ impl WrapperWidget for Align {
&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)
}
fn layout_child(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
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::{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::shapes::{Shape, StrokeOptions};
use kludgine::Color;
@ -21,7 +21,7 @@ use crate::styles::components::{
use crate::styles::{ColorExt, Styles};
use crate::value::{Destination, Dynamic, IntoValue, Source, Value};
use crate::widget::{Callback, EventHandling, MakeWidget, Widget, WidgetRef, HANDLED};
use crate::window::WindowLocal;
use crate::window::{DeviceId, WindowLocal};
use crate::FitMeasuredSize;
/// A clickable button.
@ -71,7 +71,7 @@ impl ButtonKind {
pub fn colors_for_default(
self,
visual_state: VisualState,
context: &WidgetContext<'_, '_>,
context: &WidgetContext<'_>,
) -> ButtonColors {
match self {
ButtonKind::Solid => match visual_state {
@ -171,7 +171,7 @@ impl Button {
self
}
fn invoke_on_click(&mut self, context: &WidgetContext<'_, '_>) {
fn invoke_on_click(&mut self, context: &WidgetContext<'_>) {
if context.enabled() {
if let Some(on_click) = self.on_click.as_mut() {
on_click.invoke(());
@ -179,7 +179,7 @@ impl Button {
}
}
fn visual_style(context: &WidgetContext<'_, '_>) -> VisualState {
fn visual_style(context: &WidgetContext<'_>) -> VisualState {
if !context.enabled() {
VisualState::Disabled
} else if context.active() {
@ -195,7 +195,7 @@ impl Button {
#[must_use]
pub fn colors_for_transparent(
visual_state: VisualState,
context: &WidgetContext<'_, '_>,
context: &WidgetContext<'_>,
) -> ButtonColors {
match visual_state {
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 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 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
.per_window
.entry(context)
@ -318,7 +318,7 @@ impl VisualState {
/// Returns the colors to apply to a [`ButtonKind::Solid`] [`Button`] or
/// button-like widget.
#[must_use]
pub fn solid_colors(self, context: &WidgetContext<'_, '_>) -> ButtonColors {
pub fn solid_colors(self, context: &WidgetContext<'_>) -> ButtonColors {
match self {
VisualState::Normal => ButtonColors {
background: context.get(&ButtonBackground),
@ -346,7 +346,7 @@ impl VisualState {
/// Returns the colors to apply to a [`ButtonKind::Outline`] [`Button`] or
/// button-like widget.
#[must_use]
pub fn outline_colors(self, context: &WidgetContext<'_, '_>) -> ButtonColors {
pub fn outline_colors(self, context: &WidgetContext<'_>) -> ButtonColors {
let solid = self.solid_colors(context);
ButtonColors {
background: solid.outline,
@ -364,7 +364,7 @@ impl Widget for Button {
.finish()
}
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
#![allow(clippy::similar_names)]
let current_style = self.kind.get_tracking_redraw(context);
@ -417,11 +417,11 @@ impl Widget for Button {
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
}
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()
}
@ -430,7 +430,7 @@ impl Widget for Button {
_location: Point<Px>,
_device_id: DeviceId,
_button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
self.per_window.entry(context).or_default().buttons_pressed += 1;
context.activate();
@ -442,7 +442,7 @@ impl Widget for Button {
location: Point<Px>,
_device_id: DeviceId,
_button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
let changed = if Rect::from(context.last_layout().expect("must have been rendered").size)
.contains(location)
@ -462,7 +462,7 @@ impl Widget for Button {
location: Option<Point<Px>>,
_device_id: DeviceId,
_button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
let window_local = self.per_window.entry(context).or_default();
window_local.buttons_pressed -= 1;
@ -484,7 +484,7 @@ impl Widget for Button {
fn layout(
&mut self,
available_space: Size<crate::ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
let padding = context
.get(&IntrinsicPadding)
@ -502,14 +502,14 @@ impl Widget for Button {
size + double_padding
}
fn unhover(&mut self, context: &mut EventContext<'_, '_>) {
fn unhover(&mut self, context: &mut EventContext<'_>) {
self.update_colors(context, false);
}
fn hover(
&mut self,
_location: Point<Px>,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> Option<CursorIcon> {
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();
}
fn blur(&mut self, context: &mut EventContext<'_, '_>) {
fn blur(&mut self, context: &mut EventContext<'_>) {
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();
// If we have no buttons pressed, the event should fire on activate not
// on deactivate.
@ -538,11 +538,11 @@ impl Widget for Button {
self.update_colors(context, true);
}
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {
fn deactivate(&mut self, context: &mut EventContext<'_>) {
self.update_colors(context, false);
}
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.content.unmount_in(context);
}
}

View file

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

View file

@ -175,7 +175,7 @@ struct CheckboxOrnament {
}
impl Widget for CheckboxOrnament {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
let checkbox_size = context
.gfx
.region()
@ -244,7 +244,7 @@ impl Widget for CheckboxOrnament {
fn layout(
&mut self,
_available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<figures::units::UPx> {
let checkbox_size = context.get(&CheckboxSize).into_upx(context.gfx.scale());
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) {
(EasingFunction::from(EaseOutQuadradic), Px::ZERO)
} else {
@ -90,7 +90,7 @@ impl WrapperWidget for Collapse {
&mut self,
size: Size<Px>,
_available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
let clip_size = self.size.get_tracking_invalidate(context);
if self.vertical {

View file

@ -4,7 +4,7 @@ use std::ops::Range;
use figures::units::{Lp, Px};
use figures::{FloatConversion, Point, Rect, Round, ScreenScale, Zero};
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::{Color, DrawableExt, Origin};
@ -14,6 +14,7 @@ use crate::styles::components::{HighlightColor, OutlineColor, TextColor};
use crate::styles::{ColorExt, ColorSource};
use crate::value::{Destination, Dynamic, IntoValue, Source, Value};
use crate::widget::{EventHandling, Widget, HANDLED};
use crate::window::DeviceId;
/// A widget that selects a [`ColorSource`].
#[derive(Debug)]
@ -69,7 +70,7 @@ impl 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 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)
}
@ -157,7 +158,7 @@ impl Widget for ColorSourcePicker {
location: Point<Px>,
_device_id: DeviceId,
_button: MouseButton,
_context: &mut EventContext<'_, '_>,
_context: &mut EventContext<'_>,
) -> EventHandling {
self.update_from_mouse(location);
HANDLED
@ -168,7 +169,7 @@ impl Widget for ColorSourcePicker {
location: Point<Px>,
_device_id: DeviceId,
_button: MouseButton,
_context: &mut EventContext<'_, '_>,
_context: &mut EventContext<'_>,
) {
self.update_from_mouse(location);
}
@ -180,7 +181,7 @@ fn draw_gradient_segment(
height: Px,
hue: Range<f32>,
lightness: ZeroToOne,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_>,
) {
let mid_left = (
Point::new(start.x, start.y + height / 2),

View file

@ -158,7 +158,7 @@ impl Container {
self
}
fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_, '_>) -> Edges<Px> {
fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_>) -> Edges<Px> {
match &self.padding {
Some(padding) => padding.get(),
None => Edges::from(context.get(&IntrinsicPadding)),
@ -166,7 +166,7 @@ impl Container {
.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() {
ContainerBackground::Color(color) => EffectiveBackground::Color(color),
ContainerBackground::Level(level) => EffectiveBackground::Level(level),
@ -206,7 +206,7 @@ impl Widget for Container {
.finish()
}
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
fn unmounted(&mut self, context: &mut EventContext<'_>) {
self.child.unmount_in(context);
}
@ -215,7 +215,7 @@ impl Widget for Container {
}
#[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 background = self.effective_background_color(context);
@ -251,7 +251,7 @@ impl Widget for Container {
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
let child = self.child.mounted(context);
@ -308,7 +308,7 @@ impl Widget for Container {
fn root_behavior(
&mut self,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> Option<(RootBehavior, WidgetInstance)> {
let mut padding = self
.padding
@ -345,7 +345,7 @@ fn render_shadow(
mut corner_radii: CornerRadii<Px>,
shadow: &ContainerShadow<Px>,
background: Color,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_>,
) {
let shadow_color = shadow.color.unwrap_or_else(|| context.theme_pair().shadow);
let shadow_color =
@ -588,7 +588,7 @@ fn shadow_arc(
solid_color: Color,
transparent_color: Color,
start_angle: Angle,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
context: &mut GraphicsContext<'_, '_, '_, '_>,
) {
let full_radius = radius + gradient;
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::{Point, Size};
use kludgine::app::winit::event::{
DeviceId, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase,
};
use kludgine::app::winit::event::{Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase};
use kludgine::app::winit::window::CursorIcon;
use kludgine::Color;
@ -13,6 +11,7 @@ use crate::styles::VisualOrder;
use crate::value::{IntoValue, Value};
use crate::widget::{EventHandling, MakeWidget, WidgetRef, WrappedLayout, WrapperWidget, IGNORED};
use crate::widgets::Space;
use crate::window::DeviceId;
use crate::ConstraintLimit;
/// A callback-based custom widget.
@ -121,8 +120,8 @@ impl Custom {
where
Redraw: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'clip, 'gfx, 'pass>,
),
{
self.redraw_background = Some(Box::new(redraw));
@ -143,8 +142,8 @@ impl Custom {
where
Redraw: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'clip, 'gfx, 'pass>,
),
{
self.redraw_foreground = Some(Box::new(redraw));
@ -156,8 +155,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::mounted`].
pub fn on_mounted<Mounted>(mut self, mounted: Mounted) -> Self
where
Mounted:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Mounted: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.mounted = Some(Box::new(mounted));
self
@ -169,8 +167,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::unmounted`].
pub fn on_unmounted<Mounted>(mut self, mounted: Mounted) -> Self
where
Mounted:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Mounted: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.unmounted = Some(Box::new(mounted));
self
@ -181,8 +178,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::unhover`].
pub fn on_unhover<Unhover>(mut self, unhovered: Unhover) -> Self
where
Unhover:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Unhover: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.unhover = Some(Box::new(unhovered));
self
@ -193,8 +189,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::focus`].
pub fn on_focus<Focused>(mut self, focus: Focused) -> Self
where
Focused:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Focused: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.focus = Some(Box::new(focus));
self
@ -205,7 +200,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::blur`].
pub fn on_blur<Blur>(mut self, blur: Blur) -> Self
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
@ -216,8 +211,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::activate`].
pub fn on_activate<Activated>(mut self, activated: Activated) -> Self
where
Activated:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Activated: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.activate = Some(Box::new(activated));
self
@ -228,8 +222,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::deactivate`].
pub fn on_deactivate<Deactivated>(mut self, deactivated: Deactivated) -> Self
where
Deactivated:
Send + 'static + for<'context, 'window> FnMut(&mut EventContext<'context, 'window>),
Deactivated: Send + 'static + for<'context> FnMut(&mut EventContext<'context>),
{
self.deactivate = Some(Box::new(deactivated));
self
@ -241,9 +234,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::accept_focus`].
pub fn on_accept_focus<AcceptFocus>(mut self, accept: AcceptFocus) -> Self
where
AcceptFocus: Send
+ 'static
+ for<'context, 'window> FnMut(&mut EventContext<'context, 'window>) -> bool,
AcceptFocus: Send + 'static + for<'context> FnMut(&mut EventContext<'context>) -> bool,
{
self.accept_focus = Some(Box::new(accept));
self
@ -256,9 +247,7 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::allow_blur`].
pub fn on_allow_blur<AllowBlur>(mut self, allow_blur: AllowBlur) -> Self
where
AllowBlur: Send
+ 'static
+ for<'context, 'window> FnMut(&mut EventContext<'context, 'window>) -> bool,
AllowBlur: Send + 'static + for<'context> FnMut(&mut EventContext<'context>) -> bool,
{
self.allow_blur = Some(Box::new(allow_blur));
self
@ -274,10 +263,7 @@ impl Custom {
where
AdvanceFocus: Send
+ 'static
+ for<'context, 'window> FnMut(
VisualOrder,
&mut EventContext<'context, 'window>,
) -> EventHandling,
+ for<'context> FnMut(VisualOrder, &mut EventContext<'context>) -> EventHandling,
{
self.advance_focus = Some(Box::new(advance_focus));
self
@ -295,9 +281,9 @@ impl Custom {
where
AdjustChildConstraints: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>,
&mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> Size<ConstraintLimit>,
{
self.adjust_child = Some(Box::new(adjust_child_constraints));
@ -311,10 +297,10 @@ impl Custom {
where
PositionChild: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<Px>,
Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>,
&mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> WrappedLayout,
{
self.position_child = Some(Box::new(position_child));
@ -327,9 +313,8 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::hit_test`].
pub fn on_hit_test<HitTest>(mut self, hit_test: HitTest) -> Self
where
HitTest: Send
+ 'static
+ for<'context, 'window> FnMut(Point<Px>, &mut EventContext<'context, 'window>) -> bool,
HitTest:
Send + 'static + for<'context> FnMut(Point<Px>, &mut EventContext<'context>) -> bool,
{
self.hit_test = Some(Box::new(hit_test));
self
@ -342,10 +327,7 @@ impl Custom {
where
Hover: Send
+ 'static
+ for<'context, 'window> FnMut(
Point<Px>,
&mut EventContext<'context, 'window>,
) -> Option<CursorIcon>,
+ for<'context> FnMut(Point<Px>, &mut EventContext<'context>) -> Option<CursorIcon>,
{
self.hover = Some(Box::new(hover));
self
@ -364,11 +346,11 @@ impl Custom {
where
MouseDown: Send
+ 'static
+ for<'context, 'window> FnMut(
+ for<'context> FnMut(
Point<Px>,
DeviceId,
MouseButton,
&mut EventContext<'context, 'window>,
&mut EventContext<'context>,
) -> EventHandling,
{
self.mouse_down = Some(Box::new(mouse_down));
@ -383,12 +365,7 @@ impl Custom {
where
MouseDrag: Send
+ 'static
+ for<'context, 'window> FnMut(
Point<Px>,
DeviceId,
MouseButton,
&mut EventContext<'context, 'window>,
),
+ for<'context> FnMut(Point<Px>, DeviceId, MouseButton, &mut EventContext<'context>),
{
self.mouse_drag = Some(Box::new(mouse_drag));
self
@ -401,11 +378,11 @@ impl Custom {
where
MouseUp: Send
+ 'static
+ for<'context, 'window> FnMut(
+ for<'context> FnMut(
Option<Point<Px>>,
DeviceId,
MouseButton,
&mut EventContext<'context, 'window>,
&mut EventContext<'context>,
),
{
self.mouse_up = Some(Box::new(mouse_up));
@ -417,9 +394,8 @@ impl Custom {
/// This callback corresponds to [`WrapperWidget::ime`].
pub fn on_ime<OnIme>(mut self, ime: OnIme) -> Self
where
OnIme: Send
+ 'static
+ for<'context, 'window> FnMut(Ime, &mut EventContext<'context, 'window>) -> EventHandling,
OnIme:
Send + 'static + for<'context> FnMut(Ime, &mut EventContext<'context>) -> EventHandling,
{
self.ime = Some(Box::new(ime));
self
@ -432,11 +408,11 @@ impl Custom {
where
KeyboardInput: Send
+ 'static
+ for<'context, 'window> FnMut(
+ for<'context> FnMut(
DeviceId,
KeyEvent,
bool,
&mut EventContext<'context, 'window>,
&mut EventContext<'context>,
) -> EventHandling,
{
self.keyboard_input = Some(Box::new(keyboard_input));
@ -450,11 +426,11 @@ impl Custom {
where
MouseWheel: Send
+ 'static
+ for<'context, 'window> FnMut(
+ for<'context> FnMut(
DeviceId,
MouseScrollDelta,
TouchPhase,
&mut EventContext<'context, 'window>,
&mut EventContext<'context>,
) -> EventHandling,
{
self.mouse_wheel = Some(Box::new(mouse_wheel));
@ -467,13 +443,13 @@ impl WrapperWidget for Custom {
&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 {
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 {
redraw.invoke(context);
}
@ -482,7 +458,7 @@ impl WrapperWidget for Custom {
fn adjust_child_constraints(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> {
if let Some(adjust_child) = &mut self.adjust_child {
adjust_child.invoke(available_space, context)
@ -495,7 +471,7 @@ impl WrapperWidget for Custom {
&mut self,
size: Size<Px>,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
if let Some(position_child) = &mut self.position_child {
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
.as_ref()
.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 {
mounted.invoke(context);
}
}
fn unmounted(&mut self, context: &mut EventContext<'_, '_>) {
fn unmounted(&mut self, context: &mut EventContext<'_>) {
if let Some(unmounted) = &mut self.unmounted {
unmounted.invoke(context);
} 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 {
hit_test.invoke(location, context)
} else {
@ -540,22 +516,18 @@ impl WrapperWidget for Custom {
}
}
fn hover(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Option<CursorIcon> {
fn hover(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Option<CursorIcon> {
let hover = self.hover.as_mut()?;
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 {
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 {
accept_focus.invoke(context)
} 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 {
focus.invoke(context);
}
}
fn blur(&mut self, context: &mut EventContext<'_, '_>) {
fn blur(&mut self, context: &mut EventContext<'_>) {
if let Some(blur) = &mut self.blur {
blur.invoke(context);
}
}
fn activate(&mut self, context: &mut EventContext<'_, '_>) {
fn activate(&mut self, context: &mut EventContext<'_>) {
if let Some(activate) = &mut self.activate {
activate.invoke(context);
}
}
fn deactivate(&mut self, context: &mut EventContext<'_, '_>) {
fn deactivate(&mut self, context: &mut EventContext<'_>) {
if let Some(deactivate) = &mut self.deactivate {
deactivate.invoke(context);
}
@ -592,7 +564,7 @@ impl WrapperWidget for Custom {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
if let Some(mouse_down) = &mut self.mouse_down {
mouse_down.invoke(location, device_id, button, context)
@ -606,7 +578,7 @@ impl WrapperWidget for Custom {
location: Point<Px>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
if let Some(mouse_drag) = &mut self.mouse_drag {
mouse_drag.invoke(location, device_id, button, context);
@ -618,7 +590,7 @@ impl WrapperWidget for Custom {
location: Option<Point<Px>>,
device_id: DeviceId,
button: MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
if let Some(mouse_up) = &mut self.mouse_up {
mouse_up.invoke(location, device_id, button, context);
@ -630,7 +602,7 @@ impl WrapperWidget for Custom {
device_id: DeviceId,
input: KeyEvent,
is_synthetic: bool,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
if let Some(keyboard_input) = &mut self.keyboard_input {
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 {
f.invoke(ime, context)
} else {
@ -652,7 +624,7 @@ impl WrapperWidget for Custom {
device_id: DeviceId,
delta: MouseScrollDelta,
phase: TouchPhase,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
if let Some(mouse_wheel) = &mut self.mouse_wheel {
mouse_wheel.invoke(device_id, delta, phase, context)
@ -664,7 +636,7 @@ impl WrapperWidget for Custom {
fn advance_focus(
&mut self,
direction: VisualOrder,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
if let Some(advance_focus) = &mut self.advance_focus {
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 {
allow_blur.invoke(context)
} else {
@ -683,18 +655,16 @@ impl WrapperWidget for Custom {
}
trait RedrawFunc: Send {
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>);
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>);
}
impl<Func> RedrawFunc for Func
where
Func: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
&mut GraphicsContext<'context, 'window, 'clip, 'gfx, 'pass>,
),
+ for<'context, 'clip, 'gfx, 'pass> FnMut(&mut GraphicsContext<'context, 'clip, 'gfx, 'pass>),
{
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
self(context);
}
}
@ -703,7 +673,7 @@ trait AdjustChildConstraintsFunc: Send {
fn invoke(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit>;
}
@ -711,15 +681,15 @@ impl<Func> AdjustChildConstraintsFunc for Func
where
Func: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>,
&mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> Size<ConstraintLimit>,
{
fn invoke(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<ConstraintLimit> {
self(available_space, context)
}
@ -730,7 +700,7 @@ trait PositionChildFunc: Send {
&mut self,
size: Size<Px>,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout;
}
@ -738,45 +708,44 @@ impl<Func> PositionChildFunc for Func
where
Func: Send
+ 'static
+ for<'context, 'window, 'clip, 'gfx, 'pass> FnMut(
+ for<'context, 'clip, 'gfx, 'pass> FnMut(
Size<Px>,
Size<ConstraintLimit>,
&mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>,
&mut LayoutContext<'context, 'clip, 'gfx, 'pass>,
) -> WrappedLayout,
{
fn invoke(
&mut self,
size: Size<Px>,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> WrappedLayout {
self(size, available_space, context)
}
}
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
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)
}
}
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
where
Func:
Send + 'static + for<'context, 'window> FnMut(P, &mut EventContext<'context, 'window>) -> R,
Func: Send + 'static + for<'context> FnMut(P, &mut EventContext<'context>) -> R,
{
fn invoke(&mut self, location: P, context: &mut EventContext<'_, '_>) -> R {
fn invoke(&mut self, location: P, context: &mut EventContext<'_>) -> R {
self(location, context)
}
}
@ -787,7 +756,7 @@ trait ThreeParamEventFunc<P1, P2, P3, R = ()>: Send {
location: P1,
device_id: P2,
button: P3,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> 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
where
Func: Send
+ 'static
+ for<'context, 'window> FnMut(P1, P2, P3, &mut EventContext<'context, 'window>) -> R,
Func: Send + 'static + for<'context> FnMut(P1, P2, P3, &mut EventContext<'context>) -> R,
{
fn invoke(
&mut self,
location: P1,
device_id: P2,
button: P3,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> R {
self(location, device_id, button, context)
}

View file

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

View file

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

View file

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

View file

@ -112,7 +112,7 @@ impl 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| {
let size = texture.size().into_signed();
let rect = match self.scaling.get() {
@ -147,7 +147,7 @@ impl Widget for Image {
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
match self.scaling.get_tracking_invalidate(context) {
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() {
return;
}
@ -200,7 +200,7 @@ where
});
}
fn delete(&mut self, context: &mut EventContext<'_, '_>) {
fn delete(&mut self, context: &mut EventContext<'_>) {
if !context.enabled() {
return;
}
@ -231,7 +231,7 @@ where
&mut self,
direction: Affinity,
mode: CursorNavigationMode,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
if !matches!(mode, CursorNavigationMode::Line) {
self.line_navigation_x_target = None;
@ -315,11 +315,7 @@ where
}
}
fn move_cursor_by_line_extent(
&mut self,
affinity: Affinity,
context: &mut EventContext<'_, '_>,
) {
fn move_cursor_by_line_extent(&mut self, affinity: Affinity, context: &mut EventContext<'_>) {
let Some(cache) = self.cache.as_ref() else {
return;
};
@ -339,7 +335,7 @@ where
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 {
return;
};
@ -406,13 +402,13 @@ where
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() {
return;
}
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) {
Ok(()) => {}
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() {
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() {
return false;
}
match context
.cushy()
.clipboard_guard()
.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()) {
(ElementState::Pressed, Key::Named(key @ (NamedKey::Backspace| NamedKey::Delete)), _) => {
match key {
@ -547,11 +544,7 @@ where
}
}
fn layout_text(
&mut self,
width: Option<Px>,
context: &mut GraphicsContext<'_, '_, '_, '_, '_>,
) {
fn layout_text(&mut self, width: Option<Px>, context: &mut GraphicsContext<'_, '_, '_, '_>) {
context.invalidate_when_changed(&self.value);
let mut key = {
@ -808,11 +801,7 @@ where
}
}
fn cursor_from_point(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
) -> Cursor {
fn cursor_from_point(&mut self, location: Point<Px>, context: &mut EventContext<'_>) -> Cursor {
let mut cursor = self.cached_cursor_from_point(location, context);
if let Some(symbol) = self.mask.graphemes(true).next() {
let grapheme_offset = cursor.offset / symbol.len();
@ -831,7 +820,7 @@ where
fn cached_cursor_from_point(
&mut self,
location: Point<Px>,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> Cursor {
let Some(cache) = &self.cache else {
return Cursor::default();
@ -973,20 +962,20 @@ impl<Storage> Widget for Input<Storage>
where
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
}
fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool {
fn accept_focus(&mut self, _context: &mut EventContext<'_>) -> bool {
true
}
fn mouse_down(
&mut self,
location: Point<Px>,
_device_id: kludgine::app::winit::event::DeviceId,
_device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
self.mouse_buttons_down += 1;
context.focus();
@ -1000,7 +989,7 @@ where
fn hover(
&mut self,
_location: Point<Px>,
_context: &mut EventContext<'_, '_>,
_context: &mut EventContext<'_>,
) -> Option<CursorIcon> {
Some(CursorIcon::Text)
}
@ -1008,9 +997,9 @@ where
fn mouse_drag(
&mut self,
location: Point<Px>,
_device_id: kludgine::app::winit::event::DeviceId,
_device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) {
let cursor_location = self.cursor_from_point(location, context);
if self.selection.cursor != cursor_location {
@ -1023,15 +1012,15 @@ where
fn mouse_up(
&mut self,
_location: Option<Point<Px>>,
_device_id: kludgine::app::winit::event::DeviceId,
_device_id: crate::window::DeviceId,
_button: kludgine::app::winit::event::MouseButton,
_context: &mut EventContext<'_, '_>,
_context: &mut EventContext<'_>,
) {
self.mouse_buttons_down -= 1;
}
#[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 {
self.needs_to_select_all = false;
self.select_all();
@ -1177,7 +1166,7 @@ where
fn layout(
&mut self,
available_space: Size<ConstraintLimit>,
context: &mut LayoutContext<'_, '_, '_, '_, '_>,
context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
let padding = context
.get(&IntrinsicPadding)
@ -1199,10 +1188,10 @@ where
fn keyboard_input(
&mut self,
_device_id: kludgine::app::winit::event::DeviceId,
_device_id: crate::window::DeviceId,
input: kludgine::app::winit::event::KeyEvent,
_is_synthetic: bool,
context: &mut EventContext<'_, '_>,
context: &mut EventContext<'_>,
) -> EventHandling {
if let Some(on_key) = &mut self.on_key {
on_key.invoke(input.clone())?;
@ -1219,7 +1208,7 @@ where
handled
}
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_, '_>) -> EventHandling {
fn ime(&mut self, ime: Ime, context: &mut EventContext<'_>) -> EventHandling {
match ime {
Ime::Enabled | Ime::Disabled => {}
Ime::Preedit(text, cursor) => {
@ -1234,7 +1223,7 @@ where
HANDLED
}
fn focus(&mut self, context: &mut EventContext<'_, '_>) {
fn focus(&mut self, context: &mut EventContext<'_>) {
if self.mouse_buttons_down == 0 {
self.needs_to_select_all = true;
}
@ -1248,7 +1237,7 @@ where
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_needs_redraw();
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,8 @@ use figures::Size;
use kludgine::Color;
use crate::context::{GraphicsContext, LayoutContext};
use crate::styles::components::PrimaryColor;
use crate::styles::{DynamicComponent, IntoDynamicComponentValue};
use crate::value::{IntoValue, Value};
use crate::widget::Widget;
use crate::ConstraintLimit;
@ -10,7 +12,7 @@ use crate::ConstraintLimit;
/// A widget that occupies space, optionally filling it with a color.
#[derive(Debug, Clone)]
pub struct Space {
color: Value<Color>,
color: Value<ColorSource>,
}
impl Default for Space {
@ -24,7 +26,7 @@ impl Space {
#[must_use]
pub const fn clear() -> Self {
Self {
color: Value::Constant(Color::CLEAR_BLACK),
color: Value::Constant(ColorSource::Color(Color::CLEAR_BLACK)),
}
}
@ -32,22 +34,52 @@ impl Space {
#[must_use]
pub fn colored(color: impl IntoValue<Color>) -> 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 {
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) {
let color = self.color.get_tracking_redraw(context);
fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) {
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);
}
fn layout(
&mut self,
_available_space: Size<ConstraintLimit>,
_context: &mut LayoutContext<'_, '_, '_, '_, '_>,
_context: &mut LayoutContext<'_, '_, '_, '_>,
) -> Size<UPx> {
Size::default()
}
}
#[derive(Debug, PartialEq, Clone)]
enum ColorSource {
Color(Color),
Dynamic(DynamicComponent),
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff