#[cfg(windows)]
pub mod win32;
#[cfg(windows)]
pub use win32::clipboard::WinClipboard as Clipboard;
#[cfg(windows)]
pub use win32::{open_url, run, Tray, TrayCtx, TrayMenuItem};
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "macos")]
pub use macos::clipboard::MacClipboard as Clipboard;
#[cfg(target_os = "macos")]
pub use macos::{open_url, run, Tray, TrayCtx, TrayMenuItem};
#[cfg(not(any(windows, target_os = "macos")))]
compile_error!("windui 目前仅支持 Windows 与 macOS 平台");
use std::path::Path;
use std::path::PathBuf;
use tiny_skia::Pixmap;
use crate::event::{CursorShape, KeyEvent, MouseButton, PointerEvent, PointerKind, WindowOp};
use crate::geometry::{Color, Point, Size};
pub(crate) fn to_skia_color(c: Color) -> tiny_skia::Color {
tiny_skia::Color::from_rgba8(c.r, c.g, c.b, c.a)
}
pub(crate) fn run_offscreen(cfg: &WindowConfig, handler: &mut Box<dyn AppHandler>, path: &Path) {
let s = cfg.screenshot_scale.max(0.1);
let pw = (cfg.width as f32 * s).round().max(1.0) as i32;
let ph = (cfg.height as f32 * s).round().max(1.0) as i32;
let size = Size::new(pw, ph);
let mut pixmap = Pixmap::new(pw as u32, ph as u32).expect("分配 pixmap 失败");
pixmap.fill(to_skia_color(cfg.bg));
handler.set_scale(s);
handler.render(&mut pixmap, size);
if let Some((lx, ly)) = cfg.screenshot_rclick {
let pos = Point::new((lx as f32 * s).round() as i32, (ly as f32 * s).round() as i32);
handler.on_pointer(PointerEvent::single(PointerKind::Down, pos, MouseButton::Right));
pixmap.fill(to_skia_color(cfg.bg));
handler.render(&mut pixmap, size);
}
if let Some((lx, ly)) = cfg.screenshot_click {
let pos = Point::new((lx as f32 * s).round() as i32, (ly as f32 * s).round() as i32);
handler.on_pointer(PointerEvent::single(PointerKind::Down, pos, MouseButton::Left));
handler.on_pointer(PointerEvent::single(PointerKind::Up, pos, MouseButton::Left));
pixmap.fill(to_skia_color(cfg.bg));
handler.render(&mut pixmap, size);
}
if let Some((lx, ly)) = cfg.screenshot_hover {
let pos = Point::new((lx as f32 * s).round() as i32, (ly as f32 * s).round() as i32);
handler.on_pointer(PointerEvent::single(PointerKind::Move, pos, MouseButton::Left));
std::thread::sleep(std::time::Duration::from_millis(650));
pixmap.fill(to_skia_color(cfg.bg));
handler.render(&mut pixmap, size);
}
for _ in 0..4 {
if !handler.wants_animation() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(300));
pixmap.fill(to_skia_color(cfg.bg));
handler.render(&mut pixmap, size);
}
if let Some(parent) = path.parent() {
let _ = std::fs::create_dir_all(parent);
}
pixmap.save_png(path).expect("保存 PNG 失败");
eprintln!("[windui] 截屏已保存: {}", path.display());
}
pub struct WindowConfig {
pub title: String,
pub width: i32,
pub height: i32,
pub bg: Color,
pub centered: bool,
pub resizable: bool,
pub screenshot: Option<PathBuf>,
pub screenshot_scale: f32,
pub screenshot_rclick: Option<(i32, i32)>,
pub screenshot_click: Option<(i32, i32)>,
pub screenshot_hover: Option<(i32, i32)>,
pub tray: Option<Tray>,
pub frameless: bool,
pub animations: Option<bool>,
}
impl Default for WindowConfig {
fn default() -> Self {
Self {
title: "windui".into(),
width: 800,
height: 600,
bg: Color::hex(0xF3F3F3),
centered: false,
resizable: true,
screenshot: None,
screenshot_scale: 1.0,
screenshot_rclick: None,
screenshot_click: None,
screenshot_hover: None,
tray: None,
frameless: false,
animations: None,
}
}
}
pub trait AppHandler {
fn render(&mut self, pixmap: &mut Pixmap, size: Size);
fn on_pointer(&mut self, _ev: PointerEvent) -> bool {
false
}
fn on_key(&mut self, _ev: KeyEvent) -> bool {
false
}
fn wants_close(&self) -> bool {
false
}
fn capture_active(&self) -> bool {
false
}
fn on_capture_lost(&mut self) -> bool {
false
}
fn set_scale(&mut self, _scale: f32) {}
fn ime_caret(&self) -> Option<(i32, i32, i32)> {
None
}
fn wants_animation(&self) -> bool {
false
}
fn cursor(&self) -> CursorShape {
CursorShape::Arrow
}
fn on_pan(&mut self, _pos: Point, _dy: i32) -> bool {
false
}
fn start_fling(&mut self, _pos: Point, _vy: f32) -> bool {
false
}
fn cancel_fling(&mut self) -> bool {
false
}
fn on_drop_files(&mut self, _pos: Point, _paths: Vec<std::path::PathBuf>) -> bool {
false
}
fn window_drag_at(&self, _pos: Point) -> bool {
false
}
fn interactive_at(&self, _pos: Point) -> bool {
false
}
fn take_window_op(&mut self) -> Option<WindowOp> {
None
}
}