Add inner: &mut W parameter to Adapt closures and revise DrawShared::image_* fns#627
Add inner: &mut W parameter to Adapt closures and revise DrawShared::image_* fns#627
DrawShared::image_* fns#627Conversation
b44d372 to
f4a0621
Compare
|
I think use kas::cast::Conv;
use kas::draw::ImageFormat;
use kas::event::TimerHandle;
use kas::geom::Size;
use kas::layout::LogicalSize;
use kas::Widget;
use kas_image::Sprite;
use kas_widgets::AdaptWidget;
use std::time::Duration;
const TIMER: TimerHandle = TimerHandle::new(0, true);
#[derive(Debug, Default)]
struct Buffer {
size: Size,
buffer: Vec<u8>,
}
async fn draw(mut buf: Buffer) -> Buffer {
let len = usize::conv(buf.size.0) * usize::conv(buf.size.1);
buf.buffer.resize(len, 0);
// TODO: draw to buffer
buf
}
enum State {
Ready(Buffer),
Drawing,
}
/// Make a sprite which draws itself in a worker thread on update
fn make_sprite(size: LogicalSize) -> impl Widget<Data = ()> {
Sprite::new()
.with_logical_size(size)
.with_state(State::Ready(Buffer::default))
.on_update(|cx, sprite, state, _| {
if matches!(state, State::Ready(_)) {
// We only have a ConfigCx here; use a timer to get an EventCx:
cx.request_timer(TIMER, Duration::ZERO)
}
})
.on_timer(TIMER, |cx, sprite, state, _| {
if let State::Ready(buf) = std::mem::replace(state, State::Drawing) {
if let Some(size) = sprite.allocate(cx) {
buf.size = size;
let id = cx.id();
cx.send_spawn(id, || draw(buf))
}
}
})
.on_message(|cx, sprite, buf| {
if let Some(handle) = sprite.handle() {
let draw = cx.draw_shared();
if let Some(alloc_size) = draw.image_size(handle) && alloc_size == buf.size {
cx.draw_shared().image_upload(handle, &buf.buffer, ImageFormat::Rgba8);
}
}
})
}(I wanted to add this as example text, but cannot because I may merge this (or similar). @Kwarrtz can you test, or maybe update |
|
I think iterated function systems might be needlessly complex as an example, but I can try to come up with something to demonstrate using I'm a bit confused about the purpose of |
|
Kas widgets don't choose their own size; they "suggest" size requirements; as such the size may be changed e.g. by the user resizing the window (depending on whatever layout is used; usually fixed-size widgets do get their chosen size). Since this is "logical size", the size could also change if the scale factor changes, e.g. by dragging the window to another screen. The state machine is to only draw on demand (i.e. on resize), and if not already drawing. In your case you may still need something like this to respond to resizes. Also, you might want to use a double buffer (one to draw into, another to read from) to avoid tearing and making |
|
I'm happy to merge this, though not completely happy about |
|
Ah, I see, I'd actually somehow missed that Also, if you're not happy how |
Yes. So
The allocation needs to happen somewhere. But yes, Personally I would choose to use a custom widget at this point, but it may be useful to provide enough tools to let users implement something like this without a custom widget. |
|
Without .on_timer(TIMER, |cx, sprite, state, _| {
if let State::Ready(buf) = std::mem::replace(state, State::Drawing) {
let draw = cx.draw_shared();
let size = sprite.rect().size;
if let Some(handle) = sprite.handle() && draw.image_size(handle) == Some(size) {
return;
}
if let Ok(handle) = draw.image_alloc(size.cast()) && sprite.set(cx, handle) {
buf.size = size;
let id = cx.id();
cx.send_spawn(id, || draw(buf));
}
}
})You could simplify a bit, but should certainly not re-allocate each time this is called since there's no way to ensure |
|
Yes, my thought was exactly that if a custom widget was needed for the In either case, I'll try to put together a concrete example tomorrow. |
f4a0621 to
c431a1b
Compare
DrawShared::image_* fns
|
This got several updates motivated by #631:
|
c431a1b to
5cdb278
Compare
Draft because: I'm undecided whether this is a good design; the purpose of
Adaptis to add user-controllable state.