use crate::{
direction::{Absolute, Direction, Relative},
event::{AnyCb, Event, EventResult, Key},
rect::Rect,
view::{IntoBoxedView, Selector, ViewNotFound},
{Printer, Vec2, View, With},
};
pub struct FixedLayout {
children: Vec<Child>,
focus: usize,
}
struct Child {
view: Box<dyn View>,
position: Rect,
}
new_default!(FixedLayout);
impl FixedLayout {
pub fn new() -> Self {
FixedLayout {
children: Vec::new(),
focus: 0,
}
}
pub fn child<V: IntoBoxedView>(self, position: Rect, view: V) -> Self {
self.with(|s| s.add_child(position, view))
}
pub fn add_child<V: IntoBoxedView>(&mut self, position: Rect, view: V) {
self.children.push(Child {
view: view.into_boxed_view(),
position,
});
}
pub fn get_focus_index(&self) -> usize {
self.focus
}
pub fn set_focus_index(
&mut self,
index: usize,
) -> Result<(), ViewNotFound> {
if self
.children
.get_mut(index)
.map(|child| child.view.take_focus(Direction::none()))
.unwrap_or(false)
{
self.focus = index;
Ok(())
} else {
Err(ViewNotFound)
}
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn get_child(&self, i: usize) -> Option<&dyn View> {
self.children.get(i).map(|c| &*c.view)
}
pub fn get_child_mut(&mut self, i: usize) -> Option<&mut dyn View> {
self.children.get_mut(i).map(|c| &mut *c.view)
}
pub fn set_child_position(&mut self, i: usize, position: Rect) {
self.children[i].position = position;
}
pub fn remove_child(&mut self, i: usize) -> Option<Box<dyn View>> {
if i >= self.len() {
return None;
}
if self.focus > i
|| (self.focus != 0 && self.focus == self.children.len() - 1)
{
self.focus -= 1;
}
Some(self.children.remove(i).view)
}
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 iter_mut<'a>(
source: Direction,
children: &'a mut [Child],
) -> Box<dyn Iterator<Item = (usize, &mut Child)> + 'a> {
let children = children.iter_mut().enumerate();
match source {
Direction::Rel(Relative::Front) => Box::new(children),
Direction::Rel(Relative::Back) => Box::new(children.rev()),
Direction::Abs(abs) => {
let mut children: Vec<_> = children.collect();
children.sort_by_key(|(_, c)| c.position.edge(abs));
Box::new(children.into_iter())
}
}
}
fn circular_mut<'a>(
start: usize,
children: &'a mut [Child],
) -> impl Iterator<Item = (usize, &mut Child)> + 'a {
let (head, tail) = children.split_at_mut(start);
let head = head.iter_mut().enumerate();
let tail = tail
.iter_mut()
.enumerate()
.map(move |(i, c)| (i + start, c));
tail.chain(head)
}
fn move_focus_rel(&mut self, target: Relative) -> EventResult {
let source = Direction::Rel(target.swap());
for (i, c) in
Self::iter_mut(source, &mut self.children).skip(self.focus + 1)
{
if c.view.take_focus(source) {
self.focus = i;
return EventResult::Consumed(None);
}
}
EventResult::Ignored
}
fn move_focus_abs(&mut self, target: Absolute) -> EventResult {
let source = Direction::Abs(target.opposite());
let (orientation, rel) = target.split();
fn intersects(a: (usize, usize), b: (usize, usize)) -> bool {
a.1 >= b.0 && a.0 <= b.1
}
let current_position = self.children[self.focus].position;
let current_side = current_position.side(orientation.swap());
let current_edge = current_position.edge(target);
let children =
Self::iter_mut(source, &mut self.children).filter(|(_, c)| {
Some(rel)
== Relative::a_to_b(current_edge, c.position.edge(target))
&& intersects(
c.position.side(orientation.swap()),
current_side,
)
});
for (i, c) in children {
if c.view.take_focus(source) {
self.focus = i;
return EventResult::Consumed(None);
}
}
EventResult::Ignored
}
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,
};
for (i, child) in self.children.iter_mut().enumerate() {
if child.position.contains(position)
&& child.view.take_focus(Direction::none())
{
self.focus = i;
}
}
}
}
}
impl View for FixedLayout {
fn draw(&self, printer: &Printer) {
for child in &self.children {
child.view.draw(&printer.windowed(child.position));
}
}
fn layout(&mut self, _size: Vec2) {
for child in &mut self.children {
child.view.layout(child.position.size());
}
}
fn on_event(&mut self, event: Event) -> EventResult {
if self.is_empty() {
return EventResult::Ignored;
}
self.check_focus_grab(&event);
let child = &mut self.children[self.focus];
let result = child
.view
.on_event(event.relativized(child.position.top_left()));
match result {
EventResult::Ignored => match event {
Event::Shift(Key::Tab) => self.move_focus_rel(Relative::Front),
Event::Key(Key::Tab) => self.move_focus_rel(Relative::Back),
Event::Key(Key::Left) => self.move_focus_abs(Absolute::Left),
Event::Key(Key::Right) => self.move_focus_abs(Absolute::Right),
Event::Key(Key::Up) => self.move_focus_abs(Absolute::Up),
Event::Key(Key::Down) => self.move_focus_abs(Absolute::Down),
_ => EventResult::Ignored,
},
res => res,
}
}
fn important_area(&self, size: Vec2) -> Rect {
if self.is_empty() {
return Rect::from_size((0, 0), size);
}
let child = &self.children[self.focus];
child.view.important_area(child.position.size())
+ child.position.top_left()
}
fn required_size(&mut self, _constraint: Vec2) -> Vec2 {
self.children
.iter()
.map(|c| c.position.bottom_right() + (1, 1))
.fold(Vec2::zero(), Vec2::max)
}
fn take_focus(&mut self, source: Direction) -> bool {
match source {
Direction::Abs(Absolute::None) => {
for (i, c) in
Self::circular_mut(self.focus, &mut self.children)
{
if c.view.take_focus(source) {
self.focus = i;
return true;
}
}
false
}
source => {
for (i, c) in Self::iter_mut(source, &mut self.children) {
if c.view.take_focus(source) {
self.focus = i;
return true;
}
}
false
}
}
}
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<(), ViewNotFound> {
for (i, child) in self.children.iter_mut().enumerate() {
if child.view.focus_view(selector).is_ok() {
self.focus = i;
return Ok(());
}
}
Err(ViewNotFound)
}
}