use crate::{ AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, Bounds, DispatchEventResult, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, Size, TestPlatform, TileId, WindowAppearance, WindowBackgroundAppearance, WindowBounds, WindowParams, }; use collections::HashMap; use parking_lot::Mutex; use raw_window_handle::{HasDisplayHandle, HasWindowHandle}; use std::{ rc::{Rc, Weak}, sync::{self, Arc}, }; pub(crate) struct TestWindowState { pub(crate) bounds: Bounds, pub(crate) handle: AnyWindowHandle, display: Rc, pub(crate) title: Option, pub(crate) edited: bool, platform: Weak, sprite_atlas: Arc, pub(crate) should_close_handler: Option bool>>, input_callback: Option DispatchEventResult>>, active_status_change_callback: Option>, hover_status_change_callback: Option>, resize_callback: Option, f32)>>, moved_callback: Option>, input_handler: Option, is_fullscreen: bool, } #[derive(Clone)] pub(crate) struct TestWindow(pub(crate) Arc>); impl HasWindowHandle for TestWindow { fn window_handle( &self, ) -> Result, raw_window_handle::HandleError> { unimplemented!("Test Windows are not backed by a real platform window") } } impl HasDisplayHandle for TestWindow { fn display_handle( &self, ) -> Result, raw_window_handle::HandleError> { unimplemented!("Test Windows are not backed by a real platform window") } } impl TestWindow { pub fn new( handle: AnyWindowHandle, params: WindowParams, platform: Weak, display: Rc, ) -> Self { Self(Arc::new(Mutex::new(TestWindowState { bounds: params.bounds, display, platform, handle, sprite_atlas: Arc::new(TestAtlas::new()), title: Default::default(), edited: false, should_close_handler: None, input_callback: None, active_status_change_callback: None, hover_status_change_callback: None, resize_callback: None, moved_callback: None, input_handler: None, is_fullscreen: false, }))) } pub fn simulate_resize(&mut self, size: Size) { let scale_factor = self.scale_factor(); let mut lock = self.0.lock(); let Some(mut callback) = lock.resize_callback.take() else { return; }; lock.bounds.size = size; drop(lock); callback(size, scale_factor); self.0.lock().resize_callback = Some(callback); } pub(crate) fn simulate_active_status_change(&self, active: bool) { let mut lock = self.0.lock(); let Some(mut callback) = lock.active_status_change_callback.take() else { return; }; drop(lock); callback(active); self.0.lock().active_status_change_callback = Some(callback); } pub fn simulate_input(&mut self, event: PlatformInput) -> bool { let mut lock = self.0.lock(); let Some(mut callback) = lock.input_callback.take() else { return false; }; drop(lock); let result = callback(event); self.0.lock().input_callback = Some(callback); !result.propagate } } impl PlatformWindow for TestWindow { fn bounds(&self) -> Bounds { self.0.lock().bounds } fn window_bounds(&self) -> WindowBounds { WindowBounds::Windowed(self.bounds()) } fn is_maximized(&self) -> bool { false } fn content_size(&self) -> Size { self.bounds().size } fn scale_factor(&self) -> f32 { 2.0 } fn appearance(&self) -> WindowAppearance { WindowAppearance::Light } fn display(&self) -> Option> { Some(self.0.lock().display.clone()) } fn mouse_position(&self) -> Point { Point::default() } fn modifiers(&self) -> crate::Modifiers { crate::Modifiers::default() } fn set_input_handler(&mut self, input_handler: PlatformInputHandler) { self.0.lock().input_handler = Some(input_handler); } fn take_input_handler(&mut self) -> Option { self.0.lock().input_handler.take() } fn prompt( &self, _level: crate::PromptLevel, msg: &str, detail: Option<&str>, _answers: &[&str], ) -> Option> { Some( self.0 .lock() .platform .upgrade() .expect("platform dropped") .prompt(msg, detail), ) } fn activate(&self) { self.0 .lock() .platform .upgrade() .unwrap() .set_active_window(Some(self.clone())) } fn is_active(&self) -> bool { false } fn is_hovered(&self) -> bool { false } fn set_title(&mut self, title: &str) { self.0.lock().title = Some(title.to_owned()); } fn set_app_id(&mut self, _app_id: &str) {} fn set_background_appearance(&self, _background: WindowBackgroundAppearance) {} fn set_edited(&mut self, edited: bool) { self.0.lock().edited = edited; } fn show_character_palette(&self) { unimplemented!() } fn minimize(&self) { unimplemented!() } fn zoom(&self) { unimplemented!() } fn toggle_fullscreen(&self) { let mut lock = self.0.lock(); lock.is_fullscreen = !lock.is_fullscreen; } fn is_fullscreen(&self) -> bool { self.0.lock().is_fullscreen } fn on_request_frame(&self, _callback: Box) {} fn on_input(&self, callback: Box DispatchEventResult>) { self.0.lock().input_callback = Some(callback) } fn on_active_status_change(&self, callback: Box) { self.0.lock().active_status_change_callback = Some(callback) } fn on_hover_status_change(&self, callback: Box) { self.0.lock().hover_status_change_callback = Some(callback) } fn on_resize(&self, callback: Box, f32)>) { self.0.lock().resize_callback = Some(callback) } fn on_moved(&self, callback: Box) { self.0.lock().moved_callback = Some(callback) } fn on_should_close(&self, callback: Box bool>) { self.0.lock().should_close_handler = Some(callback); } fn on_close(&self, _callback: Box) {} fn on_appearance_changed(&self, _callback: Box) {} fn draw(&self, _scene: &crate::Scene) {} fn sprite_atlas(&self) -> sync::Arc { self.0.lock().sprite_atlas.clone() } fn as_test(&mut self) -> Option<&mut TestWindow> { Some(self) } #[cfg(target_os = "windows")] fn get_raw_handle(&self) -> windows::Win32::Foundation::HWND { unimplemented!() } fn show_window_menu(&self, _position: Point) { unimplemented!() } fn start_window_move(&self) { unimplemented!() } } pub(crate) struct TestAtlasState { next_id: u32, tiles: HashMap, } pub(crate) struct TestAtlas(Mutex); impl TestAtlas { pub fn new() -> Self { TestAtlas(Mutex::new(TestAtlasState { next_id: 0, tiles: HashMap::default(), })) } } impl PlatformAtlas for TestAtlas { fn get_or_insert_with<'a>( &self, key: &crate::AtlasKey, build: &mut dyn FnMut() -> anyhow::Result< Option<(Size, std::borrow::Cow<'a, [u8]>)>, >, ) -> anyhow::Result> { let mut state = self.0.lock(); if let Some(tile) = state.tiles.get(key) { return Ok(Some(tile.clone())); } drop(state); let Some((size, _)) = build()? else { return Ok(None); }; let mut state = self.0.lock(); state.next_id += 1; let texture_id = state.next_id; state.next_id += 1; let tile_id = state.next_id; state.tiles.insert( key.clone(), crate::AtlasTile { texture_id: AtlasTextureId { index: texture_id, kind: crate::AtlasTextureKind::Path, }, tile_id: TileId(tile_id), padding: 0, bounds: crate::Bounds { origin: Point::default(), size, }, }, ); Ok(Some(state.tiles[key].clone())) } }