use crate::access_winit_window::AccessWinitWindow;
use aeth_event::{Pub, Sub, new_pubsub};
use indexed_bitmap::IndexedBitmap;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
use winit::event::WindowEvent;
use winit::window::{Window as WinitWindow, WindowId};
pub(crate) struct WindowInner {
window: Box<dyn WinitWindow>,
window_event_pub: Pub<WindowEvent>,
win_id: WindowId,
slot_id: Rc<RefCell<usize>>,
}
impl WindowInner {
pub(crate) fn window_event_pub(&self) -> Pub<WindowEvent> {
self.window_event_pub.clone()
}
}
pub(crate) struct Windows {
windows: Vec<WindowInner>,
lookup: HashMap<WindowId, Rc<RefCell<usize>>>,
refreshing: IndexedBitmap,
}
impl Windows {
pub(crate) fn new() -> Self {
Self {
windows: Vec::new(),
lookup: HashMap::new(),
refreshing: IndexedBitmap::new(),
}
}
fn unlink(&mut self, id: usize) {
let evicted_id = self.windows[id].win_id;
self.lookup.remove(&evicted_id);
self.refreshing.bitset(id, false);
}
pub(crate) fn evict(&mut self, id: usize) {
assert!(self.windows.len() > 0);
self.unlink(id);
let last_id = self.windows.len() - 1;
if id < last_id {
let win_id = self.windows[last_id].win_id;
let slot_id = self.windows[last_id].slot_id.clone();
let last_refreshing = self.refreshing.bitget(last_id);
self.unlink(last_id);
self.windows.swap(id, last_id);
*slot_id.borrow_mut() = id;
self.refreshing.bitset(id, last_refreshing);
self.lookup.insert(win_id, slot_id);
}
self.windows.pop();
if self.windows.len() * 2 < self.windows.capacity() {
self.windows.shrink_to_fit();
self.refreshing.shrink_to(self.windows.capacity());
}
}
fn set_refreshing(&mut self, id: usize, enable: bool) {
self.refreshing.bitset(id, enable);
}
fn broadcast_refresh_option(&self) -> Option<()> {
let mut id = self.refreshing.lowest_one()?;
loop {
self.windows[id].window.request_redraw();
id = self.refreshing.next_one(id)?;
}
}
pub(crate) fn broadcast_refresh(&self) {
self.broadcast_refresh_option();
}
pub(crate) fn allocate(rc: &Rc<RefCell<Self>>, window: Box<dyn WinitWindow>) -> Window {
let mut this = rc.borrow_mut();
let win_id = window.id();
let slot_id = this.windows.len();
let slot_id = Rc::new(RefCell::new(slot_id));
let (window_event_pub, window_event_sub) = new_pubsub();
this.windows.push(WindowInner {
window,
window_event_pub,
win_id,
slot_id: slot_id.clone(),
});
this.lookup.insert(win_id, slot_id.clone());
Window {
windows: Rc::downgrade(rc),
window_event_sub,
slot_id: slot_id,
}
}
pub(crate) fn find_inner_by_window_id(
&mut self,
window_id: WindowId,
) -> Option<&mut WindowInner> {
let slot_id = *self.lookup.get(&window_id)?.borrow();
Some(&mut self.windows[slot_id])
}
}
pub struct Window {
windows: Weak<RefCell<Windows>>,
window_event_sub: Sub<WindowEvent>,
slot_id: Rc<RefCell<usize>>,
}
impl Drop for Window {
fn drop(&mut self) {
self.drop_option();
}
}
impl Window {
fn id(&self) -> usize {
*self.slot_id.borrow()
}
fn drop_option(&mut self) -> Option<()> {
let rc = self.windows.upgrade()?;
rc.borrow_mut().evict(self.id());
Some(())
}
fn must_upgrade_windows(&self) -> Rc<RefCell<Windows>> {
self.windows
.upgrade()
.expect("Windows dropped, maybe not inside valid window subsystem context")
}
pub fn set_refreshing(&mut self, enable: bool) {
let rc = self.must_upgrade_windows();
let mut windows = rc.borrow_mut();
windows.set_refreshing(self.id(), enable);
}
pub fn is_refreshing(&self) -> bool {
let rc = self.must_upgrade_windows();
let windows = rc.borrow_mut();
windows.refreshing.bitget(self.id())
}
pub fn window_event_sub(&self) -> Sub<WindowEvent> {
self.window_event_sub.clone()
}
}
impl AccessWinitWindow for Window {
fn map_winit_window<F, T>(&self, f: F) -> T
where
F: FnOnce(&Box<dyn WinitWindow>) -> T,
{
let rc = self.must_upgrade_windows();
let windows = rc.borrow();
let window = &windows.windows[self.id()].window;
f(window)
}
fn map_winit_window_mut<F, T>(&mut self, f: F) -> T
where
F: FnOnce(&mut Box<dyn WinitWindow>) -> T,
{
let rc = self.must_upgrade_windows();
let mut windows = rc.borrow_mut();
let window = &mut windows.windows[self.id()].window;
f(window)
}
}