diff --git a/crates/kas-core/src/draw/draw_shared.rs b/crates/kas-core/src/draw/draw_shared.rs index 1b1160000..182d85e83 100644 --- a/crates/kas-core/src/draw/draw_shared.rs +++ b/crates/kas-core/src/draw/draw_shared.rs @@ -7,7 +7,7 @@ use super::color::Rgba; use super::{DrawImpl, PassId}; -use crate::cast::Cast; +use crate::ActionRedraw; use crate::config::RasterConfig; use crate::geom::{Quad, Size, Vec2}; use crate::text::{Effect, TextDisplay}; @@ -62,6 +62,17 @@ pub enum ImageFormat { #[error("failed to allocate: size too large or zero-sized")] pub struct AllocError; +/// Upload failed +#[derive(Error, Debug)] +pub enum UploadError { + /// No allocation found for the [`ImageId`] used + #[error("image_upload: allocation not found")] + Missing, + /// Size of the uploaded image is wrong + #[error("image_upload: size does not match the allocation")] + Size, +} + /// Shared draw state /// /// A single [`SharedState`] instance is shared by all windows and draw contexts. @@ -93,17 +104,28 @@ pub trait DrawShared { /// Allocate an image /// /// Use [`SharedState::image_upload`] to set contents of the new image. - fn image_alloc(&mut self, size: (u32, u32)) -> Result; + fn image_alloc(&mut self, size: Size) -> Result; /// Upload an image to the GPU /// /// This should be called at least once on each image before display. May be /// called again to update the image contents. /// - /// `handle` must refer to an allocation of some size `(w, h)`, such that - /// `data.len() == b * w * h` where `b` is the number of bytes per pixel, - /// according to `format`. Data must be in row-major order. - fn image_upload(&mut self, handle: &ImageHandle, data: &[u8], format: ImageFormat); + /// `handle` must refer to an allocation of some size matching `size`. + /// + /// The image `data` must have `data.len() == b * w * h` where + /// `(w, h) == size.cast()` and `b` is the number of bytes per pixel + /// (according to `format`). Data must be in row-major order. + /// + /// On success, this returns an [`ActionRedraw`] to indicate that any + /// widgets using this image will require a redraw. + fn image_upload( + &mut self, + handle: &ImageHandle, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result; /// Potentially free an image /// @@ -117,15 +139,23 @@ pub trait DrawShared { impl DrawShared for SharedState { #[inline] - fn image_alloc(&mut self, size: (u32, u32)) -> Result { + fn image_alloc(&mut self, size: Size) -> Result { self.draw .image_alloc(size) .map(|id| ImageHandle(id, Rc::new(()))) } #[inline] - fn image_upload(&mut self, handle: &ImageHandle, data: &[u8], format: ImageFormat) { - self.draw.image_upload(handle.0, data, format); + fn image_upload( + &mut self, + handle: &ImageHandle, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result { + self.draw + .image_upload(handle.0, size, data, format) + .map(|_| ActionRedraw) } #[inline] @@ -137,7 +167,7 @@ impl DrawShared for SharedState { #[inline] fn image_size(&self, handle: &ImageHandle) -> Option { - self.draw.image_size(handle.0).map(|size| size.cast()) + self.draw.image_size(handle.0) } } @@ -156,19 +186,25 @@ pub trait DrawSharedImpl: Any { /// Allocate an image /// /// Use [`DrawSharedImpl::image_upload`] to set contents of the new image. - fn image_alloc(&mut self, size: (u32, u32)) -> Result; + fn image_alloc(&mut self, size: Size) -> Result; /// Upload an image to the GPU /// /// This should be called at least once on each image before display. May be /// called again to update the image contents. - fn image_upload(&mut self, id: ImageId, data: &[u8], format: ImageFormat); + fn image_upload( + &mut self, + id: ImageId, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result<(), UploadError>; /// Free an image allocation fn image_free(&mut self, id: ImageId); /// Query an image's size - fn image_size(&self, id: ImageId) -> Option<(u32, u32)>; + fn image_size(&self, id: ImageId) -> Option; /// Draw the image in the given `rect` fn draw_image(&self, draw: &mut Self::Draw, pass: PassId, id: ImageId, rect: Quad); diff --git a/crates/kas-core/src/draw/mod.rs b/crates/kas-core/src/draw/mod.rs index 9e5dd50cc..137745f99 100644 --- a/crates/kas-core/src/draw/mod.rs +++ b/crates/kas-core/src/draw/mod.rs @@ -43,12 +43,12 @@ mod draw_rounded; mod draw_shared; use crate::cast::Cast; -use crate::geom::Quad; +use crate::geom::{Quad, Size}; #[allow(unused)] use crate::theme::DrawCx; pub use draw::{Draw, DrawIface, DrawImpl}; pub use draw_rounded::{DrawRounded, DrawRoundedImpl}; -pub use draw_shared::{AllocError, ImageFormat, ImageHandle, ImageId}; +pub use draw_shared::{AllocError, ImageFormat, ImageHandle, ImageId, UploadError}; pub use draw_shared::{DrawShared, DrawSharedImpl, SharedState}; use std::time::{Duration, Instant}; @@ -73,7 +73,7 @@ pub struct Allocation { #[cfg_attr(not(feature = "internal_doc"), doc(hidden))] #[cfg_attr(docsrs, doc(cfg(internal_doc)))] pub trait Allocator { - fn allocate(&mut self, size: (u32, u32)) -> Result; + fn allocate(&mut self, size: Size) -> Result; fn deallocate(&mut self, atlas: u32, alloc: u32); } diff --git a/crates/kas-image/Cargo.toml b/crates/kas-image/Cargo.toml index 85231e45f..a53408e59 100644 --- a/crates/kas-image/Cargo.toml +++ b/crates/kas-image/Cargo.toml @@ -38,6 +38,7 @@ png = ["dep:image", "image/png"] webp = ["dep:image", "image/webp"] [dependencies] +log = "0.4" tiny-skia = { version = "0.11.0" } resvg = { version = "0.45.0", optional = true } usvg = { version = "0.45.0", optional = true } diff --git a/crates/kas-image/src/canvas.rs b/crates/kas-image/src/canvas.rs index 35f0f48b1..df40e3f7c 100644 --- a/crates/kas-image/src/canvas.rs +++ b/crates/kas-image/src/canvas.rs @@ -208,11 +208,11 @@ mod Canvas { fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) { if let Some((program, mut pixmap)) = cx.try_pop::<(P, Pixmap)>() { debug_assert!(matches!(self.inner.get_mut(), State::Rendering)); - let size = (pixmap.width(), pixmap.height()); + let size = (pixmap.width(), pixmap.height()).cast(); let ds = cx.draw_shared(); if let Some(im_size) = self.image.as_ref().and_then(|h| ds.image_size(h)) - && im_size != Size::conv(size) + && im_size != size && let Some(handle) = self.image.take() { ds.image_free(handle); @@ -223,17 +223,18 @@ mod Canvas { } if let Some(handle) = self.image.as_ref() { - ds.image_upload(handle, pixmap.data(), ImageFormat::Rgba8); + match ds.image_upload(handle, size, pixmap.data(), ImageFormat::Rgba8) { + Ok(_) => cx.redraw(), + Err(err) => log::warn!("Canvas: image upload failed: {err}"), + } } - cx.redraw(); - - let rect_size: (u32, u32) = self.rect().size.cast(); + let own_size = self.rect().size; let state = self.inner.get_mut(); - if rect_size != size { + if own_size != size { // Possible if a redraw was in progress when set_rect was called - pixmap = if let Some(px) = Pixmap::new(rect_size.0, rect_size.1) { + pixmap = if let Some(px) = Pixmap::new(own_size.0.cast(), own_size.1.cast()) { px } else { *state = State::Initial(program); diff --git a/crates/kas-image/src/image.rs b/crates/kas-image/src/image.rs index 6e9238709..e7d832b01 100644 --- a/crates/kas-image/src/image.rs +++ b/crates/kas-image/src/image.rs @@ -168,18 +168,22 @@ mod Image { // TODO(opt): we converted to RGBA8 since this is the only format common // to both the image and wgpu crates. It may not be optimal however. // It also assumes that the image colour space is sRGB. - let size = image.dimensions(); + let size = image.dimensions().cast(); let draw = cx.draw_shared(); - match draw.image_alloc(size) { - Ok(handle) => { - draw.image_upload(&handle, &image, kas::draw::ImageFormat::Rgba8); - self.raw.set(cx, handle); - } + let handle = match draw.image_alloc(size) { + Ok(handle) => handle, Err(err) => { - warn_about_error("Failed to allocate image", &err); + log::warn!("Image: allocate failed: {err}"); + return; } - } + }; + + match draw.image_upload(&handle, size, &image, kas::draw::ImageFormat::Rgba8) { + Ok(_) => cx.redraw(), + Err(err) => log::warn!("Image: image upload failed: {err}"), + }; + self.raw.set(cx, handle); } else { self.raw.clear(cx); } diff --git a/crates/kas-image/src/sprite.rs b/crates/kas-image/src/sprite.rs index f3c61d580..a81da9c6c 100644 --- a/crates/kas-image/src/sprite.rs +++ b/crates/kas-image/src/sprite.rs @@ -57,6 +57,24 @@ mod Sprite { } } + /// Access the current [`ImageHandle`], if any + /// + /// This handle may be used with [`DrawShared`](kas::draw::DrawShared) + /// methods. + #[inline] + pub fn handle(&self) -> Option<&ImageHandle> { + self.handle.as_ref() + } + + /// Get the image buffer size + /// + /// This is the size of the image last assigned using [`Sprite::set`]. + /// Initially it is [`Size::ZERO`]. + #[inline] + pub fn image_size(&self) -> Size { + self.image_size + } + /// Remove image (set empty) pub fn clear(&mut self, cx: &mut EventCx) { if let Some(handle) = self.handle.take() { diff --git a/crates/kas-image/src/svg.rs b/crates/kas-image/src/svg.rs index 1f1fc3f6a..bfb9e4414 100644 --- a/crates/kas-image/src/svg.rs +++ b/crates/kas-image/src/svg.rs @@ -299,11 +299,11 @@ mod Svg { fn handle_messages(&mut self, cx: &mut EventCx, _: &Self::Data) { if let Some(pixmap) = cx.try_pop::() { - let size = (pixmap.width(), pixmap.height()); + let size = (pixmap.width(), pixmap.height()).cast(); let ds = cx.draw_shared(); if let Some(im_size) = self.image.as_ref().and_then(|h| ds.image_size(h)) - && im_size != Size::conv(size) + && im_size != size && let Some(handle) = self.image.take() { ds.image_free(handle); @@ -314,10 +314,12 @@ mod Svg { } if let Some(handle) = self.image.as_ref() { - ds.image_upload(handle, pixmap.data(), ImageFormat::Rgba8); + match ds.image_upload(handle, size, pixmap.data(), ImageFormat::Rgba8) { + Ok(_) => cx.redraw(), + Err(err) => log::warn!("Svg: image upload failed: {err}"), + } } - cx.redraw(); self.inner = match std::mem::take(&mut self.inner) { State::None => State::None, State::Initial(source) | State::Rendering(source) | State::Ready(source, _) => { @@ -325,9 +327,8 @@ mod Svg { } }; - let own_size: (u32, u32) = self.rect().size.cast(); - if size != own_size - && let Some(fut) = self.inner.resize(own_size) + if size != self.rect().size + && let Some(fut) = self.inner.resize(self.rect().size.cast()) { cx.send_spawn(self.id(), fut); } diff --git a/crates/kas-soft/src/atlas.rs b/crates/kas-soft/src/atlas.rs index 30655d23e..d32290c2a 100644 --- a/crates/kas-soft/src/atlas.rs +++ b/crates/kas-soft/src/atlas.rs @@ -11,7 +11,9 @@ use std::collections::HashMap; use kas::autoimpl; use kas::cast::traits::*; -use kas::draw::{AllocError, Allocation, Allocator, ImageFormat, ImageId, PassId, color}; +use kas::draw::{ + AllocError, Allocation, Allocator, ImageFormat, ImageId, PassId, UploadError, color, +}; use kas::geom::{Coord, Offset, Quad, Rect, Size, Vec2}; use kas::text::raster::{RenderQueue, Sprite, SpriteAllocator, SpriteType, UnpreparedSprite}; @@ -128,12 +130,12 @@ impl Allocator for Atlases { /// Allocate space within a texture atlas /// /// Fails if `size` is zero in any dimension. - fn allocate(&mut self, size: (u32, u32)) -> Result { + fn allocate(&mut self, size: Size) -> Result { if size.0 == 0 || size.1 == 0 { return Err(AllocError); } - let (atlas, alloc, tex_size) = self.allocate_space((size.0.cast(), size.1.cast()))?; + let (atlas, alloc, tex_size) = self.allocate_space((size.0, size.1))?; let origin = (alloc.rectangle.min.x.cast(), alloc.rectangle.min.y.cast()); @@ -234,7 +236,7 @@ impl AtlasWindow { #[derive(Debug)] struct Image { - size: (u32, u32), + size: Size, alloc: Allocation, } @@ -571,7 +573,7 @@ impl Shared { } /// Allocate an image - pub fn alloc(&mut self, size: (u32, u32)) -> Result { + pub fn alloc(&mut self, size: Size) -> Result { let id = self.next_image_id(); let alloc = self.atlas_rgba.allocate(size)?; let image = Image { size, alloc }; @@ -580,16 +582,29 @@ impl Shared { } /// Upload an image - pub fn upload(&mut self, id: ImageId, data: &[u8], format: ImageFormat) { + pub fn upload( + &mut self, + id: ImageId, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result<(), UploadError> { match format { ImageFormat::Rgba8 => (), } - if let Some(image) = self.images.get_mut(&id) { - let atlas = image.alloc.atlas.cast(); - let origin = image.alloc.origin; - self.atlas_rgba.upload(atlas, origin, image.size, data); + let Some(image) = self.images.get_mut(&id) else { + return Err(UploadError::Missing); + }; + if size != image.size { + return Err(UploadError::Size); } + + let atlas = image.alloc.atlas.cast(); + let origin = image.alloc.origin; + self.atlas_rgba + .upload(atlas, origin, image.size.cast(), data); + Ok(()) } /// Free an image allocation @@ -600,7 +615,7 @@ impl Shared { } /// Query image size - pub fn image_size(&self, id: ImageId) -> Option<(u32, u32)> { + pub fn image_size(&self, id: ImageId) -> Option { self.images.get(&id).map(|im| im.size) } @@ -641,16 +656,16 @@ impl SpriteAllocator for Shared { } fn alloc_mask(&mut self, size: (u32, u32)) -> Result { - self.atlas_mask.allocate(size) + self.atlas_mask.allocate(size.cast()) } fn alloc_rgba_mask(&mut self, mut size: (u32, u32)) -> Result { size.0 += 2; // allow for correct filtering - self.atlas_rgba_mask.allocate(size) + self.atlas_rgba_mask.allocate(size.cast()) } fn alloc_rgba(&mut self, size: (u32, u32)) -> Result { - self.atlas_rgba.allocate(size) + self.atlas_rgba.allocate(size.cast()) } } diff --git a/crates/kas-soft/src/draw.rs b/crates/kas-soft/src/draw.rs index a378dc061..2b3af460b 100644 --- a/crates/kas-soft/src/draw.rs +++ b/crates/kas-soft/src/draw.rs @@ -7,7 +7,9 @@ use super::{atlas, basic}; use kas::cast::Cast; -use kas::draw::{AllocError, DrawImpl, DrawSharedImpl, PassId, PassType, WindowCommon}; +use kas::draw::{ + AllocError, DrawImpl, DrawSharedImpl, PassId, PassType, UploadError, WindowCommon, +}; use kas::draw::{ImageFormat, ImageId, color}; use kas::geom::{Quad, Size, Vec2}; use kas::prelude::{Offset, Rect}; @@ -151,13 +153,19 @@ impl DrawSharedImpl for Shared { } #[inline] - fn image_alloc(&mut self, size: (u32, u32)) -> Result { + fn image_alloc(&mut self, size: Size) -> Result { self.images.alloc(size) } #[inline] - fn image_upload(&mut self, id: ImageId, data: &[u8], format: ImageFormat) { - self.images.upload(id, data, format); + fn image_upload( + &mut self, + id: ImageId, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result<(), UploadError> { + self.images.upload(id, size, data, format) } #[inline] @@ -166,7 +174,7 @@ impl DrawSharedImpl for Shared { } #[inline] - fn image_size(&self, id: ImageId) -> Option<(u32, u32)> { + fn image_size(&self, id: ImageId) -> Option { self.images.image_size(id) } diff --git a/crates/kas-wgpu/src/draw/atlases.rs b/crates/kas-wgpu/src/draw/atlases.rs index cc00dbb0e..6340152b5 100644 --- a/crates/kas-wgpu/src/draw/atlases.rs +++ b/crates/kas-wgpu/src/draw/atlases.rs @@ -263,12 +263,12 @@ impl Allocator for Pipeline { /// Allocate space within a texture atlas /// /// Fails if `size` is zero in any dimension. - fn allocate(&mut self, size: (u32, u32)) -> Result { + fn allocate(&mut self, size: Size) -> Result { if size.0 == 0 || size.1 == 0 { return Err(AllocError); } - let (atlas, alloc, tex_size) = self.allocate_space((size.0.cast(), size.1.cast()))?; + let (atlas, alloc, tex_size) = self.allocate_space((size.0, size.1))?; let origin = (alloc.rectangle.min.x.cast(), alloc.rectangle.min.y.cast()); diff --git a/crates/kas-wgpu/src/draw/draw_pipe.rs b/crates/kas-wgpu/src/draw/draw_pipe.rs index ba890949d..ac64c77ef 100644 --- a/crates/kas-wgpu/src/draw/draw_pipe.rs +++ b/crates/kas-wgpu/src/draw/draw_pipe.rs @@ -330,14 +330,20 @@ impl DrawSharedImpl for DrawPipe { } #[inline] - fn image_alloc(&mut self, size: (u32, u32)) -> Result { + fn image_alloc(&mut self, size: Size) -> Result { self.images.alloc(size) } #[inline] - fn image_upload(&mut self, id: ImageId, data: &[u8], format: ImageFormat) { + fn image_upload( + &mut self, + id: ImageId, + size: Size, + data: &[u8], + format: ImageFormat, + ) -> Result<(), UploadError> { self.images - .upload(&self.device, &self.queue, id, data, format); + .upload(&self.device, &self.queue, id, size, data, format) } #[inline] @@ -346,7 +352,7 @@ impl DrawSharedImpl for DrawPipe { } #[inline] - fn image_size(&self, id: ImageId) -> Option<(u32, u32)> { + fn image_size(&self, id: ImageId) -> Option { self.images.image_size(id) } diff --git a/crates/kas-wgpu/src/draw/images.rs b/crates/kas-wgpu/src/draw/images.rs index 42253cfbd..bc8167dcb 100644 --- a/crates/kas-wgpu/src/draw/images.rs +++ b/crates/kas-wgpu/src/draw/images.rs @@ -10,14 +10,14 @@ use std::collections::HashMap; use std::mem::size_of; use super::{ShaderManager, atlases}; -use kas::cast::Conv; -use kas::draw::{AllocError, Allocation, Allocator, ImageFormat, ImageId, PassId}; -use kas::geom::{Quad, Vec2}; +use kas::cast::{Cast, Conv}; +use kas::draw::{AllocError, Allocation, Allocator, ImageFormat, ImageId, PassId, UploadError}; +use kas::geom::{Quad, Size, Vec2}; use kas::text::raster::{RenderQueue, Sprite, SpriteAllocator, SpriteType, UnpreparedSprite}; #[derive(Debug)] struct Image { - size: (u32, u32), + size: Size, alloc: Allocation, } @@ -29,7 +29,7 @@ impl Image { data: &[u8], ) { // TODO(opt): use StagingBelt for upload (when supported)? Or our own equivalent. - let size = self.size; + let size: (u32, u32) = self.size.cast(); assert!(!data.is_empty()); assert_eq!(data.len(), 4 * usize::conv(size.0) * usize::conv(size.1)); queue.write_texture( @@ -233,7 +233,7 @@ impl Images { } /// Allocate an image - pub fn alloc(&mut self, size: (u32, u32)) -> Result { + pub fn alloc(&mut self, size: Size) -> Result { let id = self.next_image_id(); let alloc = self.atlas_rgba.allocate(size)?; let image = Image { size, alloc }; @@ -247,9 +247,10 @@ impl Images { device: &wgpu::Device, queue: &wgpu::Queue, id: ImageId, + size: Size, data: &[u8], format: ImageFormat, - ) { + ) -> Result<(), UploadError> { // The atlas pipe allocates textures lazily. Ensure ours is ready: self.atlas_rgba.prepare(device); @@ -257,9 +258,15 @@ impl Images { ImageFormat::Rgba8 => (), } - if let Some(image) = self.images.get_mut(&id) { - image.upload(&self.atlas_rgba, queue, data); + let Some(image) = self.images.get_mut(&id) else { + return Err(UploadError::Missing); + }; + if size != image.size { + return Err(UploadError::Size); } + + image.upload(&self.atlas_rgba, queue, data); + Ok(()) } /// Free an image allocation @@ -270,7 +277,7 @@ impl Images { } /// Query image size - pub fn image_size(&self, id: ImageId) -> Option<(u32, u32)> { + pub fn image_size(&self, id: ImageId) -> Option { self.images.get(&id).map(|im| im.size) } @@ -389,18 +396,18 @@ impl SpriteAllocator for Images { } fn alloc_mask(&mut self, size: (u32, u32)) -> Result { - self.atlas_mask.allocate(size) + self.atlas_mask.allocate(size.cast()) } fn alloc_rgba_mask(&mut self, size: (u32, u32)) -> Result { self.atlas_rgba_mask .as_mut() .expect("subpixel rendering feature is unavailable") - .allocate(size) + .allocate(size.cast()) } fn alloc_rgba(&mut self, size: (u32, u32)) -> Result { - self.atlas_rgba.allocate(size) + self.atlas_rgba.allocate(size.cast()) } } diff --git a/crates/kas-widgets/src/adapt/adapt.rs b/crates/kas-widgets/src/adapt/adapt.rs index f95e311e2..cc6ffd5fc 100644 --- a/crates/kas-widgets/src/adapt/adapt.rs +++ b/crates/kas-widgets/src/adapt/adapt.rs @@ -45,10 +45,11 @@ mod Adapt { state: W::Data, #[widget(&self.state)] inner: W, - configure_handler: Option>, - update_handler: Option>, - timer_handlers: LinearMap>, - message_handlers: Vec>, + configure_handler: Option>, + update_handler: Option>, + timer_handlers: + LinearMap>, + message_handlers: Vec>, } impl Self { @@ -69,7 +70,7 @@ mod Adapt { /// Add a handler to be called on configuration pub fn on_configure(mut self, handler: F) -> Self where - F: Fn(&mut AdaptConfigCx, &mut W::Data) + 'static, + F: Fn(&mut AdaptConfigCx, &mut W, &mut W::Data) + 'static, { debug_assert!(self.configure_handler.is_none()); self.configure_handler = Some(Box::new(handler)); @@ -81,7 +82,7 @@ mod Adapt { /// Children will be updated after the handler is called. pub fn on_update(mut self, handler: F) -> Self where - F: Fn(&mut AdaptConfigCx, &mut W::Data, &A) + 'static, + F: Fn(&mut AdaptConfigCx, &mut W, &mut W::Data, &A) + 'static, { debug_assert!(self.update_handler.is_none()); self.update_handler = Some(Box::new(handler)); @@ -95,7 +96,7 @@ mod Adapt { /// of [`EventState::send_async`](kas::event::EventState::send_async). pub fn on_timer(mut self, timer_id: TimerHandle, handler: H) -> Self where - H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static, + H: Fn(&mut AdaptEventCx, &mut W, &mut W::Data, &A) + 'static, { debug_assert!(self.timer_handlers.get(&timer_id).is_none()); self.timer_handlers.insert(timer_id, Box::new(handler)); @@ -113,7 +114,7 @@ mod Adapt { M: Debug + 'static, H: Fn(&mut AdaptEventCx, &mut W::Data, M) + 'static, { - self.on_messages(move |cx, state, _data| { + self.on_messages(move |cx, _, state, _data| { if let Some(m) = cx.try_pop() { handler(cx, state, m); } @@ -123,7 +124,7 @@ mod Adapt { /// Add a generic message handler pub fn on_messages(mut self, handler: H) -> Self where - H: Fn(&mut AdaptEventCx, &mut W::Data, &A) + 'static, + H: Fn(&mut AdaptEventCx, &mut W, &mut W::Data, &A) + 'static, { self.message_handlers.push(Box::new(handler)); self @@ -136,14 +137,14 @@ mod Adapt { fn configure(&mut self, cx: &mut ConfigCx) { if let Some(handler) = self.configure_handler.as_ref() { let mut cx = AdaptConfigCx::new(cx, self.id()); - handler(&mut cx, &mut self.state); + handler(&mut cx, &mut self.inner, &mut self.state); } } fn update(&mut self, cx: &mut ConfigCx, data: &A) { if let Some(handler) = self.update_handler.as_ref() { let mut cx = AdaptConfigCx::new(cx, self.id()); - handler(&mut cx, &mut self.state, data); + handler(&mut cx, &mut self.inner, &mut self.state, data); } } @@ -152,7 +153,7 @@ mod Adapt { Event::Timer(timer_id) => { if let Some(handler) = self.timer_handlers.get(&timer_id) { let mut cx = AdaptEventCx::new(cx, self.id()); - handler(&mut cx, &mut self.state, data); + handler(&mut cx, &mut self.inner, &mut self.state, data); cx.update(self.as_node(data)); Used } else { @@ -167,7 +168,7 @@ mod Adapt { let count = cx.msg_op_count(); let mut cx = AdaptEventCx::new(cx, self.id()); for handler in self.message_handlers.iter() { - handler(&mut cx, &mut self.state, data); + handler(&mut cx, &mut self.inner, &mut self.state, data); } if cx.msg_op_count() != count { cx.update(self.as_node(data)); diff --git a/examples/gallery.rs b/examples/gallery.rs index b754f98be..d769e5f14 100644 --- a/examples/gallery.rs +++ b/examples/gallery.rs @@ -341,7 +341,7 @@ Demonstration of *as-you-type* formatting from **Markdown**. let ui = ui .with_state(Data::default()) - .on_update(|_, data, app_data: &AppData| data.disabled = app_data.disabled) + .on_update(|_, _, data, app_data: &AppData| data.disabled = app_data.disabled) .on_message(|_, data, MsgDirection| { data.dir = match data.dir { Direction::Up => Direction::Right, diff --git a/examples/stopwatch.rs b/examples/stopwatch.rs index 9e4ff3a57..adbf860ca 100644 --- a/examples/stopwatch.rs +++ b/examples/stopwatch.rs @@ -47,7 +47,7 @@ fn make_ui() -> impl Widget { cx.request_frame_timer(TIMER); } }) - .on_timer(TIMER, |cx, timer, _| { + .on_timer(TIMER, |cx, _, timer, _| { if let Some(last) = timer.last { let now = Instant::now(); timer.elapsed += now - last; diff --git a/examples/sync-counter.rs b/examples/sync-counter.rs index bd24a5a1b..0f30d49dc 100644 --- a/examples/sync-counter.rs +++ b/examples/sync-counter.rs @@ -53,7 +53,7 @@ fn counter(title: &str) -> Window { let ui = ui .with_state(initial) - .on_update(|_, state, count| state.0 = *count) + .on_update(|_, _, state, count| state.0 = *count) .on_message(|_, state, SetValue(v)| state.1 = v); Window::new(ui, title).escapable() }