diff options
Diffstat (limited to 'src/tuice/component/base/row.rs')
-rw-r--r-- | src/tuice/component/base/row.rs | 107 |
1 files changed, 90 insertions, 17 deletions
diff --git a/src/tuice/component/base/row.rs b/src/tuice/component/base/row.rs index 93c5b317..75c5177c 100644 --- a/src/tuice/component/base/row.rs +++ b/src/tuice/component/base/row.rs @@ -1,6 +1,9 @@ +use itertools::izip; use tui::{backend::Backend, layout::Rect, Frame}; -use crate::tuice::{Bounds, Event, FlexElement, LayoutNode, Size, Status, TmpComponent}; +use crate::tuice::{ + Bounds, DrawContext, Element, Event, FlexElement, LayoutNode, Size, Status, TmpComponent, +}; #[derive(Default)] pub struct Row<'a, Message> { @@ -18,23 +21,35 @@ impl<'a, Message> Row<'a, Message> { } } - pub fn with_child(mut self) -> Self { + pub fn with_child<E>(mut self, child: E) -> Self + where + E: Into<Element<'a, Message>>, + { + self.children.push(FlexElement::with_no_flex(child.into())); self } - pub fn with_flex_child(mut self) -> Self { + pub fn with_flex_child<E>(mut self, child: E, flex: u16) -> Self + where + E: Into<Element<'a, Message>>, + { + self.children + .push(FlexElement::with_flex(child.into(), flex)); self } } impl<'a, Message> TmpComponent<Message> for Row<'a, Message> { - fn draw<B>(&mut self, area: Rect, frame: &mut Frame<'_, B>) + fn draw<B>(&mut self, context: DrawContext<'_>, frame: &mut Frame<'_, B>) where B: Backend, { - self.children.iter_mut().for_each(|child| { - child.draw(area, frame); - }) + self.children + .iter_mut() + .zip(context.children()) + .for_each(|(child, child_node)| { + child.draw(child_node, frame); + }); } fn on_event(&mut self, _area: Rect, _event: Event, _messages: &mut Vec<Message>) -> Status { @@ -43,17 +58,75 @@ impl<'a, Message> TmpComponent<Message> for Row<'a, Message> { fn layout(&self, bounds: Bounds, node: &mut LayoutNode) -> Size { let mut remaining_bounds = bounds; + let mut children = vec![LayoutNode::default(); self.children.len()]; + let mut inflexible_children_indexes = vec![]; + let mut offsets = vec![]; + let mut current_x = 0; + let mut current_y = 0; + let mut sizes = Vec::with_capacity(self.children.len()); + let mut current_size = Size::default(); - let child_nodes: Vec<LayoutNode> = self - .children + let mut get_child_size = |child: &FlexElement<'_, Message>, + child_node: &mut LayoutNode, + remaining_bounds: &mut Bounds| { + let size = child.layout(*remaining_bounds, child_node); + current_size += size; + remaining_bounds.shrink_size(size); + offsets.push((current_x, current_y)); + current_x += size.width; + current_y += size.height; + + size + }; + + // We handle inflexible children first, then distribute all remaining + // space to flexible children. + self.children .iter() - .map(|child| { - let mut child_node = LayoutNode::default(); - let size = child.layout(remaining_bounds, &mut child_node); - child_node - }) - .collect(); - - todo!() + .zip(children.iter_mut()) + .enumerate() + .for_each(|(index, (child, child_node))| { + if child.flex == 0 && remaining_bounds.has_space() { + let size = get_child_size(child, child_node, &mut remaining_bounds); + sizes.push(size); + } else { + inflexible_children_indexes.push(index); + sizes.push(Size::default()); + } + }); + + inflexible_children_indexes.into_iter().for_each(|index| { + // The index accesses are safe by above definitions, so we can use unsafe operations. + // If you EVER make changes to above, ensure this invariant still holds! + let child = unsafe { self.children.get_unchecked(index) }; + let child_node = unsafe { children.get_unchecked_mut(index) }; + let size = unsafe { sizes.get_unchecked_mut(index) }; + + *size = get_child_size(child, child_node, &mut remaining_bounds); + }); + + // If there is still remaining space after, distribute the rest if + // appropriate (e.x. current_size is too small for the bounds). + if current_size.width < bounds.min_width { + // For now, we'll cheat and just set it to be equal. + current_size.width = bounds.min_width; + } + if current_size.height < bounds.min_height { + // For now, we'll cheat and just set it to be equal. + current_size.height = bounds.min_height; + } + + // Now that we're done determining sizes, convert all children into the appropriate + // layout nodes. Remember - parents determine children, and so, we determine + // children here! + izip!(sizes, offsets, children.iter_mut()).for_each( + |(size, offset, child): (Size, (u16, u16), &mut LayoutNode)| { + let rect = Rect::new(offset.0, offset.1, size.width, size.height); + child.rect = rect; + }, + ); + node.children = children; + + current_size } } |