tuifw-window 0.24.0

Text User Interface Framework. Overlappable hierachical windows.
Documentation
#![feature(effects)]
#![feature(never_type)]
#![feature(ptr_metadata)]

#![deny(warnings)]
#![doc(test(attr(deny(warnings))))]
#![doc(test(attr(allow(dead_code))))]
#![doc(test(attr(allow(unused_variables))))]
#![allow(clippy::assertions_on_constants)]
#![allow(clippy::blocks_in_if_conditions)]
#![allow(clippy::collapsible_else_if)]
#![allow(clippy::collapsible_if)]
#![allow(clippy::non_canonical_partial_ord_impl)]
#![allow(clippy::option_map_unit_fn)]
#![allow(clippy::partialeq_to_none)]
#![allow(clippy::type_complexity)]

#![no_std]

extern crate alloc;

use alloc::borrow::Cow;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
use components_arena::{Arena, Component, Id, NewtypeComponentId};
use core::cmp::{max, min};
use core::mem::replace;
use core::ops::RangeInclusive;
use core::ptr::{DynMetadata, Pointee};
use downcast_rs::{Downcast, impl_downcast};
use dyn_clone::{DynClone, clone_trait_object};
use dynamic_cast::{SupportsInterfaces, dyn_cast_box};
use either::{Either, Left, Right};
use iter_identify_first_last::IteratorIdentifyFirstLastExt;
use macro_attr_2018::macro_attr;
use timer_no_std::{MonoClock, MonoTime};
use tuifw_screen_base::{Bg, Error, Fg, Key, Point, Rect, Screen, Vector};
use tuifw_screen_base::Event as screen_Event;
use tuifw_screen_base::{HAlign, VAlign, Thickness, Range1d, text_width};

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Event {
    Key(Key),
    PreviewKey(Key),
    PreProcessKey(Key),
    PostProcessKey(Key),
    Cmd(u16),
    PreviewCmd(u16),
}

impl Event {
    pub fn is_preview(self) -> bool {
        match self {
            Event::Key(_) => false,
            Event::PreviewKey(_) => true,
            Event::Cmd(_) => false,
            Event::PreviewCmd(_) => true,
            Event::PreProcessKey(_) => false,
            Event::PostProcessKey(_) => false,
        }
    }

    fn preview(self) -> Self {
        match self {
            Event::Key(k) => Event::PreviewKey(k),
            Event::Cmd(n) => Event::PreviewCmd(n),
            _ => unreachable!(),
        }
    }
}

pub const CMD_GOT_PRIMARY_FOCUS: u16 = 0;

pub const CMD_LOST_PRIMARY_FOCUS: u16 = 1;

pub const CMD_GOT_SECONDARY_FOCUS: u16 = 2;

pub const CMD_LOST_SECONDARY_FOCUS: u16 = 3;

pub const CMD_LOST_ATTENTION: u16 = 4;

fn invalidate_rect(screen: &mut dyn Screen, rect: Rect) {
    let rect = rect.intersect(Rect { tl: Point { x: 0, y: 0 }, size: screen.size() });
    if rect.is_empty() { return; }
    let l = rect.l();
    let r = rect.r();
    for y in rect.t() .. rect.b() {
        let row = screen.line_invalidated_range_mut(y);
        row.start = min(row.start, l);
        row.end = max(row.end, r);
    }
}

fn rect_invalidated(screen: &dyn Screen, rect: Rect) -> bool {
    let rect = rect.intersect(Rect { tl: Point { x: 0, y: 0 }, size: screen.size() });
    if rect.is_empty() { return false; }
    let l = rect.l();
    let r = rect.r();
    for y in rect.t() .. rect.b() {
        let row = screen.line_invalidated_range(y);
        if row.end == row.start { continue; }
        if l < row.start {
            if r > row.end { return true; }
        } else if l < row.end {
            return true;
        }
    }
    false
}

pub struct RenderPort {
    screen: Box<dyn Screen>,
    offset: Vector,
    bounds: Rect,
    cursor: Option<Point>,
}

impl RenderPort {
    pub fn text(&mut self, p: Point, color: (Fg, Bg), text: &str) {
        let screen_size = self.screen.size();
        let p = p.offset(self.offset);
        if !self.bounds.v_range().contains(p.y) || self.bounds.size.x == 0 { return; }
        if p.y < 0 || p.y >= screen_size.y { return; }
        let row = self.screen.line_invalidated_range(p.y).clone();
        if p.x >= row.end { return; }

        let window_start = self.bounds.l();
        let window_end = self.bounds.r();
        let chunks = if window_start <= window_end {
            if window_end <= 0 || window_start >= screen_size.x { return; }
            [max(0, window_start) .. min(screen_size.x, window_end), 0 .. 0]
        } else {
            if window_end > 0 && window_start < screen_size.x {
                [0 .. window_end, window_start .. screen_size.x]
            } else if window_end > 0 {
                [0 .. window_end, 0 .. 0]
            } else if window_start < screen_size.x {
                [window_start .. screen_size.x, 0 .. 0]
            } else {
                return
            }
        };

        for chunk in &chunks {
            if chunk.start >= chunk.end { continue; }
            let out = self.screen.out(p, color.0, color.1, text, chunk.clone(), row.clone());
            if out.start >= out.end { continue; }
            let row = self.screen.line_invalidated_range_mut(p.y);
            row.start = min(row.start, out.start);
            row.end = max(row.end, out.end);
            if let Some(cursor) = self.cursor {
                if cursor.y == p.y && cursor.x >= out.start && cursor.x < out.end {
                    self.cursor = None;
                }
            }
        }
    }

    pub fn cursor(&mut self, p: Point) {
        if self.cursor.is_some() { return; }
        let p = p.offset(self.offset);
        if p.y < 0 || p.y >= self.screen.size().y { return; }
        let row = &self.screen.line_invalidated_range(p.y);
        if p.x < row.start || p.x >= row.end { return; }
        self.cursor = Some(p);
    }

    pub fn fill(&mut self, mut f: impl FnMut(&mut Self, Point)) {
        for y in 0 .. self.screen.size().y {
            for x in self.screen.line_invalidated_range(y).clone() {
                f(self, Point { x, y }.offset(-self.offset));
            }
        }
    }

    pub fn label(&mut self, mut p: Point, color: (Fg, Bg), color_hotkey: (Fg, Bg), text: &str) {
        let mut hotkey = false;
        for (first, last, text) in text.split('~').identify_first_last() {
            if !first && !text.is_empty() {
                hotkey = !hotkey;
            }
            let actual_text = if !first && !last && text.is_empty() { "~" } else { text };
            self.text(p, if hotkey { color_hotkey } else { color }, actual_text);
            p = p.offset(Vector { x: text_width(actual_text), y: 0 });
            if !first && text.is_empty() {
                hotkey = !hotkey;
            }
        }
    }

    pub fn fill_bg(&mut self, bg: Bg) {
        self.fill(|rp, p| rp.text(p, (Fg::LightGray, bg), " "));
    }

    pub fn h_line(&mut self, start: Point, len: i16, double: bool, color: (Fg, Bg)) {
        let s = if double { "" } else { "" };
        for x in Range1d::new(start.x, start.x.wrapping_add(len)) {
            self.text(Point { x, y: start.y }, color, s);
        }
    }

    pub fn v_line(&mut self, start: Point, len: i16, double: bool, color: (Fg, Bg)) {
        let s = if double { "" } else { "" };
        for y in Range1d::new(start.y, start.y.wrapping_add(len)) {
            self.text(Point { x: start.x, y }, color, s);
        }
    }

    pub fn tl_edge(&mut self, p: Point, double: bool, color: (Fg, Bg)) {
        self.text(p, color, if double { "" } else { "" });
    }

    pub fn tr_edge(&mut self, p: Point, double: bool, color: (Fg, Bg)) {
        self.text(p, color, if double { "" } else { "" });
    }

    pub fn bl_edge(&mut self, p: Point, double: bool, color: (Fg, Bg)) {
        self.text(p, color, if double { "" } else { "" });
    }

    pub fn br_edge(&mut self, p: Point, double: bool, color: (Fg, Bg)) {
        self.text(p, color, if double { "" } else { "" });
    }
}

pub fn label_width(text: &str) -> i16 {
    let mut width = 0i16;
    let mut hotkey = false;
    for (first, last, text) in text.split('~').identify_first_last() {
        if !first && !text.is_empty() {
            hotkey = !hotkey;
        }
        let actual_text = if !first && !last && text.is_empty() { "~" } else { text };
        width = width.wrapping_add(text_width(actual_text));
        if !first && text.is_empty() {
            hotkey = !hotkey;
        }
    }
    width
}

pub fn label(text: &str) -> Option<char> {
    let mut hotkey = false;
    for (first, last, text) in text.split('~').identify_first_last() {
        if !first && !text.is_empty() {
            hotkey = !hotkey;
        }
        let actual_text = if !first && !last && text.is_empty() { "~" } else { text };
        if hotkey && !actual_text.is_empty() {
            return Some(actual_text.chars().next().unwrap().to_lowercase().next().unwrap());
        }
        if !first && text.is_empty() {
            hotkey = !hotkey;
        }
    }
    None
}

pub trait Data: Downcast + DynClone { }

impl_downcast!(Data);

clone_trait_object!(Data);

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum Visibility {
    Visible,
    Hidden,
    Collapsed
}

pub trait Widget: DynClone + SupportsInterfaces {
    #[allow(clippy::wrong_self_convention)]
    #[allow(clippy::new_ret_no_self)]
    fn new(&self) -> Box<dyn WidgetData>;

    fn clone_data(
        &self,
        tree: &mut WindowTree,
        source: Window,
        dest: Window,
        clone_window: Box<dyn Fn(&WindowTree, Window) -> Window>,
    );

    fn render(
        &self,
        tree: &WindowTree,
        window: Window,
        rp: &mut RenderPort,
        app: &mut dyn App,
    );

    fn measure(
        &self,
        tree: &mut WindowTree,
        window: Window,
        available_width: Option<i16>,
        available_height: Option<i16>,
        app: &mut dyn App,
    ) -> Vector;

    fn arrange(
        &self,
        tree: &mut WindowTree,
        window: Window,
        final_inner_bounds: Rect,
        app: &mut dyn App,
    ) -> Vector;

    fn update(
        &self,
        tree: &mut WindowTree,
        window: Window,
        event: Event,
        event_source: Window,
        app: &mut dyn App,
    ) -> bool;

    fn secondary_focusable(&self) -> bool { false }

    fn pre_process(&self) -> bool { false }

    fn post_process(&self) -> bool { false }

    fn bring_into_view(&self, _tree: &mut WindowTree, _window: Window, _rect: Rect) -> bool {
        false
    }
}

clone_trait_object!(Widget);

pub trait WidgetData: Downcast {
    fn drop_widget_data(&mut self, _tree: &mut WindowTree, _app: &mut dyn App) { }
}

impl_downcast!(WidgetData);

pub trait Layout: Downcast + DynClone { }

impl_downcast!(Layout);

clone_trait_object!(Layout);

pub trait App: Downcast { }

impl_downcast!(App);

#[derive(Clone)]
pub struct Palette(Vec<Either<u8, (Fg, Bg)>>);

impl Palette {
    pub fn new() -> Palette {
        Palette(Vec::new())
    }

    pub fn get(&self, i: u8) -> Either<u8, (Fg, Bg)> {
        self.0.get(usize::from(i)).cloned().unwrap_or(Left(i))
    }

    pub fn set(&mut self, i: u8, o: Either<u8, (Fg, Bg)>) {
        for k in self.0.len() ..= usize::from(i) {
            self.0.push(Left(u8::try_from(k).unwrap()));
        }
        self.0[usize::from(i)] = o;
    }
}

impl Default for Palette {
    fn default() -> Self {
        Self::new()
    }
}

pub trait EventHandler: DynClone {
    fn invoke(
        &self,
        tree: &mut WindowTree,
        window: Window,
        event: Event,
        event_source: Window,
        app: &mut dyn App,
    ) -> bool;
}

clone_trait_object!(EventHandler);

macro_attr! {
    #[derive(Component!)]
    struct WindowNode {
        is_template: bool,
        source_index: Option<usize>,
        parent: Option<Window>,
        prev: Window,
        next: Window,
        first_child: Option<Window>,
        widget: Box<dyn Widget>,
        data: Box<dyn WidgetData>,
        layout: Option<Box<dyn Layout>>,
        palette: Palette,
        clip: Option<Rect>,
        measure_size: Option<(Option<i16>, Option<i16>)>,
        desired_size: Vector,
        arrange_size: Option<Vector>,
        arranged_size: Vector,
        render_bounds: Rect,
        window_bounds: Rect,
        h_align: Option<HAlign>,
        v_align: Option<VAlign>,
        margin: Thickness,
        width: Option<i16>,
        min_width: i16,
        max_width: i16,
        height: Option<i16>,
        min_height: i16,
        max_height: i16,
        event_handler: Option<Box<dyn EventHandler>>,
        focus_tab: Window,
        focus_right: Window,
        focus_left: Window,
        focus_up: Window,
        focus_down: Window,
        contains_primary_focus: bool,
        name: String,
        pre_process: Option<Id<PrePostProcess>>,
        post_process: Option<Id<PrePostProcess>>,
        is_enabled: bool,
        visibility: Visibility,
        cloning: Option<Window>,
    }
}

fn offset_from_root(
    window: Option<Window>,
    tree: &WindowTree
) -> Vector {
    let mut offset = Vector::null();
    let Some(mut window) = window else { return offset; };
    loop {
        offset += tree.arena[window.0].window_bounds.tl.offset_from(Point { x: 0, y: 0 });
        if let Some(parent) = tree.arena[window.0].parent {
            window = parent;
        } else {
            break;
        }
    }
    offset
}

macro_attr! {
    #[derive(NewtypeComponentId!)]
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
    pub struct Window(Id<WindowNode>);
}

impl Window {
    pub fn new(
        tree: &mut WindowTree,
        widget: Box<dyn Widget>,
        parent: Option<Self>,
        prev: Option<Self>,
    ) -> Result<Self, Error> {
        let is_template = if let Some(parent) = parent {
            tree.arena[parent.0].is_template
        } else {
            false
        };
        Self::new_raw(tree, widget, parent, prev, is_template)
    }

    pub fn new_template(
        tree: &mut WindowTree,
        widget: Box<dyn Widget>,
    ) -> Result<Self, Error> {
        Self::new_raw(tree, widget, None, None, true)
    }

    pub fn is_template(self, tree: &WindowTree) -> bool {
        tree.arena[self.0].is_template
    }

    pub fn new_instance(
        self,
        tree: &mut WindowTree,
        parent: Option<Self>,
        prev: Option<Self>
    ) -> Result<Self, Error> {
        assert!(self.is_template(tree), "cannot instantiate non-template window");
        let window = self.begin_cloning(tree, parent, prev)?;
        self.do_cloning(tree);
        self.end_cloning(tree);
        Ok(window)
    }

    fn begin_cloning(
        self,
        tree: &mut WindowTree,
        parent: Option<Self>,
        prev: Option<Self>,
    ) -> Result<Window, Error> {
        let widget = tree.arena[self.0].widget.clone();
        let clone = Self::new(tree, widget, parent, prev)?;
        assert!(tree.arena[self.0].cloning.replace(clone).is_none());
        if let Some(first_child) = self.first_child(tree) {
            let mut child = first_child;
            let mut prev = None;
            loop {
                prev = Some(child.begin_cloning(tree, Some(clone), prev)?);
                child = child.next(tree);
                if child == first_child { break; }
            }
        }
        Ok(clone)
    }

    fn do_cloning(self, tree: &mut WindowTree) {
        let clone = tree.arena[self.0].cloning.unwrap();
        clone.set_palette(tree, self.palette(tree).clone());
        clone.set_visibility(tree, self.visibility(tree));
        clone.set_layout(tree, self.layout_raw(tree).clone());
        clone.set_is_enabled(tree, self.is_enabled(tree));
        clone.set_h_align(tree, self.h_align(tree));
        clone.set_v_align(tree, self.v_align(tree));
        clone.set_margin(tree, self.margin(tree));
        clone.set_min_width(tree, self.min_width(tree));
        clone.set_min_height(tree, self.min_height(tree));
        clone.set_max_width(tree, self.max_width(tree));
        clone.set_max_height(tree, self.max_height(tree));
        clone.set_width(tree, self.width(tree));
        clone.set_height(tree, self.height(tree));
        let clone_window: Box<dyn Fn(&WindowTree, Window) -> Window> =
            Box::new(|tree: &WindowTree, window: Window| tree.arena[window.0].cloning.unwrap_or(window))
        ;
        clone.set_focus_tab(tree, clone_window(tree, self.focus_tab(tree)));
        clone.set_focus_right(tree, clone_window(tree, self.focus_right(tree)));
        clone.set_focus_left(tree, clone_window(tree, self.focus_left(tree)));
        clone.set_focus_up(tree, clone_window(tree, self.focus_up(tree)));
        clone.set_focus_down(tree, clone_window(tree, self.focus_down(tree)));
        let widget = tree.arena[self.0].widget.clone();
        widget.clone_data(tree, self, clone, clone_window);
        if let Some(first_child) = self.first_child(tree) {
            let mut child = first_child;
            loop {
                child.do_cloning(tree);
                child = child.next(tree);
                if child == first_child { break; }
            }
        }
    }

    fn end_cloning(self, tree: &mut WindowTree) {
        assert!(tree.arena[self.0].cloning.take().is_some());
        if let Some(first_child) = self.first_child(tree) {
            let mut child = first_child;
            loop {
                child.end_cloning(tree);
                child = child.next(tree);
                if child == first_child { break; }
            }
        }
    }

    fn new_raw(
        tree: &mut WindowTree,
        widget: Box<dyn Widget>,
        parent: Option<Self>,
        prev: Option<Self>,
        is_template: bool,
    ) -> Result<Self, Error> {
        let data = widget.new();
        let pre_process = if is_template { false } else { widget.pre_process() };
        let post_process = if is_template { false } else { widget.post_process() };
        tree.arena.try_reserve().map_err(|_| Error::Oom)?;
        let window = tree.arena.insert(move |window| {
            (WindowNode {
                is_template,
                source_index: None,
                parent,
                prev: Window(window),
                next: Window(window),
                first_child: None,
                event_handler: None,
                widget,
                data,
                layout: None,
                palette: Palette::new(),
                clip: None,
                measure_size: None,
                desired_size: Vector::null(),
                arrange_size: None,
                arranged_size: Vector::null(),
                render_bounds: Rect { tl: Point { x: 0, y: 0 }, size: Vector::null() },
                window_bounds: Rect { tl: Point { x: 0, y: 0 }, size: Vector::null() },
                h_align: None,
                v_align: None,
                margin: Thickness::all(0),
                width: None,
                min_width: 0,
                max_width: -1,
                height: None,
                min_height: 0,
                max_height: -1,
                focus_tab: Window(window),
                focus_right: Window(window),
                focus_left: Window(window),
                focus_up: Window(window),
                focus_down: Window(window),
                contains_primary_focus: false,
                name: String::new(),
                pre_process: None,
                post_process: None,
                is_enabled: true,
                visibility: Visibility::Visible,
                cloning: None,
            }, Window(window))
        });
        window.attach(tree, parent, prev);
        if pre_process {
            let id = tree.pre_process.insert(|id| (PrePostProcess(window), id));
            tree.arena[window.0].pre_process = Some(id);
        }
        if post_process {
            let id = tree.post_process.insert(|id| (PrePostProcess(window), id));
            tree.arena[window.0].post_process = Some(id);
        }
        Ok(window)
    }

    pub fn widget_extension<DynInterface: ?Sized + 'static>(
        self,
        tree: &WindowTree
    ) -> Option<Box<DynInterface>> where
        DynInterface: Pointee<Metadata=DynMetadata<DynInterface>>
    {
        let widget = tree.arena[self.0].widget.clone();
        dyn_cast_box(widget)
    }

    pub fn source_index(self, tree: &WindowTree) -> Option<usize> {
        tree.arena[self.0].source_index
    }

    pub fn set_source_index(self, tree: &mut WindowTree, value: Option<usize>) {
        tree.arena[self.0].source_index = value;
    }

    pub fn set_clip(self, tree: &mut WindowTree, value: Option<Rect>) {
        tree.arena[self.0].clip = value;
        self.invalidate_render(tree);
    }

    pub fn invalidate_measure(self, tree: &mut WindowTree) {
        let mut window = self;
        loop {
            let node = &mut tree.arena[window.0];
            let old_measure_size = node.measure_size.take();
            if old_measure_size.is_none() { break; }
            let Some(parent) = node.parent else { break; };
            window = parent;
        }
    }

    pub fn invalidate_arrange(self, tree: &mut WindowTree) {
        let mut window = self;
        loop {
            let node = &mut tree.arena[window.0];
            let old_arrange_size = node.arrange_size.take();
            if old_arrange_size.is_none() { break; }
            let Some(parent) = node.parent else { break; };
            window = parent;
        }
    }

    pub fn measure(
        self,
        tree: &mut WindowTree,
        available_width: Option<i16>,
        available_height: Option<i16>,
        app: &mut dyn App
    ) {
        let node = &mut tree.arena[self.0];
        if node.visibility == Visibility::Collapsed {
            node.desired_size = Vector::null();
            return;
        }
        let min_size = Vector {
            x: node.width.unwrap_or(node.min_width),
            y: node.height.unwrap_or(node.min_height)
        };
        let max_size = Vector {
            x: node.width.unwrap_or(node.max_width),
            y: node.height.unwrap_or(node.max_height)
        };
        let available_size = Vector { x: available_width.unwrap_or(0), y: available_height.unwrap_or(0) };
        let measure_size = node.margin.shrink_rect_size(available_size).min(max_size).max(min_size);
        let measure_size = (available_width.map(|_| measure_size.x), available_height.map(|_| measure_size.y));
        if node.measure_size == Some(measure_size) { return; }
        node.measure_size = Some(measure_size);
        let widget = node.widget.clone();
        let measured_size = widget.measure(tree, self, measure_size.0, measure_size.1, app);
        let node = &mut tree.arena[self.0];
        node.desired_size = node.margin.expand_rect_size(measured_size.min(max_size).max(min_size));
        self.invalidate_arrange(tree);
    }

    pub fn arrange(self, tree: &mut WindowTree, final_bounds: Rect, app: &mut dyn App) {
        let node = &mut tree.arena[self.0];
        if node.visibility == Visibility::Collapsed {
            let bounds = Rect { tl: Point { x: 0, y: 0 }, size: Vector::null() };
            node.render_bounds = bounds;
            self.move_xy_raw(tree, bounds);
            return;
        }
        let min_size = Vector {
            x: node.width.unwrap_or(node.min_width),
            y: node.height.unwrap_or(node.min_height)
        };
        let max_size = Vector {
            x: node.width.unwrap_or(node.max_width),
            y: node.height.unwrap_or(node.max_height)
        };
        let margined_bounds = node.margin.shrink_rect(final_bounds);
        let arrange_size = Vector {
            x: if node.h_align.is_none() { final_bounds.w() } else { node.desired_size.x },
            y: if node.v_align.is_none() { final_bounds.h() } else { node.desired_size.y }
        };
        let arrange_size = node.margin.shrink_rect_size(arrange_size).min(max_size).max(min_size);
        if node.arrange_size != Some(arrange_size) {
            node.arrange_size = Some(arrange_size);
            let widget = node.widget.clone();
            let arranged_size = widget.arrange(
                tree,
                self,
                Rect { tl: Point { x: 0, y: 0 }, size: arrange_size },
                app
            );
            let node = &mut tree.arena[self.0];
            node.arranged_size = arranged_size.min(max_size).max(min_size);
        }
        let node = &mut tree.arena[self.0];
        let arranged_bounds = Thickness::align(
            node.arranged_size,
            margined_bounds.size,
            node.h_align.unwrap_or(HAlign::Left),
            node.v_align.unwrap_or(VAlign::Top)
        ).shrink_rect(margined_bounds).intersect(margined_bounds);
        node.render_bounds = final_bounds;
        self.move_xy_raw(tree, arranged_bounds);
    }

    pub fn set_event_handler(
        self,
        tree: &mut WindowTree,
        handler: Option<Box<dyn EventHandler>>
    ) {
        tree.arena[self.0].event_handler = handler;
    }

    pub fn desired_size(
        self,
        tree: &WindowTree
    ) -> Vector {
        tree.arena[self.0].desired_size
    }

    pub fn render_bounds(
        self,
        tree: &WindowTree
    ) -> Rect {
        tree.arena[self.0].render_bounds
    }

    pub fn inner_bounds(
        self,
        tree: &WindowTree
    ) -> Rect {
        let window_bounds = tree.arena[self.0].window_bounds;
        Rect { tl: Point { x: 0, y: 0 }, size: window_bounds.size }
    }

    pub fn data<'a, T: WidgetData + 'static>(
        self,
        tree: &'a WindowTree<'_>
    ) -> &'a T {
        tree.arena[self.0].data.downcast_ref::<T>().expect("wrong type")
    }

    pub fn data_mut<'a, T: WidgetData + 'static>(
        self,
        tree: &'a mut WindowTree<'_>
    ) -> &'a mut T {
        tree.arena[self.0].data.downcast_mut::<T>().expect("wrong type")
    }

    pub fn layout_raw<'a>(
        self,
        tree: &'a WindowTree<'_>
    ) -> &'a Option<Box<dyn Layout>> {
        &tree.arena[self.0].layout
    }

    pub fn layout<'a, T: Layout + 'static>(
        self,
        tree: &'a WindowTree<'_>
    ) -> Option<&'a T> {
        tree.arena[self.0].layout.as_ref().and_then(|x| x.downcast_ref::<T>())
    }

    pub fn layout_mut<R>(
        self,
        tree: &mut WindowTree,
        f: impl FnOnce(&mut Option<Box<dyn Layout>>) -> R
    ) -> R {
        let layout = &mut tree.arena[self.0].layout;
        let res = f(layout);
        if let Some(parent) = self.parent(tree) {
            parent.invalidate_measure(tree);
        }
        res
    }

    pub fn set_layout(
        self,
        tree: &mut WindowTree,
        value: Option<Box<dyn Layout>>
    ) {
        self.layout_mut(tree, |layout| replace(layout, value));
    }

    pub fn focus_tab(self, tree: &WindowTree) -> Self {
        tree.arena[self.0].focus_tab
    }

    pub fn set_focus_tab(self, tree: &mut WindowTree, value: Self) {
        tree.arena[self.0].focus_tab = value;
    }

    pub fn focus_right(self, tree: &WindowTree) -> Self {
        tree.arena[self.0].focus_right
    }

    pub fn set_focus_right(self, tree: &mut WindowTree, value: Self) {
        let old_value = replace(&mut tree.arena[self.0].focus_right, value);
        if old_value != value {
            value.set_focus_left(tree, self);
        }
    }

    pub fn focus_left(self, tree: &WindowTree) -> Self {
        tree.arena[self.0].focus_left
    }

    pub fn set_focus_left(self, tree: &mut WindowTree, value: Self) {
        let old_value = replace(&mut tree.arena[self.0].focus_left, value);
        if old_value != value {
            value.set_focus_right(tree, self);
        }
    }

    pub fn focus_up(self, tree: &WindowTree) -> Self {
        tree.arena[self.0].focus_up
    }

    pub fn set_focus_up(self, tree: &mut WindowTree, value: Self) {
        let old_value = replace(&mut tree.arena[self.0].focus_up, value);
        if old_value != value {
            value.set_focus_down(tree, self);
        }
    }

    pub fn focus_down(self, tree: &WindowTree) -> Self {
        tree.arena[self.0].focus_down
    }

    pub fn set_focus_down(self, tree: &mut WindowTree, value: Self) {
        let old_value = replace(&mut tree.arena[self.0].focus_down, value);
        if old_value != value {
            value.set_focus_up(tree, self);
        }
    }

    pub fn is_primary_focused(self, tree: &WindowTree) -> bool {
        tree.primary_focused == Some(self)
    }

    pub fn is_secondary_focused(self, tree: &WindowTree) -> bool {
        tree.secondary_focused == Some(self)
    }

    pub fn is_focused(self, tree: &WindowTree) -> bool {
        tree.primary_focused == Some(self) || tree.secondary_focused == Some(self)
    }

    pub fn set_focused_primary(self, tree: &mut WindowTree, value: bool) {
        assert!(!self.is_template(tree), "cannot focus template");
        if value {
            tree.next_primary_focused = Some(Some(self));
        } else if
            tree.next_primary_focused == Some(Some(self)) ||
            tree.next_primary_focused == None && tree.primary_focused == Some(self)
        {
            tree.next_primary_focused = Some(None);
        }
    }

    pub fn set_focused_secondary(self, tree: &mut WindowTree, value: bool) {
        assert!(!self.is_template(tree), "cannot focus template");
        if value {
            tree.next_secondary_focused = Some(Some(self));
        } else if
            tree.next_secondary_focused == Some(Some(self)) ||
            tree.next_secondary_focused == None && tree.secondary_focused == Some(self)
        {
            tree.next_secondary_focused = Some(None);
        }
    }

    pub fn palette<'a>(self, tree: &'a WindowTree<'_>) -> &'a Palette {
        &tree.arena[self.0].palette
    }

    pub fn palette_mut<T>(self, tree: &mut WindowTree, f: impl FnOnce(&mut Palette) -> T) -> T {
        let res = f(&mut tree.arena[self.0].palette);
        self.invalidate_render(tree);
        res
    }

    pub fn set_palette(self, tree: &mut WindowTree, value: Palette) {
        self.palette_mut(tree, |palette| replace(palette, value));
    }

    pub fn color(self, tree: &WindowTree, i: u8) -> (Fg, Bg) {
        let mut window = self;
        let mut index = i;
        loop {
            match window.palette(tree).get(index) {
                Left(i) => {
                    if let Some(parent) = window.parent(tree) {
                        window = parent;
                        index = i;
                    } else if let Right(color) = tree.palette().get(i) {
                        break color;
                    } else {
                        break (Fg::Red, Bg::Green);
                    }
                },
                Right(c) => break c,
            }
        }
    }

    pub fn is_enabled(self, tree: &WindowTree) -> bool {
        tree.arena[self.0].is_enabled
    }

    pub fn set_is_enabled(self, tree: &mut WindowTree, value: bool) {
        tree.arena[self.0].is_enabled = value;
        self.invalidate_render(tree);
    }

    pub fn actual_is_enabled(self, tree: &WindowTree) -> bool {
        let mut window = self;
        loop {
            if !window.is_enabled(tree) { return false; }
            if let Some(parent) = window.parent(tree) {
                window = parent;
            } else {
                break;
            }
        }
        true
    }

    pub fn visibility(self, tree: &WindowTree) -> Visibility {
        tree.arena[self.0].visibility
    }

    pub fn set_visibility(self, tree: &mut WindowTree, value: Visibility) {
        let old_value = replace(&mut tree.arena[self.0].visibility, value);
        match (old_value, value) {
            (Visibility::Visible, Visibility::Collapsed) =>
                self.parent(tree).map(|x| x.invalidate_measure(tree)).unwrap_or(()),
            (Visibility::Visible, Visibility::Hidden) =>
                self.invalidate_render(tree),
            (Visibility::Collapsed, Visibility::Visible) =>
                self.parent(tree).map(|x| x.invalidate_measure(tree)).unwrap_or(()),
            (Visibility::Collapsed, Visibility::Hidden) =>
                self.parent(tree).map(|x| x.invalidate_measure(tree)).unwrap_or(()),
            (Visibility::Hidden, Visibility::Visible) =>
                self.invalidate_render(tree),
            (Visibility::Hidden, Visibility::Collapsed) =>
                self.parent(tree).map(|x| x.invalidate_measure(tree)).unwrap_or(()),
            _ => { },
        }
    }

    pub fn parent(
        self,
        tree: &WindowTree
    ) -> Option<Self> {
        tree.arena[self.0].parent
    }

    pub fn first_child(
        self,
        tree: &WindowTree
    ) -> Option<Self> {
        tree.arena[self.0].first_child
    }

    pub fn prev(
        self,
        tree: &WindowTree
    ) -> Self {
        tree.arena[self.0].prev
    }

    pub fn next(
        self,
        tree: &WindowTree
    ) -> Self {
        tree.arena[self.0].next
    }

    pub fn raise(
        self,
        tree: &mut WindowTree,
        event: Event,
        app: &mut dyn App
    ) -> bool {
        self.raise_priv(tree, event, false, app)
    }

    fn raise_priv(
        self,
        tree: &mut WindowTree,
        event: Event,
        secondary: bool,
        app: &mut dyn App
    ) -> bool {
        let mut handled = false;
        self.raise_raw(tree, event.preview(), self, secondary, &mut handled, app);
        if !handled {
            self.raise_raw(tree, event, self, secondary, &mut handled, app);
        }
        handled
    }

    fn raise_raw(
        self,
        tree: &mut WindowTree,
        event: Event,
        event_source: Window,
        secondary: bool,
        handled: &mut bool,
        app: &mut dyn App
    ) {
        if secondary && tree.arena[self.0].contains_primary_focus { return; }
        let parent = self.parent(tree);
        if !*handled && event.is_preview() {
            if let Some(parent) = parent {
                parent.raise_raw(tree, event, event_source, secondary, handled, app);
            }
        }
        if !*handled {
            *handled = self.raise_core(tree, event, event_source, app);
        }
        if !*handled && !event.is_preview() {
            if let Some(parent) = parent {
                parent.raise_raw(tree, event, event_source, secondary, handled, app);
            }
        }
    }

    fn raise_core(
        self,
        tree: &mut WindowTree,
        event: Event,
        event_source: Window,
        app: &mut dyn App
    ) -> bool {
        let node = &tree.arena[self.0];
        let widget = node.widget.clone();
        let event_handler = node.event_handler.clone();
        let mut handled = widget.update(tree, self, event, event_source, app);
        if !handled {
            if let Some(event_handler) = event_handler {
                handled = event_handler.invoke(tree, self, event, event_source, app);
            }
        }
        handled
    }

    fn move_xy_raw(
        self,
        tree: &mut WindowTree,
        window_bounds: Rect
    ) {
        let parent = tree.arena[self.0].parent;
        let screen_bounds = window_bounds.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_bounds);
        let window_bounds = replace(&mut tree.arena[self.0].window_bounds, window_bounds);
        let screen_bounds = window_bounds.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_bounds);
    }

    pub fn h_align(self, tree: &WindowTree) -> Option<HAlign> {
        tree.arena[self.0].h_align
    }

    pub fn set_h_align(self, tree: &mut WindowTree, value: Option<HAlign>) {
        tree.arena[self.0].h_align = value;
        self.invalidate_measure(tree);
    }

    pub fn v_align(self, tree: &WindowTree) -> Option<VAlign> {
        tree.arena[self.0].v_align
    }

    pub fn set_v_align(self, tree: &mut WindowTree, value: Option<VAlign>) {
        tree.arena[self.0].v_align = value;
        self.invalidate_measure(tree);
    }

    pub fn margin(self, tree: &WindowTree) -> Thickness {
        tree.arena[self.0].margin
    }

    pub fn set_margin(self, tree: &mut WindowTree, value: Thickness) {
        tree.arena[self.0].margin = value;
        self.invalidate_measure(tree);
    }

    pub fn min_width(self, tree: &WindowTree) -> i16 {
        tree.arena[self.0].min_width
    }

    pub fn set_min_width(self, tree: &mut WindowTree, value: i16) {
        tree.arena[self.0].min_width = value;
        self.invalidate_measure(tree);
    }

    pub fn min_height(self, tree: &WindowTree) -> i16 {
        tree.arena[self.0].min_height
    }

    pub fn set_min_height(self, tree: &mut WindowTree, value: i16) {
        tree.arena[self.0].min_height = value;
        self.invalidate_measure(tree);
    }

    pub fn max_width(self, tree: &WindowTree) -> i16 {
        tree.arena[self.0].max_width
    }

    pub fn set_max_width(self, tree: &mut WindowTree, value: i16) {
        tree.arena[self.0].max_width = value;
        self.invalidate_measure(tree);
    }

    pub fn max_height(self, tree: &WindowTree) -> i16 {
        tree.arena[self.0].max_height
    }

    pub fn set_max_height(self, tree: &mut WindowTree, value: i16) {
        tree.arena[self.0].max_height = value;
        self.invalidate_measure(tree);
    }

    pub fn width(self, tree: &WindowTree) -> Option<i16> {
        tree.arena[self.0].width
    }

    pub fn set_width(self, tree: &mut WindowTree, value: Option<i16>) {
        tree.arena[self.0].width = value;
        self.invalidate_measure(tree);
    }

    pub fn height(self, tree: &WindowTree) -> Option<i16> {
        tree.arena[self.0].height
    }

    pub fn set_height(self, tree: &mut WindowTree, value: Option<i16>) {
        tree.arena[self.0].height = value;
        self.invalidate_measure(tree);
    }

    pub fn move_z(
        self,
        tree: &mut WindowTree,
        prev: Option<Self>
    ) {
        let parent = self.detach(tree);
        self.attach(tree, parent, prev);
        let bounds = tree.arena[self.0].window_bounds;
        let screen_bounds = bounds.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_bounds);
    }

    pub fn name<'a>(self, tree: &'a WindowTree) -> &'a String {
        &tree.arena[self.0].name
    }

    pub fn name_mut<T>(self, tree: &mut WindowTree, f: impl FnOnce(&mut String) -> T) -> T {
        f(&mut tree.arena[self.0].name)
    }

    pub fn set_name<'a>(self, tree: &mut WindowTree, value: impl Into<Cow<'a, str>>) {
        self.name_mut(tree, |name| replace(name, value.into().into_owned()));
    }

    fn detach(
        self,
        tree: &mut WindowTree
    ) -> Option<Self> {
        let node = &mut tree.arena[self.0];
        let parent = node.parent.take();
        let prev = replace(&mut node.prev, self);
        let next = replace(&mut node.next, self);
        tree.arena[prev.0].next = next;
        tree.arena[next.0].prev = prev;
        if let Some(parent) = parent {
            let parent_node = &mut tree.arena[parent.0];
            if parent_node.first_child.unwrap() == self {
                parent_node.first_child = if next == self { None } else { Some(next) };
            }
            parent.invalidate_measure(tree);
        } else {
            if tree.first_child.unwrap() == self {
                tree.first_child = if next == self { None } else { Some(next) };
            }
        }
        parent
    }

    fn attach(
        self,
        tree: &mut WindowTree,
        parent: Option<Self>,
        prev: Option<Self>
    ) {
        let (prev, next) = if let Some(prev) = prev {
            assert_eq!(tree.arena[prev.0].parent, parent);
            let next = replace(&mut tree.arena[prev.0].next, self);
            tree.arena[next.0].prev = self;
            (prev, next)
        } else if let Some(parent) = parent {
            let parent_node = &mut tree.arena[parent.0];
            let next = parent_node.first_child.replace(self).unwrap_or(self);
            let prev = replace(&mut tree.arena[next.0].prev, self);
            tree.arena[prev.0].next = self;
            (prev, next)
        } else {
            let next = tree.first_child.replace(self).unwrap_or(self);
            let prev = replace(&mut tree.arena[next.0].prev, self);
            tree.arena[prev.0].next = self;
            (prev, next)
        };
        let node = &mut tree.arena[self.0];
        node.parent = parent;
        node.prev = prev;
        node.next = next;
        parent.map(|x| x.invalidate_measure(tree));
    }

    pub fn drop_window(
        self,
        tree: &mut WindowTree,
        app: &mut dyn App,
    ) {
        self.cleanup(tree, app);
        let parent = self.detach(tree);
        let mut node = tree.arena.remove(self.0);
        if let Some(pre_process) = node.pre_process {
            tree.pre_process.remove(pre_process);
        }
        if let Some(post_process) = node.post_process {
            tree.post_process.remove(post_process);
        }
        node.data.drop_widget_data(tree, app);
        let screen_bounds = node.window_bounds.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_bounds);
        Self::drop_node_tree(node.first_child, tree, app);
    }

    fn cleanup(
        self,
        tree: &mut WindowTree,
        app: &mut dyn App,
    ) {
        if tree.next_primary_focused == Some(Some(self)) {
            tree.next_primary_focused = Some(None);
        }
        if tree.primary_focused == Some(self) {
            tree.focus_primary_raw(None, app);
        }
        if tree.next_secondary_focused == Some(Some(self)) {
            tree.next_secondary_focused = Some(None);
        }
        if tree.secondary_focused == Some(self) {
            tree.focus_secondary_raw(None, app);
        }
        if let Some(first_child) = self.first_child(tree) {
            let mut child = first_child;
            loop {
                child.cleanup(tree, app);
                child = child.next(tree);
                if child == first_child { break; }
            }
        }
    }

    fn drop_node_tree(
        first_child: Option<Window>,
        tree: &mut WindowTree,
        app: &mut dyn App,
    ) {
        if let Some(first_child) = first_child {
            let mut child = first_child;
            loop {
                let mut child_node = tree.arena.remove(child.0);
                if let Some(pre_process) = child_node.pre_process {
                    tree.pre_process.remove(pre_process);
                }
                if let Some(post_process) = child_node.post_process {
                    tree.post_process.remove(post_process);
                }
                child_node.data.drop_widget_data(tree, app);
                child = child_node.next;
                Self::drop_node_tree(child_node.first_child, tree, app);
                if child == first_child { break; }
            }
        }
    }

    pub fn invalidate_rect(
        self,
        tree: &mut WindowTree,
        rect: Rect
    ) {
        let bounds = tree.arena[self.0].window_bounds;
        let rect = rect.offset(bounds.tl.offset_from(Point { x: 0, y: 0 })).intersect(bounds);
        let parent = tree.arena[self.0].parent;
        let screen_rect = rect.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_rect);
    }
 
    pub fn invalidate_render(
        self,
        tree: &mut WindowTree
    ) {
        let bounds = tree.arena[self.0].window_bounds;
        let parent = tree.arena[self.0].parent;
        let screen_bounds = bounds.offset(offset_from_root(parent, tree));
        invalidate_rect(tree.screen(), screen_bounds);
    }

    pub fn bring_into_view(self, tree: &mut WindowTree) {
        let node = &tree.arena[self.0];
        if let Some(mut window) = node.parent {
            let mut rect = node.window_bounds;
            loop {
                let node = &tree.arena[window.0];
                let widget = node.widget.clone();
                let parent = node.parent;
                if widget.bring_into_view(tree, window, rect) {
                    if let Some(parent) = parent {
                        rect = tree.arena[window.0].window_bounds;
                        window = parent;
                    } else {
                        break;
                    }
                } else {
                    if let Some(parent) = parent {
                        let offset = tree.arena[window.0].window_bounds.tl.offset_from(Point { x: 0, y: 0 });
                        rect = rect.offset(offset);
                        window = parent;
                    } else {
                        break;
                    }
                }
            }
        }
    }
}

const FPS: u16 = 40;

pub const COLOR_BACKGROUND: u8 = 10;
pub const COLOR_TEXT: u8 = 11;
pub const COLOR_DISABLED: u8 = 12;
pub const COLOR_HOTKEY: u8 = 13;
pub const COLOR_INPUT_LINE_INVALID: u8 = 14;
pub const COLOR_INPUT_LINE_FOCUSED: u8 = 15;
pub const COLOR_INPUT_LINE_FOCUSED_INVALID: u8 = 16;
pub const COLOR_INPUT_LINE_FOCUSED_DISABLED: u8 = 17;
pub const COLOR_BUTTON_FOCUSED: u8 = 18;
pub const COLOR_BUTTON_FOCUSED_HOTKEY: u8 = 19;
pub const COLOR_BUTTON_FOCUSED_DISABLED: u8 = 20;
pub const COLOR_BUTTON_PRESSED: u8 = 21;
pub const COLOR_FRAME: u8 = 22;

pub const COLORS: RangeInclusive<u8> = 10 ..= 22;

pub const COLOR_IN_FRAME: u8 = 20;

fn root_palette() -> Palette {
    let mut p = Palette::new();

    p.set(COLOR_BACKGROUND, Right((Fg::LightGray, Bg::None)));
    p.set(COLOR_TEXT, Right((Fg::LightGray, Bg::None)));
    p.set(COLOR_DISABLED, Right((Fg::DarkGray, Bg::None)));
    p.set(COLOR_HOTKEY, Right((Fg::White, Bg::None)));
    p.set(COLOR_INPUT_LINE_INVALID, Right((Fg::Red, Bg::None)));
    p.set(COLOR_INPUT_LINE_FOCUSED, Right((Fg::LightGray, Bg::Blue)));
    p.set(COLOR_INPUT_LINE_FOCUSED_DISABLED, Right((Fg::DarkGray, Bg::Blue)));
    p.set(COLOR_INPUT_LINE_FOCUSED_INVALID, Right((Fg::LightGray, Bg::Red)));
    p.set(COLOR_BUTTON_FOCUSED, Right((Fg::LightGray, Bg::Blue)));
    p.set(COLOR_BUTTON_FOCUSED_HOTKEY, Right((Fg::White, Bg::Blue)));
    p.set(COLOR_BUTTON_FOCUSED_DISABLED, Right((Fg::DarkGray, Bg::Blue)));
    p.set(COLOR_BUTTON_PRESSED, Right((Fg::Blue, Bg::None)));
    p.set(COLOR_FRAME, Right((Fg::LightGray, Bg::Black)));

    p.set(COLOR_IN_FRAME + COLOR_BACKGROUND, Right((Fg::LightGray, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_TEXT, Right((Fg::LightGray, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_DISABLED, Right((Fg::DarkGray, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_HOTKEY, Right((Fg::White, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_INPUT_LINE_INVALID, Right((Fg::Red, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_INPUT_LINE_FOCUSED, Right((Fg::LightGray, Bg::Blue)));
    p.set(COLOR_IN_FRAME + COLOR_INPUT_LINE_FOCUSED_DISABLED, Right((Fg::DarkGray, Bg::Blue)));
    p.set(COLOR_IN_FRAME + COLOR_INPUT_LINE_FOCUSED_INVALID, Right((Fg::LightGray, Bg::Red)));
    p.set(COLOR_IN_FRAME + COLOR_BUTTON_FOCUSED, Right((Fg::LightGray, Bg::Blue)));
    p.set(COLOR_IN_FRAME + COLOR_BUTTON_FOCUSED_HOTKEY, Right((Fg::White, Bg::Blue)));
    p.set(COLOR_IN_FRAME + COLOR_BUTTON_FOCUSED_DISABLED, Right((Fg::DarkGray, Bg::Blue)));
    p.set(COLOR_IN_FRAME + COLOR_BUTTON_PRESSED, Right((Fg::Blue, Bg::Black)));
    p.set(COLOR_IN_FRAME + COLOR_FRAME, Right((Fg::LightGray, Bg::Black)));

    p
}

macro_attr! {
    #[derive(Component!)]
    struct TimerData {
        start: MonoTime,
        span_ms: u16,
        alarm: Box<dyn FnOnce(&mut WindowTree, &mut dyn App)>,
    }
}

macro_attr! {
    #[derive(NewtypeComponentId!)]
    #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
    pub struct Timer(Id<TimerData>);
}

impl Timer {
    pub fn new(
        tree: &mut WindowTree,
        span_ms: u16,
        alarm: Box<dyn FnOnce(&mut WindowTree, &mut dyn App)>
    ) -> Self {
        let start = tree.clock.time();
        tree.timers.insert(move |id| (TimerData {
            start,
            span_ms,
            alarm
        }, Timer(id)))
    }

    pub fn drop_timer(self, tree: &mut WindowTree) {
        tree.timers.remove(self.0);
    }
}

macro_attr! {
    #[derive(Component!)]
    #[derive(Clone)]
    struct PrePostProcess(Window);
}

pub struct WindowTree<'clock> {
    screen: Option<Box<dyn Screen>>,
    arena: Arena<WindowNode>,
    first_child: Option<Window>,
    primary_focused: Option<Window>,
    secondary_focused: Option<Window>,
    next_primary_focused: Option<Option<Window>>,
    next_secondary_focused: Option<Option<Window>>,
    cursor: Option<Point>,
    quit: bool,
    timers: Arena<TimerData>,
    clock: &'clock MonoClock,
    palette: Palette,
    pre_process: Arena<PrePostProcess>,
    post_process: Arena<PrePostProcess>,
}

impl<'clock> WindowTree<'clock> {
    pub fn new(
        screen: Box<dyn Screen>,
        clock: &'clock MonoClock,
    ) -> Result<Self, Error> {
        Ok(WindowTree {
            screen: Some(screen),
            arena: Arena::new(),
            first_child: None,
            primary_focused: None,
            secondary_focused: None,
            next_primary_focused: None,
            next_secondary_focused: None,
            cursor: None,
            quit: false,
            clock,
            timers: Arena::new(),
            palette: root_palette(),
            pre_process: Arena::new(),
            post_process: Arena::new(),
        })
    }

    pub fn palette(&self) -> &Palette {
        &self.palette
    }

    pub fn palette_mut<T>(&mut self, f: impl FnOnce(&mut Palette) -> T) -> T {
        let res = f(&mut self.palette);
        if let Some(first_child) = self.first_child {
            let mut child = first_child;
            loop {
                child.invalidate_render(self);
                child = child.next(self);
                if child == first_child { break; }
            }
        }
        res
    }

    pub fn first_child(&self) -> Option<Window> { self.first_child }

    pub fn primary_focused(&self) -> Option<Window> { self.primary_focused }

    pub fn secondary_focused(&self) -> Option<Window> { self.secondary_focused }

    pub fn quit(&mut self) {
        self.quit = true;
    }

    fn screen(&mut self) -> &mut dyn Screen {
        self.screen.as_mut().expect("WindowTree is in invalid state").as_mut()
    }

    fn render_window(&mut self, window: Window, offset: Vector, clip_bounds: Rect, app: &mut dyn App) {
        if window.visibility(self) != Visibility::Visible {
            return;
        }
        let bounds = self.arena[window.0].window_bounds.offset(offset);
        let offset = bounds.tl.offset_from(Point { x: 0, y: 0 });
        let clip = self.arena[window.0].clip.map(|x| x.offset(offset));
        let mut clipped_bounds = bounds.intersect(clip_bounds);
        if let Some(clip) = clip {
            clipped_bounds = clipped_bounds.intersect(clip);
        }
        let screen = self.screen();
        if !rect_invalidated(screen, clipped_bounds) { return; }
        let screen = self.screen.take().expect("WindowTree is in invalid state");
        let mut port = RenderPort {
            screen,
            cursor: self.cursor,
            offset,
            bounds: clipped_bounds,
        };
        let widget = self.arena[window.0].widget.clone();
        widget.render(self, window, &mut port, app);
        self.screen.replace(port.screen);
        self.cursor = port.cursor;
        if let Some(first_child) = self.arena[window.0].first_child {
            let mut child = first_child;
            loop {
                self.render_window(child, offset, clipped_bounds, app);
                child = self.arena[child.0].next;
                if child == first_child { break; }
            }
        }
    }

    pub fn run(&mut self, app: &mut dyn App) -> Result<(), Error> {
        let mut time = self.clock.time();
        while !self.quit {
            if let Some(next_primary_focused) = self.next_primary_focused.take() {
                self.focus_primary_raw(next_primary_focused, app);
            }
            if let Some(next_secondary_focused) = self.next_secondary_focused.take() {
                self.focus_secondary_raw(next_secondary_focused, app);
            }
            let no_timers = self.timers.items().is_empty();
            let timers_time = self.clock.time();
            loop {
                let timer = self.timers.items().iter()
                    .find(|(_, data)| timers_time.delta_ms_u16(data.start).unwrap_or(u16::MAX) >= data.span_ms)
                    .map(|(id, _)| id)
                ;
                if let Some(timer) = timer {
                    let alarm = self.timers.remove(timer).alarm;
                    alarm(self, app);
                } else {
                    break;
                }
            }
            if no_timers {
                self.update(true, app)?;
            } else {
                let ms = time.split_ms_u16(self.clock).unwrap_or(u16::MAX);
                self.update(false, app)?;
                assert!(FPS != 0 && u16::MAX / FPS > 8);
                self.clock.sleep_ms_u16((1000 / FPS).saturating_sub(ms));
            }
        }
        Ok(())
    }

    fn update(&mut self, wait: bool, app: &mut dyn App) -> Result<(), Error> {
        let screen = self.screen.as_mut().expect("WindowTree is in invalid state");
        let screen_size = screen.size();
        if let Some(first_child) = self.first_child {
            let mut child = first_child;
            loop {
                child.measure(self, Some(screen_size.x), Some(screen_size.y), app);
                child.arrange(self, Rect { tl: Point { x: 0, y: 0 }, size: screen_size }, app);
                child = child.next(self);
                if child == first_child { break; }
            }
        }
        if let Some(cursor) = self.cursor {
            let screen = self.screen();
            if rect_invalidated(screen, Rect { tl: cursor, size: Vector { x: 1, y: 1 } }) {
                self.cursor = None;
            }
        }
        if let Some(first_child) = self.first_child {
            let mut child = first_child;
            loop {
                let bounds = self.arena[child.0].window_bounds;
                let offset = bounds.tl.offset_from(Point { x: 0, y: 0 });
                self.render_window(child, offset, bounds, app);
                child = child.next(self);
                if child == first_child { break; }
            }
        }
        let screen = self.screen.as_mut().expect("WindowTree is in invalid state");
        if let Some(screen_Event::Key(n, key)) = screen.update(self.cursor, wait)? {
            for _ in 0 .. n.get() {
                match key {
                    Key::Tab => {
                        if let Some(primary_focused) = self.primary_focused {
                            let focus = primary_focused.focus_tab(self);
                            if self.focus_primary(focus) { continue; }
                        }
                    },
                    Key::Left => {
                        if let Some(primary_focused) = self.primary_focused {
                            let focus = primary_focused.focus_left(self);
                            if self.focus_primary(focus) { continue; }
                        }
                        if let Some(secondary_focused) = self.secondary_focused {
                            let focus = secondary_focused.focus_left(self);
                            if self.focus_secondary(focus) { continue; }
                        }
                    },
                    Key::Right => {
                        if let Some(primary_focused) = self.primary_focused {
                            let focus = primary_focused.focus_right(self);
                            if self.focus_primary(focus) { continue; }
                        }
                        if let Some(secondary_focused) = self.secondary_focused {
                            let focus = secondary_focused.focus_right(self);
                            if self.focus_secondary(focus) { continue; }
                        }
                    },
                    Key::Up => {
                        if let Some(primary_focused) = self.primary_focused {
                            let focus = primary_focused.focus_up(self);
                            if self.focus_primary(focus) { continue; }
                        }
                        if let Some(secondary_focused) = self.secondary_focused {
                            let focus = secondary_focused.focus_up(self);
                            if self.focus_secondary(focus) { continue; }
                        }
                    },
                    Key::Down => {
                        if let Some(primary_focused) = self.primary_focused {
                            let focus = primary_focused.focus_down(self);
                            if self.focus_primary(focus) { continue; }
                        }
                        if let Some(secondary_focused) = self.secondary_focused {
                            let focus = secondary_focused.focus_down(self);
                            if self.focus_secondary(focus) { continue; }
                        }
                    },
                    _ => { },
                }
                let mut handled = false;
                for pre_process in self.pre_process.items().clone().values() {
                    handled = pre_process.0.raise_core(self, Event::PreProcessKey(key), pre_process.0, app);
                    if handled { break; }
                }
                if handled { continue; }
                handled = self.primary_focused.map_or(false, |x|
                    x.raise_priv(self, Event::Key(key), false, app)
                );
                if handled { continue; }
                handled = self.secondary_focused.map_or(false, |x|
                    x.raise_priv(self, Event::Key(key), true, app)
                );
                if handled {
                    self.primary_focused.map(|x|
                        x.raise_priv(self, Event::Cmd(CMD_LOST_ATTENTION), false, app)
                    );
                    continue;
                }
                for post_process in self.post_process.items().clone().values() {
                    handled =
                        post_process.0.raise_core(self, Event::PostProcessKey(key), post_process.0, app);
                    if handled { break; }
                }
            }
        }
        Ok(())
    }

    fn focus_primary(
        &mut self,
        window: Window,
    ) -> bool {
        if Some(window) == self.primary_focused { return false; }
        window.set_focused_primary(self, true);
        true
    }

    fn focus_primary_raw(
        &mut self,
        window: Option<Window>,
        app: &mut dyn App
    ) {
        let old_focused = self.primary_focused;
        if window == old_focused { return; }
        window.map(|x| x.raise(self, Event::Cmd(CMD_GOT_PRIMARY_FOCUS), app));

        if let Some(mut window) = self.primary_focused {
            loop {
                self.arena[window.0].contains_primary_focus = false;
                if let Some(parent) = window.parent(self) {
                    window = parent;
                } else {
                    break;
                }
            }
        }
        self.primary_focused = window;
        if let Some(mut window) = self.primary_focused {
            loop {
                self.arena[window.0].contains_primary_focus = true;
                if let Some(parent) = window.parent(self) {
                    window = parent;
                } else {
                    break;
                }
            }
        }

        old_focused.map(|x| x.raise(self, Event::Cmd(CMD_LOST_PRIMARY_FOCUS), app));
        window.map(|x| x.bring_into_view(self));
    }

    fn focus_secondary(
        &mut self,
        window: Window,
    ) -> bool {
        if Some(window) == self.secondary_focused { return false; }
        let focusable = self.arena[window.0].widget.secondary_focusable();
        if !focusable { return false; }
        window.set_focused_secondary(self, true);
        true
    }

    fn focus_secondary_raw(
        &mut self,
        window: Option<Window>,
        app: &mut dyn App
    ) {
        let old_focused = self.secondary_focused;
        if window == old_focused { return; }
        let focusable = window.map_or(true, |x| self.arena[x.0].widget.secondary_focusable());
        if !focusable { return; }
        window.map(|x| x.raise(self, Event::Cmd(CMD_GOT_SECONDARY_FOCUS), app));
        self.secondary_focused = window;
        old_focused.map(|x| x.raise(self, Event::Cmd(CMD_LOST_SECONDARY_FOCUS), app));
        window.map(|x| x.bring_into_view(self));
    }
}