use std::cell::Cell;
use std::path::Path;
use std::rc::Rc;
use std::time::Duration;
use slint::platform::software_renderer::{
MinimalSoftwareWindow, PremultipliedRgbaColor, RepaintBufferType,
};
use slint::platform::{Platform, WindowAdapter, WindowEvent};
use slint::{LogicalPosition, PhysicalSize};
pub const WIDTH: u32 = 1280;
pub const HEIGHT: u32 = 820;
thread_local! {
static SIZE: Cell<(u32, u32)> = const { Cell::new((WIDTH, HEIGHT)) };
}
pub fn resize(window: &MinimalSoftwareWindow, width: u32, height: u32) {
SIZE.set((width, height));
window.set_size(PhysicalSize::new(width, height));
}
struct Headless(Rc<MinimalSoftwareWindow>);
impl Platform for Headless {
fn create_window_adapter(&self) -> Result<Rc<dyn WindowAdapter>, slint::PlatformError> {
Ok(self.0.clone())
}
}
pub fn install() -> Rc<MinimalSoftwareWindow> {
let window = MinimalSoftwareWindow::new(RepaintBufferType::NewBuffer);
window.set_size(PhysicalSize::new(WIDTH, HEIGHT));
slint::platform::set_platform(Box::new(Headless(window.clone()))).unwrap();
window
}
pub fn settle(duration: Duration) {
std::thread::sleep(duration);
slint::platform::update_timers_and_animations();
}
pub fn hover(window: &MinimalSoftwareWindow, x: f32, y: f32) {
window.dispatch_event(WindowEvent::PointerMoved {
position: LogicalPosition::new(x, y),
});
settle(Duration::from_millis(250));
}
pub fn click(window: &MinimalSoftwareWindow, x: f32, y: f32) {
let position = LogicalPosition::new(x, y);
window.dispatch_event(WindowEvent::PointerMoved { position });
let button = slint::platform::PointerEventButton::Left;
window.dispatch_event(WindowEvent::PointerPressed { position, button });
window.dispatch_event(WindowEvent::PointerReleased { position, button });
slint::platform::update_timers_and_animations();
}
pub fn key(window: &MinimalSoftwareWindow, key: slint::platform::Key) {
let text: slint::SharedString = key.into();
window.dispatch_event(WindowEvent::KeyPressed { text: text.clone() });
window.dispatch_event(WindowEvent::KeyReleased { text });
slint::platform::update_timers_and_animations();
}
pub fn leave(window: &MinimalSoftwareWindow) {
window.dispatch_event(WindowEvent::PointerExited);
slint::platform::update_timers_and_animations();
}
pub fn scroll(window: &MinimalSoftwareWindow, x: f32, y: f32, delta: f32) {
let position = LogicalPosition::new(x, y);
window.dispatch_event(WindowEvent::PointerMoved { position });
window.dispatch_event(WindowEvent::PointerScrolled {
position,
delta_x: 0.0,
delta_y: delta,
});
settle(Duration::from_millis(250));
}
pub fn render(window: &MinimalSoftwareWindow) -> Vec<PremultipliedRgbaColor> {
let (width, height) = SIZE.get();
window.request_redraw();
let mut pixels = vec![PremultipliedRgbaColor::default(); (width * height) as usize];
window.draw_if_needed(|renderer| {
renderer.render(&mut pixels, width as usize);
});
pixels
}
pub fn save(window: &MinimalSoftwareWindow, path: &Path) -> std::io::Result<()> {
let pixels = render(window);
let (width, height) = SIZE.get();
let bytes: Vec<u8> = pixels
.iter()
.flat_map(|p| [p.red, p.green, p.blue])
.collect();
let mut encoder = png::Encoder::new(
std::io::BufWriter::new(std::fs::File::create(path)?),
width,
height,
);
encoder.set_color(png::ColorType::Rgb);
encoder.set_depth(png::BitDepth::Eight);
encoder.write_header()?.write_image_data(&bytes)?;
Ok(())
}