use crate::direction;
use crate::event::{AnyCb, Event, EventResult, Key};
use crate::rect::Rect;
use crate::view::{IntoBoxedView, Selector, SizeCache, View};
use crate::Printer;
use crate::Vec2;
use crate::With;
use crate::XY;
use log::debug;
use std::cmp::min;
use std::ops::Deref;
pub struct LinearLayout {
children: Vec<Child>,
orientation: direction::Orientation,
focus: usize,
cache: Option<XY<SizeCache>>,
}
struct Child {
view: Box<dyn View>,
required_size: Vec2,
last_size: Vec2,
weight: usize,
}
impl Child {
fn required_size(&mut self, req: Vec2) -> Vec2 {
self.required_size = self.view.required_size(req);
self.required_size
}
fn layout(&mut self, size: Vec2) {
self.last_size = size;
self.view.layout(size);
}
fn as_view(&self) -> &dyn View {
&*self.view
}
}
struct ChildIterator<I> {
inner: I,
offset: usize,
available: usize,
orientation: direction::Orientation,
}
struct ChildItem<T> {
child: T,
offset: usize,
length: usize,
}
impl<T> ChildIterator<T> {
fn new(
inner: T,
orientation: direction::Orientation,
available: usize,
) -> Self {
ChildIterator {
inner,
available,
orientation,
offset: 0,
}
}
}
impl<'a, T: Deref<Target = Child>, I: Iterator<Item = T>> Iterator
for ChildIterator<I>
{
type Item = ChildItem<T>;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().map(|child| {
let offset = self.offset;
let length = min(
self.available,
*child.required_size.get(self.orientation),
);
self.available = self.available.saturating_sub(length);
self.offset += length;
ChildItem {
offset,
length,
child,
}
})
}
}
fn cap<'a, I: Iterator<Item = &'a mut usize>>(iter: I, max: usize) {
let mut available = max;
for item in iter {
if *item > available {
*item = available;
}
available -= *item;
}
}
impl LinearLayout {
pub fn new(orientation: direction::Orientation) -> Self {
LinearLayout {
children: Vec::new(),
orientation,
focus: 0,
cache: None,
}
}
pub fn set_weight(&mut self, i: usize, weight: usize) {
self.children[i].weight = weight;
}
pub fn weight(mut self, weight: usize) -> Self {
self.children.last_mut().unwrap().weight = weight;
self
}
pub fn child<V: IntoBoxedView + 'static>(self, view: V) -> Self {
self.with(|s| s.add_child(view))
}
pub fn add_child<V: IntoBoxedView + 'static>(&mut self, view: V) {
self.children.push(Child {
view: view.as_boxed_view(),
required_size: Vec2::zero(),
last_size: Vec2::zero(),
weight: 0,
});
self.invalidate();
}
pub fn insert_child<V: IntoBoxedView + 'static>(
&mut self,
i: usize,
view: V,
) {
self.children.insert(
i,
Child {
view: view.as_boxed_view(),
required_size: Vec2::zero(),
last_size: Vec2::zero(),
weight: 0,
},
);
self.invalidate();
}
pub fn swap_children(&mut self, i: usize, j: usize) {
self.children.swap(i, j);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn get_focus_index(&self) -> usize {
self.focus
}
pub fn set_focus_index(&mut self, index: usize) -> Result<(), ()> {
if self
.children
.get_mut(index)
.map(|child| child.view.take_focus(direction::Direction::none()))
.unwrap_or(false)
{
self.focus = index;
Ok(())
} else {
Err(())
}
}
fn invalidate(&mut self) {
self.cache = None;
}
pub fn vertical() -> Self {
LinearLayout::new(direction::Orientation::Vertical)
}
pub fn horizontal() -> Self {
LinearLayout::new(direction::Orientation::Horizontal)
}
pub fn get_child(&self, i: usize) -> Option<&dyn View> {
self.children.get(i).map(|child| &*child.view)
}
pub fn get_child_mut(&mut self, i: usize) -> Option<&mut dyn View> {
self.invalidate();
self.children.get_mut(i).map(|child| &mut *child.view)
}
pub fn remove_child(&mut self, i: usize) -> Option<Box<dyn View>> {
if i < self.children.len() {
self.invalidate();
if self.focus > i
|| (self.focus != 0 && self.focus == self.children.len() - 1)
{
self.focus -= 1;
}
Some(self.children.remove(i).view)
} else {
None
}
}
pub fn find_child_from_name(&mut self, name: &str) -> Option<usize> {
let selector = Selector::Name(name);
for (i, c) in self.children.iter_mut().enumerate() {
let mut found = false;
c.view.call_on_any(&selector, &mut |_| found = true);
if found {
return Some(i);
}
}
None
}
fn get_cache(&self, req: Vec2) -> Option<Vec2> {
match self.cache {
None => None,
Some(ref cache) => {
if cache.zip_map(req, SizeCache::accept).both()
&& self.children_are_sleeping()
{
Some(cache.map(|s| s.value))
} else {
None
}
}
}
}
fn children_are_sleeping(&self) -> bool {
!self
.children
.iter()
.map(Child::as_view)
.any(View::needs_relayout)
}
fn iter_mut<'a>(
&'a mut self,
from_focus: bool,
source: direction::Relative,
) -> Box<dyn Iterator<Item = (usize, &mut Child)> + 'a> {
match source {
direction::Relative::Front => {
let start = if from_focus { self.focus } else { 0 };
Box::new(self.children.iter_mut().enumerate().skip(start))
}
direction::Relative::Back => {
let end = if from_focus {
self.focus + 1
} else {
self.children.len()
};
Box::new(self.children[..end].iter_mut().enumerate().rev())
}
}
}
fn move_focus(&mut self, source: direction::Direction) -> EventResult {
source
.relative(self.orientation)
.and_then(|rel| {
self.iter_mut(true, rel)
.skip(1)
.filter_map(|p| try_focus(p, source))
.next()
})
.map_or(EventResult::Ignored, |i| {
self.focus = i;
EventResult::Consumed(None)
})
}
fn check_focus_grab(&mut self, event: &Event) {
if let Event::Mouse {
offset,
position,
event,
} = *event
{
if !event.grabs_focus() {
return;
}
let position = match position.checked_sub(offset) {
None => return,
Some(pos) => pos,
};
let position = *position.get(self.orientation);
for (i, item) in ChildIterator::new(
self.children.iter_mut(),
self.orientation,
usize::max_value(),
)
.enumerate()
{
let child_size = item.child.last_size.get(self.orientation);
if item.offset + child_size > position {
if item.child.view.take_focus(direction::Direction::none())
{
self.focus = i;
}
return;
}
}
}
}
}
fn try_focus(
(i, child): (usize, &mut Child),
source: direction::Direction,
) -> Option<usize> {
if child.view.take_focus(source) {
Some(i)
} else {
None
}
}
impl View for LinearLayout {
fn draw(&self, printer: &Printer) {
for (i, item) in ChildIterator::new(
self.children.iter(),
self.orientation,
*printer.size.get(self.orientation),
)
.enumerate()
{
let printer = &printer
.offset(self.orientation.make_vec(item.offset, 0))
.cropped(item.child.last_size)
.focused(i == self.focus);
item.child.view.draw(printer);
}
}
fn needs_relayout(&self) -> bool {
if self.cache.is_none() {
return true;
}
!self.children_are_sleeping()
}
fn layout(&mut self, size: Vec2) {
if self.get_cache(size).is_none() {
self.required_size(size);
}
let o = self.orientation;
for item in
ChildIterator::new(self.children.iter_mut(), o, *size.get(o))
{
let size = size.with_axis(o, item.length);
item.child.layout(size);
}
}
fn required_size(&mut self, req: Vec2) -> Vec2 {
if let Some(size) = self.get_cache(req) {
return size;
}
debug!("Req: {:?}", req);
let ideal_sizes: Vec<Vec2> = self
.children
.iter_mut()
.map(|c| c.required_size(req))
.collect();
debug!("Ideal sizes: {:?}", ideal_sizes);
let ideal = self.orientation.stack(ideal_sizes.iter());
debug!("Ideal result: {:?}", ideal);
if ideal.fits_in(req) {
self.cache = Some(SizeCache::build(ideal, req));
return ideal;
}
let budget_req = req.with_axis(self.orientation, 1);
debug!("Budget req: {:?}", budget_req);
let min_sizes: Vec<Vec2> = self
.children
.iter_mut()
.map(|c| c.required_size(budget_req))
.collect();
let desperate = self.orientation.stack(min_sizes.iter());
debug!("Min sizes: {:?}", min_sizes);
debug!("Desperate: {:?}", desperate);
let orientation = self.orientation;
if desperate.get(orientation) > req.get(orientation) {
cap(
self.children
.iter_mut()
.map(|c| c.required_size.get_mut(orientation)),
*req.get(self.orientation),
);
debug!("Seriously? {:?} > {:?}???", desperate, req);
self.cache = None;
return desperate;
}
let mut available =
self.orientation.get(&(req.saturating_sub(desperate)));
debug!("Available: {:?}", available);
let mut overweight: Vec<(usize, usize)> = ideal_sizes
.iter()
.map(|v| self.orientation.get(v))
.zip(min_sizes.iter().map(|v| self.orientation.get(v)))
.map(|(a, b)| a.saturating_sub(b))
.enumerate()
.collect();
debug!("Overweight: {:?}", overweight);
overweight.sort_by_key(|&(_, weight)| weight);
let mut allocations = vec![0; overweight.len()];
for (i, &(j, weight)) in overweight.iter().enumerate() {
let remaining = overweight.len() - i;
let budget = available / remaining;
let spent = min(budget, weight);
allocations[j] = spent;
available -= spent;
}
debug!("Allocations: {:?}", allocations);
let final_lengths: Vec<Vec2> = min_sizes
.iter()
.map(|v| self.orientation.get(v))
.zip(allocations.iter())
.map(|(a, b)| a + b)
.map(|l| req.with_axis(self.orientation, l))
.collect();
debug!("Final sizes: {:?}", final_lengths);
let final_sizes: Vec<Vec2> = self
.children
.iter_mut()
.enumerate()
.map(|(i, c)| c.required_size(final_lengths[i]))
.collect();
debug!("Final sizes2: {:?}", final_sizes);
let compromise = self.orientation.stack(final_sizes.iter());
self.cache = Some(SizeCache::build(compromise, req));
compromise
}
fn take_focus(&mut self, source: direction::Direction) -> bool {
let rel = source.relative(self.orientation);
let mut get_next_focus = || {
self.iter_mut(
rel.is_none(),
rel.unwrap_or(direction::Relative::Front),
)
.filter_map(|p| try_focus(p, source))
.next()
};
if let Some(i) = get_next_focus() {
self.focus = i;
true
} else {
false
}
}
fn on_event(&mut self, event: Event) -> EventResult {
if self.is_empty() {
return EventResult::Ignored;
}
self.check_focus_grab(&event);
let result = {
let mut iterator = ChildIterator::new(
self.children.iter_mut(),
self.orientation,
usize::max_value(),
);
let item = iterator.nth(self.focus).unwrap();
let offset = self.orientation.make_vec(item.offset, 0);
item.child.view.on_event(event.relativized(offset))
};
match result {
EventResult::Ignored => match event {
Event::Shift(Key::Tab) if self.focus > 0 => {
self.move_focus(direction::Direction::back())
}
Event::Key(Key::Tab)
if self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::front())
}
Event::Key(Key::Left)
if self.orientation
== direction::Orientation::Horizontal
&& self.focus > 0 =>
{
self.move_focus(direction::Direction::right())
}
Event::Key(Key::Up)
if self.orientation
== direction::Orientation::Vertical
&& self.focus > 0 =>
{
self.move_focus(direction::Direction::down())
}
Event::Key(Key::Right)
if self.orientation
== direction::Orientation::Horizontal
&& self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::left())
}
Event::Key(Key::Down)
if self.orientation
== direction::Orientation::Vertical
&& self.focus + 1 < self.children.len() =>
{
self.move_focus(direction::Direction::up())
}
_ => EventResult::Ignored,
},
res => res,
}
}
fn call_on_any<'a>(
&mut self,
selector: &Selector<'_>,
callback: AnyCb<'a>,
) {
for child in &mut self.children {
child.view.call_on_any(selector, callback);
}
}
fn focus_view(&mut self, selector: &Selector<'_>) -> Result<(), ()> {
for (i, child) in self.children.iter_mut().enumerate() {
if child.view.focus_view(selector).is_ok() {
self.focus = i;
return Ok(());
}
}
Err(())
}
fn important_area(&self, _: Vec2) -> Rect {
if self.is_empty() {
return Rect::from((0, 0));
}
let item = {
let mut iterator = ChildIterator::new(
self.children.iter(),
self.orientation,
usize::max_value(),
);
iterator.nth(self.focus).unwrap()
};
let offset = self.orientation.make_vec(item.offset, 0);
let rect = item.child.view.important_area(item.child.last_size);
rect + offset
}
}