use std::marker::PhantomData;
use euclid::Size2D;
use keyboard_types::Key;
use crate::{
event::Event, unit::Cell, view::element, Printer, View as ViewTrait,
};
mod flex;
pub mod layout;
use flex::Flex;
pub enum Row {}
pub enum Column {}
#[derive(Default)]
pub struct State {
focused: Option<usize>,
}
impl State {
pub fn focus_prev(&mut self) {
self.focused = self.focused.map(|x| x.wrapping_sub(1));
}
pub fn focus_next(&mut self) {
self.focused = self.focused.map(|x| x.wrapping_add(1));
}
}
pub struct View<'a, T, M, D> {
inners: Vec<(element::View<'a, T, M>, layout::Item)>,
state: &'a mut State,
layout: layout::Container,
direction: PhantomData<D>,
}
impl<'a, T, M, D> View<'a, T, M, D>
where
Self: Flex<T, M>,
{
pub fn new(state: &'a mut State) -> Self {
Self {
inners: Vec::new(),
state,
layout: Default::default(),
direction: PhantomData,
}
}
pub fn layout(mut self, layout: layout::Container) -> Self {
self.layout = layout;
self
}
pub fn push<V>(self, view: V) -> Self
where
V: ViewTrait<T, M> + 'a,
{
self.push_with_layout(view, Default::default())
}
pub fn push_with_layout<V>(mut self, view: V, layout: layout::Item) -> Self
where
V: ViewTrait<T, M> + 'a,
{
self.inners.push((element::new(view), layout));
if self.state.focused.is_none() {
self.state.focused = Some(0);
}
self
}
fn focused_child(&self) -> Option<usize> {
let mut interactive_idx = None;
let interactive_count =
self.inners.iter().filter(|(i, _)| i.interactive()).count();
if interactive_count == 0 {
return None;
}
for (i, inner) in self.inners.iter().map(|(i, _)| i).enumerate() {
if inner.interactive() {
match interactive_idx.as_mut() {
None => interactive_idx = Some(0),
Some(i) => *i += 1,
}
}
let state_calc_agree = self
.state
.focused
.map(|x| x % interactive_count)
.eq(&interactive_idx);
if interactive_idx.is_some() && state_calc_agree {
return Some(i);
}
}
None
}
fn flex_bases(&self, main_axis_length: u16) -> Vec<(u16, u16)> {
let mut bases: Vec<_> = self
.inners
.iter()
.map(|(inner, _layout)| {
Self::axes_from_size(Self::inner_main_axis(inner))
})
.collect();
let diff = i32::from(main_axis_length)
- i32::from(bases.iter().map(|(m, _c)| m).sum::<u16>());
match diff {
d if d > 0 => {
let all_growable =
self.inners.iter().all(|(_, l)| l.grow.is_some());
let growable_space = if all_growable {
main_axis_length
} else {
let consumed_by_basis: u16 = bases
.iter()
.zip(self.inners.iter())
.filter_map(|((m, _), (_, l))| match l.grow {
Some(_) => None,
None => Some(m),
})
.sum();
main_axis_length - consumed_by_basis
};
let total_grow_factor: u8 =
self.inners.iter().filter_map(|(_, l)| l.grow).sum();
let grow_unit =
growable_space.checked_div(total_grow_factor as u16);
let basis_grow_factor_iter = bases
.iter_mut()
.map(|(m, _c)| m)
.zip(self.inners.iter().map(|(_, l)| l.grow));
for (basis, grow_factor) in basis_grow_factor_iter {
if let (Some(grow_unit), Some(grow_factor)) =
(grow_unit, grow_factor)
{
*basis = grow_unit * grow_factor as u16;
}
}
}
0 => (),
_ => todo!(),
}
bases
}
}
pub fn row<T, M>(state: &mut State) -> View<'_, T, M, Row> {
View::new(state)
}
pub fn column<T, M>(state: &mut State) -> View<'_, T, M, Column> {
View::new(state)
}
impl<T, M, D> ViewTrait<T, M> for View<'_, T, M, D>
where
Self: Flex<T, M>,
M: 'static,
{
fn draw(&self, printer: &Printer, focused: bool) {
let focused_idx = self.focused_child();
let mut main_axis_pos_acc = 0;
let (printer_main_axis, printer_cross_axis) =
Self::axes_from_size(printer.size());
let bases = self.flex_bases(printer_main_axis);
let i_inner_basis_iter =
self.inners.iter().map(|(i, _)| i).zip(bases.iter()).enumerate();
for (i, (inner, (basis_main, basis_cross))) in i_inner_basis_iter {
let focused = focused_idx
.filter(|_| focused)
.map(|x| x == i)
.unwrap_or(false);
inner.draw(
&printer
.to_sub_area(Self::new_element_rect(
main_axis_pos_acc,
*basis_main,
match self.layout.align_items {
layout::AlignItems::Stretch => printer_cross_axis,
layout::AlignItems::Start => *basis_cross,
},
))
.unwrap(),
focused,
);
main_axis_pos_acc += *basis_main;
}
}
fn width(&self) -> Size2D<u16, Cell> {
Flex::width(self)
}
fn height(&self) -> Size2D<u16, Cell> {
Flex::height(self)
}
fn layout(&self, constraint: Size2D<u16, Cell>) -> Size2D<u16, Cell> {
let (constraint_main_axis, _constraint_cross_axis) =
Self::axes_from_size(constraint);
let mut min_main_axis = 0;
let mut min_cross_axis = 0;
for (basis_main, basis_cross) in self.flex_bases(constraint_main_axis) {
min_main_axis += basis_main;
min_cross_axis = min_cross_axis.max(basis_cross);
}
Self::axes_to_size(min_main_axis, min_cross_axis)
}
fn event(
&mut self,
event: &Event<T>,
focused: bool,
) -> Box<dyn Iterator<Item = M>> {
if focused {
if let Event::Key(k) = event {
if k.key == Key::Tab {
self.state.focus_next();
}
}
}
let mut messages = Vec::new();
let focused_idx = self.focused_child();
for (i, inner) in self.inners.iter_mut().map(|(i, _)| i).enumerate() {
let focused = focused_idx
.filter(|_| focused)
.map(|x| x == i)
.unwrap_or(false);
messages.extend(inner.event(event, focused));
}
Box::new(messages.into_iter())
}
fn interactive(&self) -> bool {
self.inners.iter().any(|(inner, _)| inner.interactive())
}
}