use std::any::Any;
use std::time::Duration;
use crate::application::Application;
use crate::backend::window as backend;
use crate::common_util::Counter;
use crate::dialog::{FileDialogOptions, FileInfo};
use crate::error::Error;
use crate::keyboard::KeyEvent;
use crate::kurbo::{Insets, Point, Rect, Size};
use crate::menu::Menu;
use crate::mouse::{Cursor, CursorDesc, MouseEvent};
use crate::region::Region;
use crate::scale::Scale;
use crate::text::{Event, InputHandler};
use piet_common::PietText;
#[cfg(feature = "raw-win-handle")]
use raw_window_handle::{HasRawWindowHandle, RawWindowHandle};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
pub struct TimerToken(u64);
impl TimerToken {
pub const INVALID: TimerToken = TimerToken(0);
pub fn next() -> TimerToken {
static TIMER_COUNTER: Counter = Counter::new();
TimerToken(TIMER_COUNTER.next())
}
pub const fn from_raw(id: u64) -> TimerToken {
TimerToken(id)
}
pub const fn into_raw(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
pub struct TextFieldToken(u64);
impl TextFieldToken {
pub const INVALID: TextFieldToken = TextFieldToken(0);
pub fn next() -> TextFieldToken {
static TEXT_FIELD_COUNTER: Counter = Counter::new();
TextFieldToken(TEXT_FIELD_COUNTER.next())
}
pub const fn from_raw(id: u64) -> TextFieldToken {
TextFieldToken(id)
}
pub const fn into_raw(self) -> u64 {
self.0
}
}
#[derive(Clone)]
pub struct IdleHandle(backend::IdleHandle);
impl IdleHandle {
pub fn add_idle<F>(&self, callback: F)
where
F: FnOnce(&mut dyn WinHandler) + Send + 'static,
{
self.0.add_idle_callback(callback)
}
pub fn schedule_idle(&mut self, token: IdleToken) {
self.0.add_idle_token(token)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
pub struct IdleToken(usize);
impl IdleToken {
pub const fn new(raw: usize) -> IdleToken {
IdleToken(raw)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Hash)]
pub struct FileDialogToken(u64);
impl FileDialogToken {
pub const INVALID: FileDialogToken = FileDialogToken(0);
pub fn next() -> FileDialogToken {
static COUNTER: Counter = Counter::new();
FileDialogToken(COUNTER.next())
}
pub const fn from_raw(id: u64) -> FileDialogToken {
FileDialogToken(id)
}
pub const fn into_raw(self) -> u64 {
self.0
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum WindowLevel {
AppWindow,
Tooltip(WindowHandle),
DropDown(WindowHandle),
Modal(WindowHandle),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WindowState {
Maximized,
Minimized,
Restored,
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct WindowHandle(pub(crate) backend::WindowHandle);
impl WindowHandle {
pub fn show(&self) {
self.0.show()
}
pub fn close(&self) {
self.0.close()
}
pub fn hide(&self) {
self.0.hide()
}
pub fn resizable(&self, resizable: bool) {
self.0.resizable(resizable)
}
pub fn set_window_state(&mut self, state: WindowState) {
self.0.set_window_state(state);
}
pub fn get_window_state(&self) -> WindowState {
self.0.get_window_state()
}
pub fn handle_titlebar(&self, val: bool) {
self.0.handle_titlebar(val);
}
pub fn show_titlebar(&self, show_titlebar: bool) {
self.0.show_titlebar(show_titlebar)
}
pub fn set_position(&self, position: impl Into<Point>) {
self.0.set_position(position.into())
}
pub fn set_always_on_top(&self, always_on_top: bool) {
self.0.set_always_on_top(always_on_top);
}
pub fn set_input_region(&self, region: Option<Region>) {
self.0.set_input_region(region)
}
pub fn get_position(&self) -> Point {
self.0.get_position()
}
pub fn content_insets(&self) -> Insets {
self.0.content_insets()
}
pub fn set_size(&self, size: impl Into<Size>) {
self.0.set_size(size.into())
}
pub fn get_size(&self) -> Size {
self.0.get_size()
}
pub fn bring_to_front_and_focus(&self) {
self.0.bring_to_front_and_focus()
}
pub fn request_anim_frame(&self) {
self.0.request_anim_frame();
}
pub fn invalidate(&self) {
self.0.invalidate();
}
pub fn invalidate_rect(&self, rect: Rect) {
self.0.invalidate_rect(rect);
}
pub fn set_title(&self, title: &str) {
self.0.set_title(title)
}
pub fn set_menu(&self, menu: Menu) {
self.0.set_menu(menu.into_inner())
}
pub fn text(&self) -> PietText {
self.0.text()
}
pub fn add_text_field(&self) -> TextFieldToken {
self.0.add_text_field()
}
pub fn remove_text_field(&self, token: TextFieldToken) {
self.0.remove_text_field(token)
}
pub fn set_focused_text_field(&self, active_field: Option<TextFieldToken>) {
self.0.set_focused_text_field(active_field)
}
pub fn update_text_field(&self, token: TextFieldToken, update: Event) {
self.0.update_text_field(token, update)
}
pub fn request_timer(&self, deadline: Duration) -> TimerToken {
self.0.request_timer(instant::Instant::now() + deadline)
}
pub fn set_cursor(&mut self, cursor: &Cursor) {
self.0.set_cursor(cursor)
}
pub fn make_cursor(&self, desc: &CursorDesc) -> Option<Cursor> {
self.0.make_cursor(desc)
}
pub fn open_file(&mut self, options: FileDialogOptions) -> Option<FileDialogToken> {
self.0.open_file(options)
}
pub fn save_as(&mut self, options: FileDialogOptions) -> Option<FileDialogToken> {
self.0.save_as(options)
}
pub fn show_context_menu(&self, menu: Menu, pos: Point) {
self.0.show_context_menu(menu.into_inner(), pos)
}
pub fn get_idle_handle(&self) -> Option<IdleHandle> {
self.0.get_idle_handle().map(IdleHandle)
}
pub fn get_scale(&self) -> Result<Scale, Error> {
self.0.get_scale().map_err(Into::into)
}
}
#[cfg(feature = "raw-win-handle")]
unsafe impl HasRawWindowHandle for WindowHandle {
fn raw_window_handle(&self) -> RawWindowHandle {
self.0.raw_window_handle()
}
}
pub struct WindowBuilder(backend::WindowBuilder);
impl WindowBuilder {
pub fn new(app: Application) -> WindowBuilder {
WindowBuilder(backend::WindowBuilder::new(app.backend_app))
}
pub fn set_handler(&mut self, handler: Box<dyn WinHandler>) {
self.0.set_handler(handler)
}
pub fn set_size(&mut self, size: Size) {
self.0.set_size(size)
}
pub fn set_min_size(&mut self, size: Size) {
self.0.set_min_size(size)
}
pub fn resizable(&mut self, resizable: bool) {
self.0.resizable(resizable)
}
pub fn show_titlebar(&mut self, show_titlebar: bool) {
self.0.show_titlebar(show_titlebar)
}
pub fn set_always_on_top(&mut self, always_on_top: bool) {
self.0.set_always_on_top(always_on_top);
}
pub fn set_transparent(&mut self, transparent: bool) {
self.0.set_transparent(transparent)
}
pub fn set_position(&mut self, position: Point) {
self.0.set_position(position);
}
pub fn set_level(&mut self, level: WindowLevel) {
self.0.set_level(level);
}
pub fn set_title(&mut self, title: impl Into<String>) {
self.0.set_title(title)
}
pub fn set_menu(&mut self, menu: Menu) {
self.0.set_menu(menu.into_inner())
}
pub fn set_window_state(&mut self, state: WindowState) {
self.0.set_window_state(state);
}
pub fn build(self) -> Result<WindowHandle, Error> {
self.0.build().map(WindowHandle).map_err(Into::into)
}
}
pub trait WinHandler {
fn connect(&mut self, handle: &WindowHandle);
#[allow(unused_variables)]
fn size(&mut self, size: Size) {}
#[allow(unused_variables)]
fn scale(&mut self, scale: Scale) {}
fn prepare_paint(&mut self);
fn paint(&mut self, piet: &mut piet_common::Piet, invalid: &Region);
#[allow(unused_variables)]
fn rebuild_resources(&mut self) {}
#[allow(unused_variables)]
fn command(&mut self, id: u32) {}
#[allow(unused_variables)]
fn save_as(&mut self, token: FileDialogToken, file: Option<FileInfo>) {}
#[allow(unused_variables)]
fn open_file(&mut self, token: FileDialogToken, file: Option<FileInfo>) {}
#[allow(unused_variables)]
fn open_files(&mut self, token: FileDialogToken, files: Vec<FileInfo>) {}
#[allow(unused_variables)]
fn key_down(&mut self, event: KeyEvent) -> bool {
false
}
#[allow(unused_variables)]
fn key_up(&mut self, event: KeyEvent) {}
#[allow(unused_variables)]
fn acquire_input_lock(
&mut self,
token: TextFieldToken,
mutable: bool,
) -> Box<dyn InputHandler> {
panic!("acquire_input_lock was called on a WinHandler that did not expect text input.")
}
#[allow(unused_variables)]
fn release_input_lock(&mut self, token: TextFieldToken) {
panic!("release_input_lock was called on a WinHandler that did not expect text input.")
}
#[allow(unused_variables)]
fn wheel(&mut self, event: &MouseEvent) {}
#[allow(unused_variables)]
fn zoom(&mut self, delta: f64) {}
#[allow(unused_variables)]
fn mouse_move(&mut self, event: &MouseEvent) {}
#[allow(unused_variables)]
fn mouse_down(&mut self, event: &MouseEvent) {}
#[allow(unused_variables)]
fn mouse_up(&mut self, event: &MouseEvent) {}
fn mouse_leave(&mut self) {}
#[allow(unused_variables)]
fn timer(&mut self, token: TimerToken) {}
#[allow(unused_variables)]
fn got_focus(&mut self) {}
#[allow(unused_variables)]
fn lost_focus(&mut self) {}
fn request_close(&mut self) {}
#[allow(unused_variables)]
fn destroy(&mut self) {}
#[allow(unused_variables)]
fn idle(&mut self, token: IdleToken) {}
fn as_any(&mut self) -> &mut dyn Any;
}
impl From<backend::WindowHandle> for WindowHandle {
fn from(src: backend::WindowHandle) -> WindowHandle {
WindowHandle(src)
}
}
#[cfg(test)]
mod test {
use super::*;
use static_assertions as sa;
sa::assert_not_impl_any!(WindowHandle: Send, Sync);
sa::assert_impl_all!(IdleHandle: Send, Sync);
}