use bevy_ecs::prelude::*;
use std::sync::{Arc, Mutex};
use windows::Win32::Foundation::HWND;
use super::DragConstraint;
use crate::ecs::Point;
#[derive(Debug, Clone, Default)]
pub struct WindowDragContext {
pub hwnd: Option<HWND>,
pub initial_window_pos: Option<Point>,
pub move_window: bool,
pub constraint: Option<DragConstraint>,
}
unsafe impl Send for WindowDragContext {}
unsafe impl Sync for WindowDragContext {}
impl WindowDragContext {
pub fn clear(&mut self) {
self.hwnd = None;
self.initial_window_pos = None;
self.move_window = false;
self.constraint = None;
}
}
#[derive(Resource, Clone)]
pub struct WindowDragContextResource {
inner: Arc<Mutex<WindowDragContext>>,
}
impl WindowDragContextResource {
pub fn new() -> Self {
Self {
inner: Arc::new(Mutex::new(WindowDragContext::default())),
}
}
pub fn set(&self, context: WindowDragContext) {
if let Ok(mut inner) = self.inner.lock() {
*inner = context;
}
}
pub fn get(&self) -> Option<WindowDragContext> {
self.inner.lock().ok().map(|inner| inner.clone())
}
pub fn clear(&self) {
if let Ok(mut inner) = self.inner.lock() {
inner.clear();
}
}
}
impl Default for WindowDragContextResource {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_is_unset() {
let ctx = WindowDragContext::default();
assert!(ctx.hwnd.is_none());
assert!(ctx.initial_window_pos.is_none());
assert!(!ctx.move_window);
assert!(ctx.constraint.is_none());
}
#[test]
fn test_clear_resets_all_fields() {
let mut ctx = WindowDragContext {
hwnd: Some(HWND::default()),
initial_window_pos: Some(Point { x: 10, y: 20 }),
move_window: true,
constraint: Some(DragConstraint {
min_x: Some(0),
max_x: Some(100),
min_y: Some(0),
max_y: Some(100),
}),
};
ctx.clear();
assert!(ctx.hwnd.is_none());
assert!(ctx.initial_window_pos.is_none());
assert!(!ctx.move_window);
assert!(ctx.constraint.is_none());
}
#[test]
fn test_resource_set_then_get_roundtrip() {
let res = WindowDragContextResource::new();
res.set(WindowDragContext {
hwnd: None,
initial_window_pos: Some(Point { x: 300, y: 400 }),
move_window: true,
constraint: None,
});
let got = res.get().expect("get は Some を返すべき");
assert_eq!(got.initial_window_pos, Some(Point { x: 300, y: 400 }));
assert!(got.move_window);
}
#[test]
fn test_resource_new_returns_default() {
let res = WindowDragContextResource::new();
let got = res.get().expect("get は Some を返すべき");
assert!(got.hwnd.is_none());
assert!(!got.move_window);
assert!(got.initial_window_pos.is_none());
}
#[test]
fn test_resource_clear_resets() {
let res = WindowDragContextResource::new();
res.set(WindowDragContext {
hwnd: Some(HWND::default()),
initial_window_pos: Some(Point { x: 1, y: 2 }),
move_window: true,
constraint: None,
});
res.clear();
let got = res.get().expect("get は Some を返すべき");
assert!(got.initial_window_pos.is_none());
assert!(!got.move_window);
}
#[test]
fn test_resource_shares_state_across_clone() {
let res = WindowDragContextResource::new();
let clone = res.clone();
clone.set(WindowDragContext {
hwnd: None,
initial_window_pos: Some(Point { x: 7, y: 9 }),
move_window: true,
constraint: None,
});
let got = res.get().expect("get は Some を返すべき");
assert_eq!(got.initial_window_pos, Some(Point { x: 7, y: 9 }));
assert!(got.move_window);
}
}