use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
use crossterm::event::Event;
use singlevec::SingleVec;
use crate::canvas::*;
use crate::common::*;
use crate::framebuffer::Framebuffer;
use crate::window::*;
#[derive(Clone)]
struct WindowStructure {
rect: Rect,
has_border: bool,
depth: u8,
border: Thickness,
render_inner: bool,
weak: WindowWeakRef,
parent: Option<WindowUID>,
children: SingleVec<WindowUID>,
}
impl WindowStructure {
fn new(
ctx: &WindowRef,
rect: Rect,
has_border: bool,
border: Thickness,
depth: u8,
parent: Option<WindowUID>,
) -> Self {
Self {
rect,
has_border,
border,
depth,
render_inner: false,
weak: Rc::downgrade(ctx),
children: SingleVec::new(),
parent,
}
}
fn subtract_border(&self, rect: &mut Rect) {
rect.start.x += self.border.left;
rect.size.x -= self.border.width();
rect.start.y += self.border.top;
rect.size.y -= self.border.height();
}
fn inner_rect(&self) -> Rect {
let mut rect = self.rect;
if self.has_border {
self.subtract_border(&mut rect);
}
rect
}
}
struct Focus {
win: WindowRef,
uid: WindowUID,
}
impl PartialEq for Focus {
fn eq(&self, other: &Self) -> bool {
self.uid == other.uid
}
}
struct SizeCache {
size: TPoint,
map: RefCell<HashMap<WindowUID, TPoint>>,
}
impl SizeCache {
fn new(size: TPoint) -> Self {
Self {
size,
map: RefCell::new(HashMap::with_capacity(128)),
}
}
fn resize(&mut self, size: TPoint) {
self.size = size;
}
fn compare_n_update(&self, uid: WindowUID, size: TPoint) -> bool {
let mut brw = self.map.borrow_mut();
let old = brw
.insert(uid, size)
.unwrap_or(Vector2D::new(TSize::MIN, TSize::MIN));
old != size
}
}
pub struct WindowHandler {
structure: HashMap<WindowUID, WindowStructure>,
main_window: Rc<RefCell<dyn Window>>,
focus: Focus,
size_cache: SizeCache,
queue: WindowEventQueue,
}
impl WindowHandler {
pub(crate) fn new(main_window: WindowRef, size: TPoint) -> Self {
let focus = Self::find_focus(&main_window);
focus
.win
.borrow_mut()
.handle_event(&mut WindowEvent::new(Event::FocusGained));
let queue = WindowEventQueue::initialize();
let mut window_handler = Self {
main_window,
focus,
structure: HashMap::with_capacity(128),
size_cache: SizeCache::new(size),
queue,
};
window_handler.broadcast_resize(size);
window_handler
}
pub(crate) fn handle_event(&mut self, event: Event) {
let mut win_event = WindowEvent::new(event);
self.handle_event_inner(self.focus.uid, &mut win_event);
}
pub(crate) fn broadcast_resize(&mut self, size: TPoint) {
self.size_cache.resize(size);
let uid = Self::determine_window_structure(
&mut self.structure,
Rect::from(size.x, size.y),
&self.main_window,
u8::MIN,
None,
);
self.broadcast_resize_rec(uid);
self.structure
.retain(|_, s| s.weak.strong_count() > usize::MIN);
}
pub(crate) fn render(&mut self, framebuffer: &mut Framebuffer) {
self.handle_events();
let uid = self.main_window.borrow().uid();
self.render_window_rec(&mut Canvas::from_framebuffer(framebuffer), uid);
}
fn broadcast_resize_rec(&self, win: WindowUID) {
let struc = self.structure.get(&win).unwrap();
let rect = struc.inner_rect();
if self.size_cache.compare_n_update(win, rect.size) {
struc
.weak
.upgrade()
.unwrap()
.borrow_mut()
.handle_event(&mut WindowEvent::new(Event::Resize(
rect.size.x as TSize,
rect.size.y as TSize,
)));
}
for w in struc.children.iter() {
self.broadcast_resize_rec(*w);
}
}
fn get_parent_rect(&self, ws: &WindowStructure) -> Rect {
if let Some(uid) = ws.parent {
if let Some(parent) = self.structure.get(&uid) {
let inner = parent.inner_rect();
return Rect::from(inner.size.x, inner.size.y);
}
}
Rect::from(self.size_cache.size.x, self.size_cache.size.y)
}
pub(crate) fn handle_events(&mut self) {
let mut window: Option<TreeUpdate> = None;
let mut focus: Option<FocusUpdate> = None;
for e in self.queue.recv.try_iter() {
match e.property {
WindowProperty::Focus => {
if let Some(w) = self.structure.get(&e.uid) {
match focus.as_ref() {
Some(current) => {
if w.depth < current.depth {
focus = w.weak.upgrade().map(|v| FocusUpdate {
win: v,
depth: w.depth,
});
}
}
None => {
focus = w.weak.upgrade().map(|v| FocusUpdate {
win: v,
depth: w.depth,
})
}
}
}
}
WindowProperty::Children => {
if let Some(w) = self.structure.get(&e.uid) {
match window.as_ref() {
Some(current) => {
if w.depth < current.depth {
window = w.weak.upgrade().map(|v| TreeUpdate {
win: v,
depth: w.depth,
rect: w.rect,
});
}
}
None => {
window = w.weak.upgrade().map(|v| TreeUpdate {
win: v,
depth: w.depth,
rect: w.rect,
})
}
}
}
}
_ => {
if let Some(w) = self.structure.get(&e.uid) {
if let Some(p) = w.parent {
match self.structure.get(&p) {
Some(parent) => match window.as_ref() {
Some(current) => {
if parent.depth < current.depth {
window = parent.weak.upgrade().map(|v| TreeUpdate {
win: v,
depth: parent.depth,
rect: self.get_parent_rect(parent),
});
}
}
None => {
window = parent.weak.upgrade().map(|v| TreeUpdate {
win: v,
depth: parent.depth,
rect: self.get_parent_rect(parent),
});
}
},
None => {
window = Some(TreeUpdate {
win: self.main_window.clone(),
depth: u8::MIN,
rect: self.get_parent_rect(w),
})
}
}
}
}
}
}
}
if let Some(update) = window.as_ref() {
let parent = {
match self.structure.get(&update.win.borrow().uid()) {
Some(ws) => ws.parent,
None => None,
}
};
Self::determine_window_structure(
&mut self.structure,
update.rect,
&update.win,
update.depth,
parent,
);
}
if let Some(update) = focus.as_ref() {
let focus = Self::find_focus(&update.win);
if self.focus != focus && self.structure.contains_key(&focus.uid) {
self.focus
.win
.borrow_mut()
.handle_event(&mut WindowEvent::new(Event::FocusLost));
self.focus = focus;
self.focus
.win
.borrow_mut()
.handle_event(&mut WindowEvent::new(Event::FocusGained));
}
}
if window.is_some() {
self.structure
.retain(|_, s| s.weak.strong_count() > usize::MIN);
let uid = self.main_window.borrow().uid();
self.broadcast_resize_rec(uid);
}
}
fn determine_window_structure(
map: &mut HashMap<WindowUID, WindowStructure>,
base_rect: Rect,
window: &WindowRef,
depth: u8,
parent: Option<WindowUID>,
) -> WindowUID {
let mut rect = base_rect;
let has_border = Self::determine_window_rect(&mut rect, &mut *window.borrow_mut());
let mut ws = WindowStructure::new(
window,
rect,
has_border,
window.borrow().border().thickness(),
depth,
parent,
);
let uid = window.borrow().uid();
let inner_rect = ws.inner_rect();
if inner_rect.area() > TSize::MIN {
ws.render_inner = true;
let child_rect = Rect::from(inner_rect.size.x, inner_rect.size.y);
let sub_wins = window
.borrow_mut()
.children(SubWindowBuilder::from(child_rect));
for (sub, child_rect) in sub_wins
.children()
.iter()
.zip(sub_wins.rects())
.filter(|(sub, _)| sub.borrow().is_visible())
{
let ch_uid =
Self::determine_window_structure(map, *child_rect, sub, depth + 1, Some(uid));
ws.children.push(ch_uid);
}
}
map.insert(uid, ws);
uid
}
fn determine_window_rect(base_rect: &mut Rect, window: &mut dyn Window) -> bool {
let (h_align, v_align) = window.alignment();
let margin = window.margin();
let tn = window.border().thickness();
let (w, h) = (tn.width(), tn.height());
window.desired_size_pre_hook(Vector2D::new(base_rect.size.x, base_rect.size.y));
let mut size = window.desired_size(Vector2D::new(base_rect.size.x, base_rect.size.y));
let has_border = window.border() != BorderStyle::None;
if size.x > base_rect.size.x || size.y > base_rect.size.y {
size = Vector2D::default();
}
if size.x * size.y > 0 {
if size.x <= w && size.x + w <= base_rect.size.x {
size.x += w;
}
if size.y <= h && size.y + h <= base_rect.size.y {
size.y += h;
}
}
Self::handle_margin(
base_rect,
margin.left,
margin.top,
margin.right,
margin.bottom,
);
Self::handle_alignment(base_rect, 0, h_align.into(), size.x);
Self::handle_alignment(base_rect, 1, v_align.into(), size.y);
has_border && base_rect.size.x > w && base_rect.size.y > h
}
fn find_focus(window: &WindowRef) -> Focus {
let win_brw = window.borrow();
match win_brw.focus() {
Some(sub) => Self::find_focus(&sub),
None => Focus {
win: window.clone(),
uid: win_brw.uid(),
},
}
}
fn handle_event_inner(&self, win: WindowUID, event: &mut WindowEvent) {
if let Some(ws) = self.structure.get(&win) {
if let Some(window) = ws.weak.upgrade() {
if window.borrow().is_enabled() {
window.borrow_mut().handle_event(event);
}
if !event.handled {
if let Some(parent) = ws.parent {
self.handle_event_inner(parent, event);
}
}
}
}
}
fn render_window_rec(&self, canvas: &mut Canvas, win: WindowUID) {
let ws = self.structure.get(&win).unwrap();
if let Some(ctx) = ws.weak.upgrade() {
let brw = ctx.borrow();
let mut rect = ws.rect;
if ws.has_border {
Self::render_border(&*brw, &mut Canvas::from_existing(canvas, rect).unwrap())
.unwrap();
ws.subtract_border(&mut rect);
}
if ws.render_inner {
let mut inner_canvas = Canvas::from_existing(canvas, rect).unwrap();
brw.render(&mut inner_canvas);
for sub in ws.children.iter() {
self.render_window_rec(&mut Canvas::from_existing(canvas, rect).unwrap(), *sub);
}
}
}
}
fn handle_margin(rect: &mut Rect, left: TSize, top: TSize, right: TSize, bottom: TSize) {
if rect.size.x > left {
rect.start.x += left;
rect.size.x -= left;
}
if rect.size.x > right {
rect.size.x -= right;
}
if rect.size.y > top {
rect.start.y += top;
rect.size.y -= top;
}
if rect.size.y > bottom {
rect.size.y -= bottom;
}
}
fn handle_alignment(
rect: &mut Rect,
rect_index: usize,
alignment: Alignment,
base_size: TSize,
) {
if let Some(size) = rect.size[rect_index].checked_sub(base_size) {
match alignment {
Alignment::LowerBound => {
rect.size[rect_index] -= size;
}
Alignment::Center => {
let rem = size % 2;
if size + rem <= rect.size[rect_index] {
rect.start[rect_index] += size / 2 + rem;
rect.size[rect_index] -= size;
}
}
Alignment::HigherBound => {
rect.start[rect_index] += size;
}
}
}
}
fn render_border(window: &dyn Window, canvas: &mut Canvas) -> Result<(), GraphemeError> {
let border = window.border();
let tn = border.thickness();
let mut bcanvas = BorderCanvas::from(canvas, tn);
match border {
BorderStyle::Custom(f, _) => f(window, bcanvas),
BorderStyle::Preset { kind, bg, fg } => bcanvas.draw_preset_border(kind, bg, fg, "")?,
BorderStyle::None => {}
}
Ok(())
}
}