diff --git a/.gitignore b/.gitignore index 9f97022..97c1151 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ -target/ \ No newline at end of file +target/ +examples/offscreen*.png \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index a9326a4..bca7823 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -574,6 +574,7 @@ dependencies = [ "ahash", "alot", "arboard", + "bytemuck", "cushy-macros", "figures", "image", @@ -1179,7 +1180,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#9999ff4a323a6dec1deebb9a0a1825d561559478" dependencies = [ "ahash", "alot", @@ -1827,9 +1828,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 +1859,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "2de98502f212cfcea8d0bb305bd0f49d7ebdd75b64ba0a68f937d888f4e0d6db" dependencies = [ "unicode-ident", ] @@ -1891,9 +1892,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 +2174,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 +2332,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "89456b690ff72fddcecf231caedbe615c59480c93358a93dfae7fc29e3ebbf0e" dependencies = [ "proc-macro2", "quote", @@ -2360,18 +2361,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 +3186,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", diff --git a/Cargo.toml b/Cargo.toml index c8f633c..00cc8ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,8 @@ 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" +bytemuck = "1.14.0" # [patch.crates-io] @@ -57,7 +59,6 @@ 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"] } diff --git a/examples/custom-widgets.rs b/examples/custom-widgets.rs index 1e68df8..9891f40 100644 --- a/examples/custom-widgets.rs +++ b/examples/custom-widgets.rs @@ -90,14 +90,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, - context: &mut cushy::context::LayoutContext<'_, '_, '_, '_, '_>, + context: &mut cushy::context::LayoutContext<'_, '_, '_, '_>, ) -> Size { Size::new( available_space.width.min(), @@ -108,7 +108,7 @@ impl Widget for Toggle { fn hit_test( &mut self, _location: figures::Point, - _context: &mut cushy::context::EventContext<'_, '_>, + _context: &mut cushy::context::EventContext<'_>, ) -> bool { true } @@ -118,7 +118,7 @@ impl Widget for Toggle { _location: figures::Point, _device_id: kludgine::app::winit::event::DeviceId, _button: kludgine::app::winit::event::MouseButton, - _context: &mut cushy::context::EventContext<'_, '_>, + _context: &mut cushy::context::EventContext<'_>, ) -> cushy::widget::EventHandling { self.value.toggle(); diff --git a/examples/offscreen.rs b/examples/offscreen.rs new file mode 100644 index 0000000..dca0bd9 --- /dev/null +++ b/examples/offscreen.rs @@ -0,0 +1,46 @@ +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(); + image::save_buffer_with_format( + "examples/offscreen.png", + recorder.bytes(), + recorder.size().width.get(), + recorder.size().height.get(), + image::ColorType::Rgb8, + image::ImageFormat::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(); + image::save_buffer_with_format( + "examples/offscreen-transparent.png", + recorder.bytes(), + recorder.size().width.get(), + recorder.size().height.get(), + image::ColorType::Rgba8, + image::ImageFormat::Png, + ) + .unwrap(); +} + +#[test] +fn runs() { + main(); +} diff --git a/src/app.rs b/src/app.rs index 4b8742b..ab5c063 100644 --- a/src/app.rs +++ b/src/app.rs @@ -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, + app: Option>, cushy: Cushy, } @@ -102,7 +106,10 @@ impl Application for App { impl AsApplication> for App { fn as_application(&self) -> &dyn kludgine::app::Application> { - self.app.as_application() + self.app + .as_ref() + .map(AsApplication::as_application) + .expect("no app") } } diff --git a/src/context.rs b/src/context.rs index 1ceda6c..417d3b0 100644 --- a/src/context.rs +++ b/src/context.rs @@ -21,16 +21,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, 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 +38,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 +55,10 @@ impl<'context, 'window> EventContext<'context, 'window> { pub fn for_other<'child, Widget>( &'child mut self, widget: &Widget, - ) -> >>::Result + ) -> >>::Result where Widget: ManageWidget, - Widget::Managed: MapManagedWidget>, + Widget::Managed: MapManagedWidget>, { widget .manage(self) @@ -177,7 +174,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 +187,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 +478,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 +521,18 @@ impl 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 +544,10 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> GraphicsContext<'context, 'window, ' pub fn for_other<'child, Widget>( &'child mut self, widget: &Widget, - ) -> , - >>::Result + ) -> >>::Result where Widget: ManageWidget, - Widget::Managed: MapManagedWidget>, + Widget::Managed: MapManagedWidget>, { let opacity = self.get(&Opacity); widget.manage(self).map(|widget| { @@ -577,7 +573,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) -> GraphicsContext<'_, 'window, '_, 'gfx, 'pass> { + pub fn clipped_to(&mut self, clip: Rect) -> GraphicsContext<'_, '_, 'gfx, 'pass> { GraphicsContext { widget: self.widget.borrowed(), gfx: Exclusive::Owned(self.gfx.clipped_to(clip)), @@ -675,7 +671,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 +679,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 +726,10 @@ impl<'context, 'window, 'clip, 'gfx, 'pass> LayoutContext<'context, 'window, 'cl pub fn for_other<'child, Widget>( &'child mut self, widget: &Widget, - ) -> >>::Result + ) -> >>::Result where Widget: ManageWidget, - Widget::Managed: MapManagedWidget>, + Widget::Managed: MapManagedWidget>, { widget.manage(self).map(|widget| LayoutContext { graphics: self.graphics.for_other(&widget), @@ -782,36 +772,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 +821,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 +837,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 +848,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 +883,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 +900,10 @@ impl<'context, 'window> WidgetContext<'context, 'window> { pub fn for_other<'child, Widget>( &'child mut self, widget: &Widget, - ) -> >>::Result + ) -> >>::Result where Widget: ManageWidget, - Widget::Managed: MapManagedWidget>, + Widget::Managed: MapManagedWidget>, { widget.manage(self).map(|current_node| { let (effective_styles, theme, theme_mode) = current_node.overidden_theme(); @@ -1159,13 +1143,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 +1185,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 +1193,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 +1252,13 @@ pub trait ManageWidget { type Managed: MapManagedWidget; /// 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; - fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed { context.tree.widget(*self) } } @@ -1284,7 +1266,7 @@ impl ManageWidget for WidgetId { impl ManageWidget for WidgetInstance { type Managed = Option; - fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed { context.tree.widget(self.id()) } } @@ -1292,7 +1274,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 +1330,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 +1340,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, { diff --git a/src/debug.rs b/src/debug.rs index bcf98fa..95c3052 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -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)] @@ -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) } } diff --git a/src/styles.rs b/src/styles.rs index 14929c6..9fc7b61 100644 --- a/src/styles.rs +++ b/src/styles.rs @@ -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( - component: &Value, - context: &WidgetContext<'_, '_>, - ) -> Option + fn resolve_component(component: &Value, context: &WidgetContext<'_>) -> Option where T: ComponentType, { @@ -162,7 +159,7 @@ impl Styles { pub fn try_get( &self, component: &Named, - context: &WidgetContext<'_, '_>, + context: &WidgetContext<'_>, ) -> Option 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( - &self, - component: &Named, - context: &WidgetContext<'_, '_>, - ) -> Named::ComponentType + pub fn get(&self, component: &Named, context: &WidgetContext<'_>) -> Named::ComponentType where Named: ComponentDefinition + ?Sized, { @@ -440,10 +433,8 @@ impl Component { #[must_use] pub fn dynamic(resolve: Func) -> Self where - Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option - + Send - + Sync - + 'static, + Func: + for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option + 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. @@ -2535,19 +2526,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; + fn resolve_component(&self, context: &WidgetContext<'_>) -> Option; } struct DynamicFunctionWrapper(F); impl DynamicComponentResolver for DynamicFunctionWrapper where - T: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option + T: for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option + Send + Sync + 'static, { - fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option { + fn resolve_component(&self, context: &WidgetContext<'_>) -> Option { self.0(context) } } @@ -2556,7 +2547,7 @@ impl DynamicComponentResolver for T where T: ComponentDefinition + Clone + Send + Sync + 'static, { - fn resolve_component(&self, context: &WidgetContext<'_, '_>) -> Option { + fn resolve_component(&self, context: &WidgetContext<'_>) -> Option { Some(context.get(self).into_component()) } } @@ -2576,7 +2567,7 @@ impl DynamicComponent { #[must_use] pub fn new(resolve: Func) -> Self where - Func: for<'a, 'context, 'widget> Fn(&'a WidgetContext<'context, 'widget>) -> Option + Func: for<'a, 'context> Fn(&'a WidgetContext<'context>) -> Option + Send + Sync + 'static, @@ -2587,7 +2578,7 @@ impl DynamicComponent { /// Invokes the resolver function, optionally returning a resolved /// component. #[must_use] - pub fn resolve(&self, context: &WidgetContext<'_, '_>) -> Option { + pub fn resolve(&self, context: &WidgetContext<'_>) -> Option { self.0.resolve_component(context) } } diff --git a/src/styles/components.rs b/src/styles/components.rs index 58ee737..8cd92f2 100644 --- a/src/styles/components.rs +++ b/src/styles/components.rs @@ -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)* } }; diff --git a/src/tick.rs b/src/tick.rs index daad98e..b1dfe70 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -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(); diff --git a/src/value.rs b/src/value.rs index 265d931..0f7ead2 100644 --- a/src/value.rs +++ b/src/value.rs @@ -103,7 +103,7 @@ pub trait Source { /// 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 { /// 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, @@ -2508,7 +2508,7 @@ impl Value { /// updated. pub fn map_tracking_redraw( &self, - context: &WidgetContext<'_, '_>, + context: &WidgetContext<'_>, map: impl FnOnce(&T) -> R, ) -> R { match self { @@ -2526,7 +2526,7 @@ impl Value { /// updated. pub fn map_tracking_invalidate( &self, - context: &WidgetContext<'_, '_>, + context: &WidgetContext<'_>, map: impl FnOnce(&T) -> R, ) -> R { match self { @@ -2573,7 +2573,7 @@ impl Value { /// /// 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 +2584,7 @@ impl Value { /// /// 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, { diff --git a/src/widget.rs b/src/widget.rs index 927d9e5..b1e43f3 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -45,7 +45,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::{ + Rgb8, RunningWindow, ThemeMode, VirtualRecorderBuilder, VirtualWindowBuilder, Window, + WindowBehavior, WindowHandle, WindowLocal, +}; use crate::ConstraintLimit; /// A type that makes up a graphical user interface. @@ -264,7 +268,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 +301,46 @@ pub trait Widget: Send + Debug + 'static { fn layout( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, 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, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, location: Point, context: &mut EventContext<'_>) -> Option { 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 +349,7 @@ pub trait Widget: Send + Debug + 'static { fn advance_focus( &mut self, direction: VisualOrder, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { IGNORED } @@ -357,21 +357,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 +384,7 @@ pub trait Widget: Send + Debug + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { IGNORED } @@ -397,7 +397,7 @@ pub trait Widget: Send + Debug + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { } @@ -408,7 +408,7 @@ pub trait Widget: Send + Debug + 'static { location: Option>, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { } @@ -420,7 +420,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 +428,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 +440,7 @@ pub trait Widget: Send + Debug + 'static { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { IGNORED } @@ -451,7 +451,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 +544,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 { + fn root_behavior(&mut self, context: &mut EventContext<'_>) -> Option { None } @@ -552,13 +552,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 +566,7 @@ pub trait WrapperWidget: Debug + Send + 'static { fn layout_child( &mut self, available_space: Size, - 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 +584,7 @@ pub trait WrapperWidget: Debug + Send + 'static { fn adjust_child_constraints( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { available_space } @@ -596,7 +596,7 @@ pub trait WrapperWidget: Debug + Send + 'static { &mut self, size: Size, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout { Size::new( available_space @@ -612,7 +612,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 { + fn background_color(&mut self, context: &WidgetContext<'_>) -> Option { // WidgetBackground is already filled, so we don't need to do anything // else by default. None @@ -620,39 +620,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, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, 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, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, location: Point, context: &mut EventContext<'_>) -> Option { 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 +659,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 +698,7 @@ pub trait WrapperWidget: Debug + Send + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { IGNORED } @@ -715,7 +711,7 @@ pub trait WrapperWidget: Debug + Send + 'static { location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { } @@ -726,7 +722,7 @@ pub trait WrapperWidget: Debug + Send + 'static { location: Option>, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { } @@ -738,7 +734,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 +742,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 +754,7 @@ pub trait WrapperWidget: Debug + Send + 'static { device_id: DeviceId, delta: MouseScrollDelta, phase: TouchPhase, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { IGNORED } @@ -770,13 +766,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 +789,7 @@ where fn layout( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { let layout = self.layout_child(available_space, context); let child = self.child_mut().mounted(&mut context.as_event_context()); @@ -801,47 +797,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, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, context: &mut EventContext<'_>) -> bool { T::hit_test(self, location, context) } - fn hover( - &mut self, - location: Point, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, location: Point, context: &mut EventContext<'_>) -> Option { 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 +842,7 @@ where location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { T::mouse_down(self, location, device_id, button, context) } @@ -860,7 +852,7 @@ where location: Point, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { T::mouse_drag(self, location, device_id, button, context); } @@ -870,7 +862,7 @@ where location: Option>, device_id: DeviceId, button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { T::mouse_up(self, location, device_id, button, context); } @@ -880,12 +872,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 +886,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 +894,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 +918,16 @@ pub trait MakeWidget: Sized { Window::new(self.make_widget()) } + /// Returns a builder for a [`VirtualWindow`](crate::window::VirtualWindow). + fn build_virtual_window(self) -> VirtualWindowBuilder { + VirtualWindowBuilder::new(self) + } + + /// Returns a builder for a [`VirtualRecorder`](crate::window::VirtualRecorder) + fn build_recorder(self) -> VirtualRecorderBuilder { + VirtualRecorderBuilder::new(self) + } + /// Associates `styles` with this widget. /// /// This is equivalent to `Style::new(styles, self)`. @@ -1589,7 +1591,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>, + context: Self::Context, + ) -> Self { context } @@ -2181,11 +2186,7 @@ where T: MountableChild, { /// Mounts and unmounts all children needed to be in sync with `children`. - pub fn synchronize_with( - &mut self, - children: &Value, - context: &mut EventContext<'_, '_>, - ) { + pub fn synchronize_with(&mut self, children: &Value, context: &mut EventContext<'_>) { let current_generation = children.generation(); if current_generation.map_or_else( || children.map(Children::len) != self.children.len(), @@ -2284,10 +2285,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 +2293,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 +2315,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 +2346,7 @@ impl PartialEq for WidgetRef { impl ManageWidget for WidgetRef { type Managed = Option; - fn manage(&self, context: &WidgetContext<'_, '_>) -> Self::Managed { + fn manage(&self, context: &WidgetContext<'_>) -> Self::Managed { self.mounted .get(context) .cloned() @@ -2374,7 +2369,7 @@ impl WidgetId { /// Finds this widget mounted in this window, if present. #[must_use] - pub fn find_in(self, context: &WidgetContext<'_, '_>) -> Option { + pub fn find_in(self, context: &WidgetContext<'_>) -> Option { context.tree.widget(self) } } diff --git a/src/widgets/align.rs b/src/widgets/align.rs index 9f2219c..34c51da 100644 --- a/src/widgets/align.rs +++ b/src/widgets/align.rs @@ -87,7 +87,7 @@ impl Align { fn measure( &mut self, available_space: Size, - 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 { + fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option { Some(RootBehavior::Align) } fn layout_child( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout { let layout = self.measure(available_space, context); diff --git a/src/widgets/button.rs b/src/widgets/button.rs index 016d0d4..55d0c82 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -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, _context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, _location: Point, _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, _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, _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>, _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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { 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); } } diff --git a/src/widgets/canvas.rs b/src/widgets/canvas.rs index 0bf58c2..02e0c1d 100644 --- a/src/widgets/canvas.rs +++ b/src/widgets/canvas.rs @@ -20,8 +20,8 @@ impl Canvas { /// Returns a new canvas that draws its contents by invoking `render`. pub fn new(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, - _context: &mut LayoutContext<'_, '_, '_, '_, '_>, + _context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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 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); } } diff --git a/src/widgets/checkbox.rs b/src/widgets/checkbox.rs index 31c09e4..8ed906c 100644 --- a/src/widgets/checkbox.rs +++ b/src/widgets/checkbox.rs @@ -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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { let checkbox_size = context.get(&CheckboxSize).into_upx(context.gfx.scale()); Size::squared(checkbox_size) diff --git a/src/widgets/collapse.rs b/src/widgets/collapse.rs index d89caac..57bd515 100644 --- a/src/widgets/collapse.rs +++ b/src/widgets/collapse.rs @@ -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, _available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout { let clip_size = self.size.get_tracking_invalidate(context); if self.vertical { diff --git a/src/widgets/color.rs b/src/widgets/color.rs index 624b5d6..86c59f6 100644 --- a/src/widgets/color.rs +++ b/src/widgets/color.rs @@ -69,7 +69,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 +148,7 @@ impl Widget for ColorSourcePicker { ); } - fn hit_test(&mut self, location: Point, _context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, _context: &mut EventContext<'_>) -> bool { self.visible_rect.contains(location) } @@ -157,7 +157,7 @@ impl Widget for ColorSourcePicker { location: Point, _device_id: DeviceId, _button: MouseButton, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) -> EventHandling { self.update_from_mouse(location); HANDLED @@ -168,7 +168,7 @@ impl Widget for ColorSourcePicker { location: Point, _device_id: DeviceId, _button: MouseButton, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) { self.update_from_mouse(location); } @@ -180,7 +180,7 @@ fn draw_gradient_segment( height: Px, hue: Range, lightness: ZeroToOne, - context: &mut GraphicsContext<'_, '_, '_, '_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_>, ) { let mid_left = ( Point::new(start.x, start.y + height / 2), diff --git a/src/widgets/container.rs b/src/widgets/container.rs index 254503e..00c0e40 100644 --- a/src/widgets/container.rs +++ b/src/widgets/container.rs @@ -158,7 +158,7 @@ impl Container { self } - fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_, '_>) -> Edges { + fn padding(&self, context: &GraphicsContext<'_, '_, '_, '_>) -> Edges { 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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, shadow: &ContainerShadow, 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); diff --git a/src/widgets/custom.rs b/src/widgets/custom.rs index 2b16eba..1553c93 100644 --- a/src/widgets/custom.rs +++ b/src/widgets/custom.rs @@ -121,8 +121,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 +143,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 +156,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::mounted`]. pub fn on_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 +168,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::unmounted`]. pub fn on_unmounted(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 +179,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::unhover`]. pub fn on_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 +190,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::focus`]. pub fn on_focus(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 +201,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::blur`]. pub fn on_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 +212,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::activate`]. pub fn on_activate(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 +223,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::deactivate`]. pub fn on_deactivate(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 +235,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::accept_focus`]. pub fn on_accept_focus(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 +248,7 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::allow_blur`]. pub fn on_allow_blur(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 +264,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 +282,9 @@ impl Custom { where AdjustChildConstraints: Send + 'static - + for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + + for<'context, 'clip, 'gfx, 'pass> FnMut( Size, - &mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, + &mut LayoutContext<'context, 'clip, 'gfx, 'pass>, ) -> Size, { self.adjust_child = Some(Box::new(adjust_child_constraints)); @@ -311,10 +298,10 @@ impl Custom { where PositionChild: Send + 'static - + for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + + for<'context, 'clip, 'gfx, 'pass> FnMut( Size, Size, - &mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, + &mut LayoutContext<'context, 'clip, 'gfx, 'pass>, ) -> WrappedLayout, { self.position_child = Some(Box::new(position_child)); @@ -327,9 +314,8 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::hit_test`]. pub fn on_hit_test(mut self, hit_test: HitTest) -> Self where - HitTest: Send - + 'static - + for<'context, 'window> FnMut(Point, &mut EventContext<'context, 'window>) -> bool, + HitTest: + Send + 'static + for<'context> FnMut(Point, &mut EventContext<'context>) -> bool, { self.hit_test = Some(Box::new(hit_test)); self @@ -342,10 +328,7 @@ impl Custom { where Hover: Send + 'static - + for<'context, 'window> FnMut( - Point, - &mut EventContext<'context, 'window>, - ) -> Option, + + for<'context> FnMut(Point, &mut EventContext<'context>) -> Option, { self.hover = Some(Box::new(hover)); self @@ -364,11 +347,11 @@ impl Custom { where MouseDown: Send + 'static - + for<'context, 'window> FnMut( + + for<'context> FnMut( Point, DeviceId, MouseButton, - &mut EventContext<'context, 'window>, + &mut EventContext<'context>, ) -> EventHandling, { self.mouse_down = Some(Box::new(mouse_down)); @@ -383,12 +366,7 @@ impl Custom { where MouseDrag: Send + 'static - + for<'context, 'window> FnMut( - Point, - DeviceId, - MouseButton, - &mut EventContext<'context, 'window>, - ), + + for<'context> FnMut(Point, DeviceId, MouseButton, &mut EventContext<'context>), { self.mouse_drag = Some(Box::new(mouse_drag)); self @@ -401,11 +379,11 @@ impl Custom { where MouseUp: Send + 'static - + for<'context, 'window> FnMut( + + for<'context> FnMut( Option>, DeviceId, MouseButton, - &mut EventContext<'context, 'window>, + &mut EventContext<'context>, ), { self.mouse_up = Some(Box::new(mouse_up)); @@ -417,9 +395,8 @@ impl Custom { /// This callback corresponds to [`WrapperWidget::ime`]. pub fn on_ime(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 +409,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 +427,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 +444,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 +459,7 @@ impl WrapperWidget for Custom { fn adjust_child_constraints( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { if let Some(adjust_child) = &mut self.adjust_child { adjust_child.invoke(available_space, context) @@ -495,7 +472,7 @@ impl WrapperWidget for Custom { &mut self, size: Size, available_space: Size, - 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 +489,19 @@ impl WrapperWidget for Custom { } } - fn background_color(&mut self, context: &WidgetContext<'_, '_>) -> Option { + fn background_color(&mut self, context: &WidgetContext<'_>) -> Option { 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 +509,7 @@ impl WrapperWidget for Custom { } } - fn hit_test(&mut self, location: Point, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, context: &mut EventContext<'_>) -> bool { if let Some(hit_test) = &mut self.hit_test { hit_test.invoke(location, context) } else { @@ -540,22 +517,18 @@ impl WrapperWidget for Custom { } } - fn hover( - &mut self, - location: Point, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, location: Point, context: &mut EventContext<'_>) -> Option { 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 +536,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 +565,7 @@ impl WrapperWidget for Custom { location: Point, 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 +579,7 @@ impl WrapperWidget for Custom { location: Point, 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 +591,7 @@ impl WrapperWidget for Custom { location: Option>, 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 +603,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 +612,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 +625,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 +637,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 +646,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 +656,16 @@ impl WrapperWidget for Custom { } trait RedrawFunc: Send { - fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>); + fn invoke(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>); } impl 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 +674,7 @@ trait AdjustChildConstraintsFunc: Send { fn invoke( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size; } @@ -711,15 +682,15 @@ impl AdjustChildConstraintsFunc for Func where Func: Send + 'static - + for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + + for<'context, 'clip, 'gfx, 'pass> FnMut( Size, - &mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, + &mut LayoutContext<'context, 'clip, 'gfx, 'pass>, ) -> Size, { fn invoke( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { self(available_space, context) } @@ -730,7 +701,7 @@ trait PositionChildFunc: Send { &mut self, size: Size, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout; } @@ -738,45 +709,44 @@ impl PositionChildFunc for Func where Func: Send + 'static - + for<'context, 'window, 'clip, 'gfx, 'pass> FnMut( + + for<'context, 'clip, 'gfx, 'pass> FnMut( Size, Size, - &mut LayoutContext<'context, 'window, 'clip, 'gfx, 'pass>, + &mut LayoutContext<'context, 'clip, 'gfx, 'pass>, ) -> WrappedLayout, { fn invoke( &mut self, size: Size, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> WrappedLayout { self(size, available_space, context) } } trait EventFunc: Send { - fn invoke(&mut self, context: &mut EventContext<'_, '_>) -> R; + fn invoke(&mut self, context: &mut EventContext<'_>) -> R; } impl EventFunc 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: Send { - fn invoke(&mut self, param: P, context: &mut EventContext<'_, '_>) -> R; + fn invoke(&mut self, param: P, context: &mut EventContext<'_>) -> R; } impl OneParamEventFunc 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 +757,7 @@ trait ThreeParamEventFunc: Send { location: P1, device_id: P2, button: P3, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> R; } @@ -795,16 +765,14 @@ type MouseUpFunc = dyn ThreeParamEventFunc>, DeviceId, MouseBut impl ThreeParamEventFunc 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) } diff --git a/src/widgets/disclose.rs b/src/widgets/disclose.rs index 321ca26..3c01365 100644 --- a/src/widgets/disclose.rs +++ b/src/widgets/disclose.rs @@ -122,7 +122,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 +161,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 +217,7 @@ impl Widget for DiscloseIndicator { fn layout( &mut self, mut available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { let indicator_size = context .get(&IndicatorSize) @@ -270,19 +270,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, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, context: &mut EventContext<'_>) -> bool { let size = context .get(&IndicatorSize) .into_px(context.kludgine.scale()) @@ -296,11 +296,7 @@ impl Widget for DiscloseIndicator { } } - fn hover( - &mut self, - location: Point, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, location: Point, context: &mut EventContext<'_>) -> Option { let hovering = self.hit_test(location, context); if self.hovering_indicator != hovering { context.set_needs_redraw(); @@ -310,7 +306,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(); @@ -322,7 +318,7 @@ impl Widget for DiscloseIndicator { location: Point, _device_id: kludgine::app::winit::event::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; @@ -339,7 +335,7 @@ impl Widget for DiscloseIndicator { _location: Option>, _device_id: kludgine::app::winit::event::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 +344,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(); } diff --git a/src/widgets/expand.rs b/src/widgets/expand.rs index 60a7745..819235d 100644 --- a/src/widgets/expand.rs +++ b/src/widgets/expand.rs @@ -98,14 +98,14 @@ impl WrapperWidget for Expand { &mut self.child } - fn root_behavior(&mut self, _context: &mut EventContext<'_, '_>) -> Option { + fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option { Some(RootBehavior::Expand) } fn layout_child( &mut self, available_space: Size, - 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()); diff --git a/src/widgets/grid.rs b/src/widgets/grid.rs index cc6bcce..d5f94be 100644 --- a/src/widgets/grid.rs +++ b/src/widgets/grid.rs @@ -67,7 +67,7 @@ impl Grid { 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 Grid { } } - 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 Grid { } impl Widget for Grid { - 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 Widget for Grid { fn layout( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { self.synchronize_children(&mut context.as_event_context()); diff --git a/src/widgets/image.rs b/src/widgets/image.rs index eeaaa9d..5353769 100644 --- a/src/widgets/image.rs +++ b/src/widgets/image.rs @@ -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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { match self.scaling.get_tracking_invalidate(context) { ImageScaling::Aspect { .. } | ImageScaling::Stretch => { diff --git a/src/widgets/input.rs b/src/widgets/input.rs index 7dbca9b..1dddd11 100644 --- a/src/widgets/input.rs +++ b/src/widgets/input.rs @@ -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, - context: &mut GraphicsContext<'_, '_, '_, '_, '_>, - ) { + fn layout_text(&mut self, width: Option, 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, - context: &mut EventContext<'_, '_>, - ) -> Cursor { + fn cursor_from_point(&mut self, location: Point, 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, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Cursor { let Some(cache) = &self.cache else { return Cursor::default(); @@ -973,11 +962,11 @@ impl Widget for Input where Storage: InputStorage + Debug, { - fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_>) -> bool { true } - fn accept_focus(&mut self, _context: &mut EventContext<'_, '_>) -> bool { + fn accept_focus(&mut self, _context: &mut EventContext<'_>) -> bool { true } @@ -986,7 +975,7 @@ where location: Point, _device_id: kludgine::app::winit::event::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, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) -> Option { Some(CursorIcon::Text) } @@ -1010,7 +999,7 @@ where location: Point, _device_id: kludgine::app::winit::event::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 { @@ -1025,13 +1014,13 @@ where _location: Option>, _device_id: kludgine::app::winit::event::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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { let padding = context .get(&IntrinsicPadding) @@ -1202,7 +1191,7 @@ where _device_id: kludgine::app::winit::event::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(); } diff --git a/src/widgets/label.rs b/src/widgets/label.rs index d9bc7c8..c6b1534 100644 --- a/src/widgets/label.rs +++ b/src/widgets/label.rs @@ -38,7 +38,7 @@ where fn prepared_text( &mut self, - context: &mut GraphicsContext<'_, '_, '_, '_, '_>, + context: &mut GraphicsContext<'_, '_, '_, '_>, color: Color, width: Px, ) -> &MeasuredText { @@ -76,7 +76,7 @@ impl Widget for Label 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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); } } diff --git a/src/widgets/layers.rs b/src/widgets/layers.rs index 689c8b2..800b772 100644 --- a/src/widgets/layers.rs +++ b/src/widgets/layers.rs @@ -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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { 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, context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, location: Point, 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, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { 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, check_original_relative: bool, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { 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, - 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, relative_to: WidgetId, ) -> Option> { let direction = self.overlays[index].direction; @@ -465,7 +465,7 @@ impl OverlayState { &self, checking_index: usize, layout: &Rect, - 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Option> { 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, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { 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); } diff --git a/src/widgets/mode_switch.rs b/src/widgets/mode_switch.rs index 2f2f61e..1cd344d 100644 --- a/src/widgets/mode_switch.rs +++ b/src/widgets/mode_switch.rs @@ -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()); } } diff --git a/src/widgets/progress.rs b/src/widgets/progress.rs index 62cca88..a857d72 100644 --- a/src/widgets/progress.rs +++ b/src/widgets/progress.rs @@ -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, - context: &mut crate::context::LayoutContext<'_, '_, '_, '_, '_>, + context: &mut crate::context::LayoutContext<'_, '_, '_, '_>, ) -> figures::Size { let track_size = context.get(&TrackSize).into_px(context.gfx.scale()); let minimum_size = track_size * 4; diff --git a/src/widgets/radio.rs b/src/widgets/radio.rs index 48b64b5..b53e27f 100644 --- a/src/widgets/radio.rs +++ b/src/widgets/radio.rs @@ -80,7 +80,7 @@ impl Widget for RadioOrnament 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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { let radio_size = context.get(&RadioSize).into_upx(context.gfx.scale()); Size::squared(radio_size) diff --git a/src/widgets/resize.rs b/src/widgets/resize.rs index bb9a271..ad0c96f 100644 --- a/src/widgets/resize.rs +++ b/src/widgets/resize.rs @@ -89,14 +89,14 @@ impl WrapperWidget for Resize { &mut self.child } - fn root_behavior(&mut self, _context: &mut EventContext<'_, '_>) -> Option { + fn root_behavior(&mut self, _context: &mut EventContext<'_>) -> Option { Some(RootBehavior::Resize(Size::new(self.width, self.height))) } fn layout_child( &mut self, available_space: Size, - 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)) = diff --git a/src/widgets/scroll.rs b/src/widgets/scroll.rs index 153cad8..208e0bc 100644 --- a/src/widgets/scroll.rs +++ b/src/widgets/scroll.rs @@ -97,7 +97,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 +130,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 +144,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, _context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_>) -> bool { true } fn hover( &mut self, _location: Point, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { 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 +198,7 @@ impl Widget for Scroll { fn layout( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { self.bar_width = context .get(&ScrollBarThickness) @@ -302,7 +302,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 +330,7 @@ impl Widget for Scroll { location: Point, _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 +380,7 @@ impl Widget for Scroll { location: Point, _device_id: DeviceId, _button: kludgine::app::winit::event::MouseButton, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) { self.drag.update( location, @@ -397,7 +397,7 @@ impl Widget for Scroll { location: Option>, _device_id: DeviceId, _button: kludgine::app::winit::event::MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { self.drag.mouse_buttons_down -= 1; diff --git a/src/widgets/slider.rs b/src/widgets/slider.rs index b720620..390400a 100644 --- a/src/widgets/slider.rs +++ b/src/widgets/slider.rs @@ -144,7 +144,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 +238,7 @@ where focus: Option, 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 +257,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 +417,7 @@ impl Widget for Slider 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 +500,7 @@ where fn layout( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { self.knob_size = if self.knob_visible { context.get(&KnobSize).into_upx(context.gfx.scale()) @@ -552,14 +552,14 @@ where } } - fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_, '_>) -> bool { + fn hit_test(&mut self, _location: Point, _context: &mut EventContext<'_>) -> bool { self.interactive } fn hover( &mut self, _location: Point, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> Option { (self.interactive && self.knob_visible).then_some({ if context.enabled() { @@ -574,14 +574,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 +595,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 +619,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 +629,7 @@ where location: Point, _device_id: DeviceId, _button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { let true = self.interactive else { return IGNORED; @@ -652,7 +652,7 @@ where location: Point, _device_id: DeviceId, _button: MouseButton, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) { if context.enabled() { self.update_from_click(location, None); @@ -664,7 +664,7 @@ where _location: Option>, _device_id: DeviceId, _button: MouseButton, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) { self.mouse_buttons_down -= 1; } @@ -674,7 +674,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 +699,7 @@ where _device_id: DeviceId, delta: MouseScrollDelta, _phase: TouchPhase, - context: &mut EventContext<'_, '_>, + context: &mut EventContext<'_>, ) -> EventHandling { let true = self.interactive else { return IGNORED; diff --git a/src/widgets/space.rs b/src/widgets/space.rs index 93dd8aa..9bb7283 100644 --- a/src/widgets/space.rs +++ b/src/widgets/space.rs @@ -38,7 +38,7 @@ impl Space { } impl Widget for Space { - fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_, '_>) { + fn redraw(&mut self, context: &mut GraphicsContext<'_, '_, '_, '_>) { let color = self.color.get_tracking_redraw(context); context.fill(color); } @@ -46,7 +46,7 @@ impl Widget for Space { fn layout( &mut self, _available_space: Size, - _context: &mut LayoutContext<'_, '_, '_, '_, '_>, + _context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { Size::default() } diff --git a/src/widgets/stack.rs b/src/widgets/stack.rs index d99fd3e..3da1907 100644 --- a/src/widgets/stack.rs +++ b/src/widgets/stack.rs @@ -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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { self.synchronize_children(&mut context.as_event_context()); diff --git a/src/widgets/style.rs b/src/widgets/style.rs index e423d0a..e6e3487 100644 --- a/src/widgets/style.rs +++ b/src/widgets/style.rs @@ -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()); } } diff --git a/src/widgets/switcher.rs b/src/widgets/switcher.rs index 0d9bc31..daaaf06 100644 --- a/src/widgets/switcher.rs +++ b/src/widgets/switcher.rs @@ -52,7 +52,7 @@ impl WrapperWidget for Switcher { fn adjust_child_constraints( &mut self, available_space: Size, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { if self.source.has_updated() { self.child = WidgetRef::new(self.source.get()); diff --git a/src/widgets/themed.rs b/src/widgets/themed.rs index 5a23fa8..1a38ec4 100644 --- a/src/widgets/themed.rs +++ b/src/widgets/themed.rs @@ -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()); } } diff --git a/src/widgets/tilemap.rs b/src/widgets/tilemap.rs index 9a3b3c4..521470b 100644 --- a/src/widgets/tilemap.rs +++ b/src/widgets/tilemap.rs @@ -64,7 +64,7 @@ impl Widget for TileMap 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 +103,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, - _context: &mut EventContext<'_, '_>, + _context: &mut EventContext<'_>, ) -> bool { true } @@ -118,7 +118,7 @@ where fn layout( &mut self, available_space: Size, - _context: &mut LayoutContext<'_, '_, '_, '_, '_>, + _context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { Size::new(available_space.width.max(), available_space.height.max()) } @@ -128,7 +128,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 +141,7 @@ where HANDLED } - fn hover( - &mut self, - local: Point, - context: &mut EventContext<'_, '_>, - ) -> Option { + fn hover(&mut self, local: Point, context: &mut EventContext<'_>) -> Option { if let Some(tick) = &self.tick { let Some(size) = context.last_layout().map(|rect| rect.size) else { return None; @@ -163,7 +159,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 +170,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 +184,7 @@ where _location: Point, _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 +200,7 @@ where _location: Option>, _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); diff --git a/src/widgets/validated.rs b/src/widgets/validated.rs index e589a74..a04eee7 100644 --- a/src/widgets/validated.rs +++ b/src/widgets/validated.rs @@ -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)); } diff --git a/src/widgets/wrap.rs b/src/widgets/wrap.rs index 111067a..dce6103 100644 --- a/src/widgets/wrap.rs +++ b/src/widgets/wrap.rs @@ -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, - context: &mut LayoutContext<'_, '_, '_, '_, '_>, + context: &mut LayoutContext<'_, '_, '_, '_>, ) -> Size { struct RowChild { index: usize, diff --git a/src/window.rs b/src/window.rs index 97626cb..ae29088 100644 --- a/src/window.rs +++ b/src/window.rs @@ -4,27 +4,31 @@ use std::cell::RefCell; use std::collections::hash_map; use std::ffi::OsStr; use std::hash::Hash; +use std::marker::PhantomData; +use std::num::TryFromIntError; use std::ops::{Deref, DerefMut, Not}; use std::path::Path; use std::string::ToString; -use std::sync::{Arc, Mutex, MutexGuard, OnceLock}; +use std::sync::{Arc, Condvar, Mutex, MutexGuard, OnceLock}; +use std::time::{Duration, Instant}; use ahash::AHashMap; use alot::LotId; use arboard::Clipboard; use figures::units::{Px, UPx}; -use figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, ScreenScale, Size, Zero}; +use figures::{IntoSigned, IntoUnsigned, Point, Ranged, Rect, Round, ScreenScale, Size, Zero}; +use intentional::{Assert, Cast}; use kludgine::app::winit::dpi::{PhysicalPosition, PhysicalSize}; use kludgine::app::winit::event::{ - DeviceId, ElementState, Ime, KeyEvent, MouseButton, MouseScrollDelta, TouchPhase, + DeviceId, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta, TouchPhase, }; use kludgine::app::winit::keyboard::{Key, NamedKey}; -use kludgine::app::winit::window; -use kludgine::app::WindowBehavior as _; +use kludgine::app::winit::window::{self, CursorIcon}; +use kludgine::app::{winit, WindowBehavior as _}; use kludgine::cosmic_text::{fontdb, Family, FamilyOwned}; use kludgine::render::Drawing; -use kludgine::wgpu::CompositeAlphaMode; -use kludgine::{Kludgine, KludgineId}; +use kludgine::wgpu::{self, CompositeAlphaMode, COPY_BYTES_PER_ROW_ALIGNMENT}; +use kludgine::{Color, Kludgine, KludgineId, Texture}; use tracing::Level; use crate::animation::{LinearInterpolate, PercentBetween, ZeroToOne}; @@ -48,9 +52,183 @@ use crate::widget::{ use crate::window::sealed::WindowCommand; use crate::{initialize_tracing, ConstraintLimit}; +/// A platform-dependent window implementation. +pub trait PlatformWindowImplementation { + /// Marks the window to close as soon as possible. + fn close(&mut self); + /// Returns the underlying `winit` window, if one exists. + fn winit(&self) -> Option<&winit::window::Window>; + /// Sets the window to redraw as soon as possible. + fn set_needs_redraw(&mut self); + /// Sets the window to redraw after a `duration`. + fn redraw_in(&mut self, duration: Duration); + /// Sets the window to redraw at a specified instant. + fn redraw_at(&mut self, moment: Instant); + /// Returns the current keyboard modifiers. + fn modifiers(&self) -> Modifiers; + /// Returns the amount of time that has elapsed since the last redraw. + fn elapsed(&self) -> Duration; + /// Sets the current cursor icon to `cursor`. + fn set_cursor_icon(&mut self, cursor: CursorIcon); + /// Returns a handle for the window. + fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle; + /// Returns the current inner size of the window. + fn inner_size(&self) -> Size; + + /// Returns true if the window can have its size changed. + /// + /// The provided implementation returns + /// [`winit::window::Window::is_resizable`], or true if this window has no + /// winit window. + fn is_resizable(&self) -> bool { + self.winit() + .map_or(true, winit::window::Window::is_resizable) + } + + /// Returns true if the window can have its size changed. + /// + /// The provided implementation returns [`winit::window::Window::theme`], or + /// dark if this window has no winit window. + fn theme(&self) -> winit::window::Theme { + self.winit() + .and_then(winit::window::Window::theme) + .unwrap_or(winit::window::Theme::Dark) + } + + /// Requests that the window change its inner size. + /// + /// The provided implementation forwards the request onto the winit window, + /// if present. + fn request_inner_size(&mut self, inner_size: Size) { + self.winit() + .map(|winit| winit.request_inner_size(PhysicalSize::from(inner_size))); + } + + /// Sets whether [`Ime`] events should be enabled. + /// + /// The provided implementation forwards the request onto the winit window, + /// if present. + fn set_ime_allowed(&self, allowed: bool) { + if let Some(winit) = self.winit() { + winit.set_ime_allowed(allowed); + } + } + + /// Sets the current [`Ime`] purpose. + /// + /// The provided implementation forwards the request onto the winit window, + /// if present. + fn set_ime_purpose(&self, purpose: winit::window::ImePurpose) { + if let Some(winit) = self.winit() { + winit.set_ime_purpose(purpose); + } + } + + /// Sets the window's minimum inner size. + fn set_min_inner_size(&self, min_size: Option>) { + if let Some(winit) = self.winit() { + winit.set_min_inner_size::>(min_size.map(Into::into)); + } + } + + /// Sets the window's maximum inner size. + fn set_max_inner_size(&self, max_size: Option>) { + if let Some(winit) = self.winit() { + winit.set_max_inner_size::>(max_size.map(Into::into)); + } + } +} + +impl PlatformWindowImplementation for kludgine::app::Window<'_, WindowCommand> { + fn set_cursor_icon(&mut self, cursor: CursorIcon) { + self.winit().set_cursor_icon(cursor); + } + + fn inner_size(&self) -> Size { + self.winit().inner_size().into() + } + + fn close(&mut self) { + self.close(); + } + + fn winit(&self) -> Option<&winit::window::Window> { + Some(self.winit()) + } + + fn set_needs_redraw(&mut self) { + self.set_needs_redraw(); + } + + fn redraw_in(&mut self, duration: Duration) { + self.redraw_in(duration); + } + + fn redraw_at(&mut self, moment: Instant) { + self.redraw_at(moment); + } + + fn modifiers(&self) -> Modifiers { + self.modifiers() + } + + fn elapsed(&self) -> Duration { + self.elapsed() + } + + fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle { + WindowHandle::new(self.handle(), redraw_status) + } +} + +/// A platform-dependent window. +pub trait PlatformWindow { + /// Marks the window to close as soon as possible. + fn close(&mut self); + /// Returns the underlying `winit` window, if one exists. + fn winit(&self) -> Option<&winit::window::Window>; + /// Returns a handle for the window. + fn handle(&self) -> WindowHandle; + /// Returns the unique id of the [`Kludgine`] instance used by this window. + fn kludgine_id(&self) -> KludgineId; + /// Returns the dynamic that is synchrnoized with the window's focus. + fn focused(&self) -> &Dynamic; + /// Returns the dynamic that is synchronized with the window's occlusion + /// status. + fn occluded(&self) -> &Dynamic; + /// Returns the current inner size of the window. + fn inner_size(&self) -> &Dynamic>; + /// Returns the shared application resources. + fn cushy(&self) -> &Cushy; + /// Sets the window to redraw as soon as possible. + fn set_needs_redraw(&mut self); + /// Sets the window to redraw after a `duration`. + fn redraw_in(&mut self, duration: Duration); + /// Sets the window to redraw at a specified instant. + fn redraw_at(&mut self, moment: Instant); + /// Returns the current keyboard modifiers. + fn modifiers(&self) -> Modifiers; + /// Returns the amount of time that has elapsed since the last redraw. + fn elapsed(&self) -> Duration; + /// Sets the current cursor icon to `cursor`. + fn set_cursor_icon(&mut self, cursor: CursorIcon); + + /// Sets whether [`Ime`] events should be enabled. + fn set_ime_allowed(&self, allowed: bool); + /// Sets the current [`Ime`] purpose. + fn set_ime_purpose(&self, purpose: winit::window::ImePurpose); + + /// Requests that the window change its inner size. + fn request_inner_size(&mut self, inner_size: Size); + /// Sets the window's minimum inner size. + fn set_min_inner_size(&self, min_size: Option>); + /// Sets the window's maximum inner size. + fn set_max_inner_size(&self, max_size: Option>); +} + /// A currently running Cushy window. -pub struct RunningWindow<'window> { - window: kludgine::app::Window<'window, WindowCommand>, +pub struct RunningWindow { + window: W, kludgine_id: KludgineId, invalidation_status: InvalidationStatus, cushy: Cushy, @@ -59,9 +237,12 @@ pub struct RunningWindow<'window> { inner_size: Dynamic>, } -impl<'window> RunningWindow<'window> { +impl RunningWindow +where + W: PlatformWindowImplementation, +{ pub(crate) fn new( - window: kludgine::app::Window<'window, WindowCommand>, + window: W, kludgine_id: KludgineId, invalidation_status: &InvalidationStatus, cushy: &Cushy, @@ -113,7 +294,7 @@ impl<'window> RunningWindow<'window> { /// Returns a handle to this window. #[must_use] pub fn handle(&self) -> WindowHandle { - WindowHandle::new(self.window.handle(), self.invalidation_status.clone()) + self.window.handle(self.invalidation_status.clone()) } /// Returns a dynamic that is synchronized with this window's inner size. @@ -135,20 +316,107 @@ impl<'window> RunningWindow<'window> { } } -impl<'window> Deref for RunningWindow<'window> { - type Target = kludgine::app::Window<'window, WindowCommand>; +impl Deref for RunningWindow +where + W: PlatformWindowImplementation + 'static, +{ + type Target = dyn PlatformWindowImplementation; fn deref(&self) -> &Self::Target { &self.window } } -impl<'window> DerefMut for RunningWindow<'window> { +impl DerefMut for RunningWindow +where + W: PlatformWindowImplementation + 'static, +{ fn deref_mut(&mut self) -> &mut Self::Target { &mut self.window } } +impl PlatformWindow for RunningWindow +where + W: PlatformWindowImplementation, +{ + fn close(&mut self) { + self.window.close(); + } + + fn winit(&self) -> Option<&winit::window::Window> { + self.window.winit() + } + + fn handle(&self) -> WindowHandle { + self.window.handle(self.invalidation_status.clone()) + } + + fn kludgine_id(&self) -> KludgineId { + self.kludgine_id + } + + fn focused(&self) -> &Dynamic { + &self.focused + } + + fn occluded(&self) -> &Dynamic { + &self.occluded + } + + fn inner_size(&self) -> &Dynamic> { + &self.inner_size + } + + fn cushy(&self) -> &Cushy { + &self.cushy + } + + fn set_needs_redraw(&mut self) { + self.window.set_needs_redraw(); + } + + fn redraw_in(&mut self, duration: Duration) { + self.window.redraw_in(duration); + } + + fn redraw_at(&mut self, moment: Instant) { + self.window.redraw_at(moment); + } + + fn modifiers(&self) -> Modifiers { + self.window.modifiers() + } + + fn elapsed(&self) -> Duration { + self.window.elapsed() + } + + fn set_ime_allowed(&self, allowed: bool) { + self.window.set_ime_allowed(allowed); + } + + fn set_ime_purpose(&self, purpose: winit::window::ImePurpose) { + self.window.set_ime_purpose(purpose); + } + + fn set_cursor_icon(&mut self, cursor: CursorIcon) { + self.window.set_cursor_icon(cursor); + } + + fn set_min_inner_size(&self, min_size: Option>) { + self.window.set_min_inner_size(min_size); + } + + fn set_max_inner_size(&self, max_size: Option>) { + self.window.set_max_inner_size(max_size); + } + + fn request_inner_size(&mut self, inner_size: Size) { + self.window.request_inner_size(inner_size); + } +} + /// The attributes of a Cushy window. pub type WindowAttributes = kludgine::app::WindowAttributes; @@ -377,7 +645,9 @@ where App: Application + ?Sized, { let cushy = app.cushy().clone(); - + // let Some(app) = app.as_app().as_kludgine() else { + // return Ok(None); + // }; let handle = CushyWindow::::open_with( app, sealed::Context { @@ -389,9 +659,9 @@ where on_closed: self.on_closed, transparent: self.attributes.transparent, attributes: Some(self.attributes), - occluded: self.occluded, - focused: self.focused, - inner_size: self.inner_size, + occluded: self.occluded.unwrap_or_default(), + focused: self.focused.unwrap_or_default(), + inner_size: self.inner_size.unwrap_or_default(), theme: Some(self.theme), theme_mode: self.theme_mode, font_data_to_load: self.font_data_to_load, @@ -419,7 +689,10 @@ pub trait WindowBehavior: Sized + 'static { type Context: Send + 'static; /// Return a new instance of this behavior using `context`. - fn initialize(window: &mut RunningWindow<'_>, context: Self::Context) -> Self; + fn initialize( + window: &mut RunningWindow>, + context: Self::Context, + ) -> Self; /// Create the window's root widget. This function is only invoked once. fn make_root(&mut self) -> WidgetInstance; @@ -428,7 +701,10 @@ pub trait WindowBehavior: Sized + 'static { /// the window will be closed. Returning false prevents the window from /// closing. #[allow(unused_variables)] - fn close_requested(&self, window: &mut RunningWindow<'_>) -> bool { + fn close_requested(&self, window: &mut RunningWindow) -> bool + where + W: PlatformWindowImplementation, + { true } @@ -479,7 +755,7 @@ where fn request_close( should_close: &mut bool, behavior: &mut T, - window: &mut RunningWindow<'_>, + window: &mut RunningWindow>, ) -> bool { *should_close |= behavior.close_requested(window); @@ -490,7 +766,7 @@ where &mut self, is_pressed: bool, widget: Option, - window: &mut RunningWindow<'_>, + window: &mut RunningWindow>, kludgine: &mut Kludgine, ) { if is_pressed { @@ -544,12 +820,15 @@ where } } - fn constrain_window_resizing( + fn constrain_window_resizing( &mut self, resizable: bool, - window: &mut RunningWindow<'_>, + window: &mut RunningWindow, graphics: &mut kludgine::Graphics<'_>, - ) -> RootMode { + ) -> RootMode + where + W: PlatformWindowImplementation, + { let mut root_or_child = self.root.widget.clone(); let mut root_mode = None; let mut padding = Edges::::default(); @@ -691,7 +970,7 @@ where fn handle_window_keyboard_input( &mut self, - window: &mut RunningWindow<'_>, + window: &mut RunningWindow>, kludgine: &mut Kludgine, input: KeyEvent, ) { @@ -782,40 +1061,31 @@ where } } } -} -#[derive(Clone, Copy, Eq, PartialEq, Debug)] -enum RootMode { - Fit, - Expand, - Align, -} - -impl kludgine::app::WindowBehavior for CushyWindow -where - T: WindowBehavior, -{ - type Context = sealed::Context; - - fn initialize( - window: kludgine::app::Window<'_, WindowCommand>, + #[allow(clippy::needless_pass_by_value)] + fn new( + mut behavior: T, + window: W, graphics: &mut kludgine::Graphics<'_>, - context: Self::Context, - ) -> Self { - let mut settings = context.settings.borrow_mut(); + mut settings: sealed::WindowSettings, + ) -> Self + where + W: PlatformWindowImplementation, + { + let redraw_status = settings.redraw_status.clone(); if let Value::Dynamic(title) = &settings.title { - let handle = window.handle(); + let handle = window.handle(redraw_status.clone()); title .for_each_cloned(move |title| { - let _result = handle.send(WindowCommand::SetTitle(title)); + handle.inner.send(WindowCommand::SetTitle(title)); }) .persist(); } let cushy = settings.cushy.clone(); - let occluded = settings.occluded.take().unwrap_or_default(); - let focused = settings.focused.take().unwrap_or_default(); - let theme = settings.theme.take().expect("theme always present"); - let inner_size = settings.inner_size.take().unwrap_or_default(); + let occluded = settings.occluded.clone(); + let focused = settings.focused.clone(); + let theme = settings.theme.take().unwrap_or_default(); + let inner_size = settings.inner_size.clone(); let on_closed = settings.on_closed.take(); inner_size.set(window.inner_size()); @@ -831,19 +1101,7 @@ where None => Value::dynamic(window.theme().into()), }; let transparent = settings.transparent; - let redraw_status = settings.redraw_status.clone(); - let mut behavior = T::initialize( - &mut RunningWindow::new( - window, - graphics.id(), - &redraw_status, - &cushy, - &focused, - &occluded, - &inner_size, - ), - context.user, - ); + let tree = Tree::default(); let root = tree.push_boxed(behavior.make_root(), None); @@ -882,11 +1140,10 @@ where } } - fn prepare( - &mut self, - window: kludgine::app::Window<'_, WindowCommand>, - graphics: &mut kludgine::Graphics<'_>, - ) { + fn prepare(&mut self, window: W, graphics: &mut kludgine::Graphics<'_>) + where + W: PlatformWindowImplementation, + { if let Some(theme) = &mut self.theme { if theme.has_updated() { self.current_theme = theme.get(); @@ -899,7 +1156,7 @@ where self.tree .new_frame(self.redraw_status.invalidations().drain()); - let resizable = window.winit().is_resizable(); + let resizable = window.is_resizable(); let mut window = RunningWindow::new( window, graphics.id(), @@ -953,9 +1210,7 @@ where layout_context.redraw_when_changed(&self.inner_size); let inner_size_generation = self.inner_size.generation(); if self.inner_size_generation != inner_size_generation { - let _ = layout_context - .winit() - .request_inner_size(PhysicalSize::from(self.inner_size.get())); + layout_context.request_inner_size(self.inner_size.get()); self.inner_size_generation = inner_size_generation; } else if actual_size != window_size && !resizable { let mut new_size = actual_size; @@ -965,9 +1220,7 @@ where if let Some(max_size) = self.max_inner_size { new_size = new_size.min(max_size); } - let _ = layout_context - .winit() - .request_inner_size(PhysicalSize::from(new_size)); + layout_context.request_inner_size(new_size); } self.root.set_layout(Rect::from(render_size.into_signed())); @@ -990,6 +1243,74 @@ where } } + fn close_requested(&mut self, window: W, kludgine: &mut Kludgine) -> bool + where + W: PlatformWindowImplementation, + { + if self.behavior.close_requested(&mut RunningWindow::new( + window, + kludgine.id(), + &self.redraw_status, + &self.cushy, + &self.focused, + &self.occluded, + &self.inner_size, + )) { + self.should_close = true; + true + } else { + false + } + } +} + +#[derive(Clone, Copy, Eq, PartialEq, Debug)] +enum RootMode { + Fit, + Expand, + Align, +} + +impl kludgine::app::WindowBehavior for CushyWindow +where + T: WindowBehavior, +{ + type Context = sealed::Context; + + fn initialize( + window: kludgine::app::Window<'_, WindowCommand>, + graphics: &mut kludgine::Graphics<'_>, + context: Self::Context, + ) -> Self { + let settings = context.settings.borrow_mut(); + let mut window = RunningWindow::new( + window, + graphics.id(), + &settings.redraw_status, + &settings.cushy, + &settings.focused, + &settings.occluded, + &settings.inner_size, + ); + drop(settings); + + let behavior = T::initialize(&mut window, context.user); + Self::new( + behavior, + window.window, + graphics, + context.settings.into_inner(), + ) + } + + fn prepare( + &mut self, + window: kludgine::app::Window<'_, WindowCommand>, + graphics: &mut kludgine::Graphics<'_>, + ) { + self.prepare(window, graphics); + } + fn focus_changed( &mut self, window: kludgine::app::Window<'_, WindowCommand>, @@ -1451,8 +1772,8 @@ impl Drop for CushyWindow { } fn recursively_handle_event( - context: &mut EventContext<'_, '_>, - mut each_widget: impl FnMut(&mut EventContext<'_, '_>) -> EventHandling, + context: &mut EventContext<'_>, + mut each_widget: impl FnMut(&mut EventContext<'_>) -> EventHandling, ) -> Option { match each_widget(context) { HANDLED => Some(context.widget().clone()), @@ -1491,9 +1812,9 @@ pub(crate) mod sealed { pub redraw_status: InvalidationStatus, pub title: Value, pub attributes: Option, - pub occluded: Option>, - pub focused: Option>, - pub inner_size: Option>>, + pub occluded: Dynamic, + pub focused: Dynamic, + pub inner_size: Dynamic>, pub theme: Option>, pub theme_mode: Option>, pub transparent: bool, @@ -1512,6 +1833,12 @@ pub(crate) mod sealed { RequestClose, SetTitle(String), } + + pub trait CaptureFormat { + const HAS_ALPHA: bool; + + fn convert_rgba(data: &mut Vec, width: u32, bytes_per_row: u32); + } } /// Controls whether the light or dark theme is applied. @@ -1678,6 +2005,7 @@ impl Hash for WindowHandle { enum InnerWindowHandle { Pending(Arc), Known(kludgine::app::WindowHandle), + Virtual(WindowDynamicState), } impl InnerWindowHandle { @@ -1697,6 +2025,11 @@ impl InnerWindowHandle { InnerWindowHandle::Known(handle) => { let _result = handle.send(message); } + InnerWindowHandle::Virtual(state) => match message { + WindowCommand::Redraw => state.redraw_target.set(RedrawTarget::Now), + WindowCommand::RequestClose => state.close_requested.set(true), + WindowCommand::SetTitle(title) => state.title.set(title), + }, }; } } @@ -1779,12 +2112,12 @@ impl WindowLocal { /// Looks up the entry for this window. /// /// Internally this API uses [`HashMap::entry`](hash_map::HashMap::entry). - pub fn entry(&mut self, context: &WidgetContext<'_, '_>) -> hash_map::Entry<'_, KludgineId, T> { + pub fn entry(&mut self, context: &WidgetContext<'_>) -> hash_map::Entry<'_, KludgineId, T> { self.by_window.entry(context.kludgine_id()) } /// Sets `value` as the local value for `context`'s window. - pub fn set(&mut self, context: &WidgetContext<'_, '_>, value: T) { + pub fn set(&mut self, context: &WidgetContext<'_>, value: T) { self.by_window.insert(context.kludgine_id(), value); } @@ -1792,12 +2125,12 @@ impl WindowLocal { /// /// Internally this API uses [`HashMap::get`](hash_map::HashMap::get). #[must_use] - pub fn get(&self, context: &WidgetContext<'_, '_>) -> Option<&T> { + pub fn get(&self, context: &WidgetContext<'_>) -> Option<&T> { self.by_window.get(&context.kludgine_id()) } /// Removes any stored value for this window. - pub fn clear_for(&mut self, context: &WidgetContext<'_, '_>) -> Option { + pub fn clear_for(&mut self, context: &WidgetContext<'_>) -> Option { self.by_window.remove(&context.kludgine_id()) } } @@ -1809,3 +2142,680 @@ impl Default for WindowLocal { } } } + +/// The state of a [`VirtualWindow`]. +pub struct VirtualState { + /// State that may be updated outside of the window's event callbacks. + pub dynamic: WindowDynamicState, + /// When true, this window should be closed. + pub closed: bool, + /// The current keyboard modifers. + pub modifiers: Modifiers, + /// The amount of time elapsed since the last redraw call. + pub elapsed: Duration, + /// The currently set cursor icon. + pub cursor: CursorIcon, + /// The inner size of the virtual window. + pub size: Size, +} + +impl VirtualState { + fn new() -> Self { + Self { + dynamic: WindowDynamicState::default(), + closed: false, + modifiers: Modifiers::default(), + elapsed: Duration::ZERO, + cursor: CursorIcon::default(), + size: Size::new(UPx::new(800), UPx::new(600)), + } + } +} + +/// Window state that is able to be updated outside of event handling, +/// potentially via other threads depending on the application. +#[derive(Clone, Debug, Default)] +pub struct WindowDynamicState { + /// The target of the next frame to draw. + pub redraw_target: Dynamic, + /// When true, the window has been asked to close. To ensure full Cushy + /// functionality, upon detecting this, [`VirtualWindow::request_close`] + /// should be invoked. + pub close_requested: Dynamic, + /// The current title of the window. + pub title: Dynamic, +} + +/// A target for the next redraw of a window. +#[derive(Default, Clone, Copy, Debug, PartialEq, Eq)] +pub enum RedrawTarget { + /// The window should not redraw. + #[default] + Never, + /// The window should redraw as soon as possible. + Now, + /// The window should try to redraw at the given instant. + At(Instant), +} + +impl PlatformWindowImplementation for &mut VirtualState { + fn close(&mut self) { + self.closed = true; + } + + fn winit(&self) -> Option<&winit::window::Window> { + None + } + + fn handle(&self, redraw_status: InvalidationStatus) -> WindowHandle { + WindowHandle { + inner: InnerWindowHandle::Virtual(self.dynamic.clone()), + redraw_status, + } + } + + fn set_needs_redraw(&mut self) { + self.dynamic.redraw_target.set(RedrawTarget::Now); + } + + fn redraw_in(&mut self, duration: Duration) { + self.redraw_at(Instant::now() + duration); + } + + fn redraw_at(&mut self, moment: Instant) { + self.dynamic.redraw_target.map_mut(|mut redraw_at| { + if match *redraw_at { + RedrawTarget::At(instant) => moment < instant, + RedrawTarget::Never => true, + RedrawTarget::Now => false, + } { + *redraw_at = RedrawTarget::At(moment); + } + }); + } + + fn modifiers(&self) -> Modifiers { + self.modifiers + } + + fn elapsed(&self) -> Duration { + self.elapsed + } + + fn set_cursor_icon(&mut self, cursor: CursorIcon) { + self.cursor = cursor; + } + + fn inner_size(&self) -> Size { + self.size + } + + fn request_inner_size(&mut self, inner_size: Size) { + self.size = inner_size; + self.set_needs_redraw(); + } +} + +/// A builder for a [`VirtualWindow`]. +pub struct VirtualWindowBuilder { + widget: WidgetInstance, + multisample_count: u32, + initial_size: Size, + scale: f32, + transparent: bool, +} + +impl VirtualWindowBuilder { + /// Returns a new builder for a virtual window that contains `contents`. + #[must_use] + pub fn new(contents: impl MakeWidget) -> Self { + Self { + widget: contents.make_widget(), + multisample_count: 4, + initial_size: Size::new(UPx::new(800), UPx::new(600)), + scale: 1., + transparent: false, + } + } + + /// Sets this virtual window's multi-sample count. + /// + /// By default, 4 samples are taken. When 1 sample is used, multisampling is + /// fully disabled. + #[must_use] + pub fn multisample_count(mut self, count: u32) -> Self { + self.multisample_count = count; + self + } + + /// Sets the size of the virtual window. + #[must_use] + pub fn size(mut self, size: Size) -> Self + where + Unit: Into, + { + self.initial_size = size.map(Into::into); + self + } + + /// Sets the DPI scaling factor of the virtual window. + #[must_use] + pub fn scale(mut self, scale: f32) -> Self { + self.scale = scale; + self + } + + /// Sets the window not fill its background before rendering its contents. + #[must_use] + pub fn transparent(mut self) -> Self { + self.transparent = true; + self + } + + /// Returns the initialized virtual window. + #[must_use] + pub fn finish(self, device: &wgpu::Device, queue: &wgpu::Queue) -> VirtualWindow { + VirtualWindow::new( + self.widget, + self.multisample_count, + self.initial_size, + self.scale, + self.transparent, + device, + queue, + ) + } +} + +/// A virtual Cushy window. +/// +/// This type allows rendering Cushy applications directly into any wgpu +/// application. +pub struct VirtualWindow { + window: CushyWindow, + kludgine: Kludgine, + last_rendered_at: Option, + state: VirtualState, +} + +impl VirtualWindow { + /// Returns a new virtual window with the provided specifications. + fn new( + widget: WidgetInstance, + multisample_count: u32, + initial_size: Size, + scale: f32, + transparent: bool, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Self { + let mut kludgine = Kludgine::new( + device, + queue, + wgpu::TextureFormat::Rgba8UnormSrgb, + wgpu::MultisampleState { + count: multisample_count, + ..Default::default() + }, + initial_size, + scale, + ); + let mut state = VirtualState::new(); + let window = CushyWindow::::new( + widget.make_widget(), + &mut state, + &mut kludgine::Graphics::new(&mut kludgine, device, queue), + sealed::WindowSettings { + cushy: Cushy::new(), + redraw_status: InvalidationStatus::default(), + title: Value::default(), + attributes: None, + occluded: Dynamic::default(), + focused: Dynamic::default(), + inner_size: Dynamic::default(), + theme: None, + theme_mode: None, + transparent, + serif_font_family: FontFamilyList::default(), + sans_serif_font_family: FontFamilyList::default(), + fantasy_font_family: FontFamilyList::default(), + monospace_font_family: FontFamilyList::default(), + cursive_font_family: FontFamilyList::default(), + font_data_to_load: Vec::default(), + on_closed: None, + }, + ); + + Self { + window, + kludgine, + last_rendered_at: None, + state, + } + } + + /// Prepares all necessary resources and operations necessary to render the + /// next frame. + pub fn prepare(&mut self, device: &wgpu::Device, queue: &wgpu::Queue) { + let now = Instant::now(); + self.state.elapsed = self + .last_rendered_at + .map(|i| now.duration_since(i)) + .unwrap_or_default(); + self.last_rendered_at = Some(now); + self.window.prepare( + &mut self.state, + &mut kludgine::Graphics::new(&mut self.kludgine, device, queue), + ); + } + + /// Renders this window in a wgpu render pass created from `pass`. + /// + /// Returns the submission index of the last command submission, if any + /// commands were submitted. + pub fn render( + &mut self, + pass: &wgpu::RenderPassDescriptor<'_, '_>, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Option { + let mut frame = self.kludgine.next_frame(); + let mut gfx = frame.render(pass, device, queue); + self.window.contents.render(1., &mut gfx); + drop(gfx); + frame.submit(queue) + } + + /// Renders this window into `texture` after performing `load_op`. + pub fn render_into( + &mut self, + texture: &kludgine::Texture, + load_op: wgpu::LoadOp, + device: &wgpu::Device, + queue: &wgpu::Queue, + ) -> Option { + let mut frame = self.kludgine.next_frame(); + let mut gfx = frame.render_into(texture, load_op, device, queue); + self.window.contents.render(1., &mut gfx); + drop(gfx); + frame.submit(queue) + } + + /// Returns a new [`kludgine::Graphics`] context for this window. + #[must_use] + pub fn graphics<'gfx>( + &'gfx mut self, + device: &'gfx wgpu::Device, + queue: &'gfx wgpu::Queue, + ) -> kludgine::Graphics<'gfx> { + kludgine::Graphics::new(&mut self.kludgine, device, queue) + } + + /// Requests that the window close. + /// + /// Returns true if the request should be honored. + pub fn request_close(&mut self) -> bool { + if self + .window + .close_requested(&mut self.state, &mut self.kludgine) + { + self.state.closed = true; + true + } else { + self.state.dynamic.close_requested.set(false); + false + } + } + + /// Returns true if this window should no longer be open. + #[must_use] + pub fn closed(&self) -> bool { + self.state.closed + } +} + +/// A color format containing 8-bit red, green, and blue channels. +pub struct Rgb8; + +/// A color format containing 8-bit red, green, blue, and alpha channels. +pub struct Rgba8; + +/// A format that can be captured in a [`VirtualRecorder`]. +pub trait CaptureFormat: sealed::CaptureFormat {} + +impl CaptureFormat for Rgb8 {} + +impl sealed::CaptureFormat for Rgb8 { + const HAS_ALPHA: bool = false; + + fn convert_rgba(data: &mut Vec, width: u32, bytes_per_row: u32) { + let packed_width = width * 4; + // Tightly pack the rgb data, discarding the alpha and extra padding.q + let mut index = 0; + data.retain(|_| { + let retain = index % bytes_per_row < packed_width && index % 4 < 3; + index += 1; + retain + }); + } +} + +impl CaptureFormat for Rgba8 {} + +impl sealed::CaptureFormat for Rgba8 { + const HAS_ALPHA: bool = true; + + fn convert_rgba(data: &mut Vec, width: u32, bytes_per_row: u32) { + let packed_width = width * 4; + if packed_width != bytes_per_row { + // Tightly pack the rgba data + let mut index = 0; + data.retain(|_| { + let retain = index % bytes_per_row < packed_width; + index += 1; + retain + }); + } + } +} + +/// A builder of a [`VirtualRecorder`]. +pub struct VirtualRecorderBuilder { + contents: WidgetInstance, + size: Size, + scale: f32, + format: PhantomData, +} + +impl VirtualRecorderBuilder { + /// Returns a builder of a [`VirtualRecorder`] that renders `contents`. + pub fn new(contents: impl MakeWidget) -> Self { + Self { + contents: contents.make_widget(), + size: Size::new(UPx::new(800), UPx::new(600)), + scale: 1.0, + format: PhantomData, + } + } + + /// Enables transparency support to render the contents without a background + /// color. + #[must_use] + pub fn with_alpha(self) -> VirtualRecorderBuilder { + VirtualRecorderBuilder { + contents: self.contents, + size: self.size, + scale: self.scale, + format: PhantomData, + } + } +} + +impl VirtualRecorderBuilder +where + Format: CaptureFormat, +{ + /// Sets the size of the virtual window. + #[must_use] + pub fn size(mut self, size: Size) -> Self + where + Unit: Into, + { + self.size = size.map(Into::into); + self + } + + /// Returns an initialized [`VirtualRecorder`]. + pub fn finish(self) -> Result, VirtualRecorderError> { + VirtualRecorder::new(self.size, self.scale, self.contents) + } +} + +struct Capture { + buffer: wgpu::Buffer, + texture: Texture, + multisample: Texture, +} + +/// A recorder of a [`VirtualWindow`]. +pub struct VirtualRecorder { + window: VirtualWindow, + device: wgpu::Device, + queue: wgpu::Queue, + capture: Option, + data: Vec, + format: PhantomData, +} + +impl VirtualRecorder +where + Format: CaptureFormat, +{ + /// Returns a new virtual recorder that renders `contents` into a graphic of + /// `size`. + /// + /// `scale` adjusts the default DPI scaling to perform. It does not affect + /// the `size`. + pub fn new( + size: Size, + scale: f32, + contents: impl MakeWidget, + ) -> Result { + let wgpu = wgpu::Instance::default(); + let adapter = + pollster::block_on(wgpu.request_adapter(&wgpu::RequestAdapterOptions::default())) + .ok_or(VirtualRecorderError::NoAdapter)?; + let (device, queue) = pollster::block_on(adapter.request_device( + &wgpu::DeviceDescriptor { + label: None, + features: Kludgine::REQURED_FEATURES, + limits: Kludgine::adjust_limits(wgpu::Limits::downlevel_webgl2_defaults()), + }, + None, + ))?; + + let mut recorder = Self { + window: VirtualWindow::new( + contents.make_widget(), + 4, + size, + scale, + Format::HAS_ALPHA, + &device, + &queue, + ), + device, + queue, + capture: None, + data: Vec::new(), + format: PhantomData, + }; + + recorder.refresh()?; + + Ok(recorder) + } + + /// Returns the tightly-packed captured bytes. + /// + /// The layout of this data is determined by the `Format` generic. + pub fn bytes(&self) -> &[u8] { + &self.data + } + + /// Returns the current size of the recorder. + pub const fn size(&self) -> Size { + self.window.kludgine.size() + } + + fn recreate_buffers_if_needed(&mut self, size: Size, bytes: u64) { + if self + .capture + .as_ref() + .map_or(true, |capture| capture.texture.size() != size) + { + let texture = Texture::new( + &self.window.graphics(&self.device, &self.queue), + size, + wgpu::TextureFormat::Rgba8UnormSrgb, + wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::COPY_SRC + | wgpu::TextureUsages::TEXTURE_BINDING, + wgpu::FilterMode::Linear, + ); + let multisample = Texture::multisampled( + &self.window.graphics(&self.device, &self.queue), + 4, + size, + wgpu::TextureFormat::Rgba8UnormSrgb, + wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + wgpu::FilterMode::Linear, + ); + let buffer = self.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: bytes, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + self.capture = Some(Capture { + buffer, + texture, + multisample, + }); + } + } + + /// Redraws the contents. + pub fn refresh(&mut self) -> Result<(), wgpu::BufferAsyncError> { + let render_size = self.window.kludgine.size().ceil(); + let bytes_per_row = copy_buffer_aligned_bytes_per_row(render_size.width.get() * 4); + let size = u64::from(bytes_per_row) * u64::from(render_size.height.get()); + self.recreate_buffers_if_needed(render_size, size); + + let capture = self.capture.as_ref().assert("always initialized above"); + + self.window.prepare(&self.device, &self.queue); + + self.window.render( + &wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: capture.multisample.view(), + resolve_target: Some(capture.texture.view()), + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(Color::CLEAR_BLACK.into()), + store: wgpu::StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }, + &self.device, + &self.queue, + ); + + let mut encoder = self + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + capture.texture.copy_to_buffer( + wgpu::ImageCopyBuffer { + buffer: &capture.buffer, + layout: wgpu::ImageDataLayout { + offset: 0, + bytes_per_row: Some(bytes_per_row), + rows_per_image: None, + }, + }, + &mut encoder, + ); + self.queue.submit([encoder.finish()]); + + let map_result = Arc::new(Mutex::new(None)); + let condvar = Arc::new(Condvar::new()); + let slice = capture.buffer.slice(0..size); + + std::thread::scope(|scope| { + scope.spawn({ + let map_result = map_result.clone(); + let condvar = condvar.clone(); + move || { + condvar.notify_one(); + slice.map_async(wgpu::MapMode::Read, { + move |result| { + *map_result.lock().assert("thread panicked") = Some(result); + condvar.notify_one(); + } + }); + } + }); + + // Now that we've queued up the data mapping thread, let's make sure + // our vec is allocated. Since an allocation can take a moment, this + // is the perfect to do it. + self.data.clear(); + self.data.reserve(size.cast()); + + // Wait for the buffer to have been mapped. + loop { + self.device.poll(wgpu::Maintain::Poll); + + let mut result = map_result.lock().assert("thread panicked"); + if let Some(result) = result.take() { + result?; + break; + } + + let _guard = condvar + .wait_timeout(result, Duration::from_millis(1)) + .assert("thread panicked"); + } + + Ok(()) + })?; + + self.data + .extend_from_slice(bytemuck::cast_slice(&slice.get_mapped_range())); + + Format::convert_rgba(&mut self.data, render_size.width.get(), bytes_per_row); + + Ok(()) + } +} + +fn copy_buffer_aligned_bytes_per_row(width: u32) -> u32 { + (width + COPY_BYTES_PER_ROW_ALIGNMENT - 1) / COPY_BYTES_PER_ROW_ALIGNMENT + * COPY_BYTES_PER_ROW_ALIGNMENT +} + +/// An error from a [`VirtualRecorder`]. +#[derive(Debug)] +pub enum VirtualRecorderError { + /// No compatible wgpu adapters could be found. + NoAdapter, + /// An error occurred requesting a device. + RequestDevice(wgpu::RequestDeviceError), + /// The capture texture dimensions are too large to fit in the current host + /// platform's memory. + TooLarge, + /// An error occurred trying to read a buffer. + MapBuffer(wgpu::BufferAsyncError), +} + +impl From for VirtualRecorderError { + fn from(value: wgpu::RequestDeviceError) -> Self { + Self::RequestDevice(value) + } +} + +impl From for VirtualRecorderError { + fn from(value: wgpu::BufferAsyncError) -> Self { + Self::MapBuffer(value) + } +} + +impl From for VirtualRecorderError { + fn from(_: TryFromIntError) -> Self { + Self::TooLarge + } +}