diff options
-rw-r--r-- | helix-view/src/tree.rs | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/helix-view/src/tree.rs b/helix-view/src/tree.rs new file mode 100644 index 00000000..5c583cbd --- /dev/null +++ b/helix-view/src/tree.rs @@ -0,0 +1,173 @@ +use crate::View; +use slotmap::{DefaultKey as Key, HopSlotMap}; +use tui::layout::Rect; + +// the dimensions are recomputed on windo resize/tree change. +// +pub struct Tree { + root: Key, + // (container, index inside the container) + current: (Key, usize), + pub focus: Key, + fullscreen: bool, + area: Rect, + + nodes: HopSlotMap<Key, Node>, + + // used for traversals + stack: Vec<(Key, Rect)>, +} + +pub enum Node { + View(Box<View>), + Container(Box<Container>), +} + +impl Node { + pub fn container(area: Rect) -> Self { + Self::Container(Box::new(Container::new())) + } + + pub fn view(view: View) -> Self { + Self::View(Box::new(view)) + } +} + +// TODO: screen coord to container + container coordinate helpers + +pub enum Layout { + Horizontal, + Vertical, + // could explore stacked/tabbed +} + +pub struct Container { + layout: Layout, + children: Vec<Key>, + area: Rect, +} + +impl Container { + pub fn new() -> Self { + Self { + layout: Layout::Horizontal, + children: Vec::new(), + area: Rect::default(), + } + } +} + +impl Tree { + pub fn new(area: Rect) -> Self { + let root = Node::container(area); + let mut nodes = HopSlotMap::new(); + let root = nodes.insert(root); + + Self { + root, + current: (root, 0), + focus: Key::default(), + fullscreen: false, + area, + nodes, + stack: Vec::new(), + } + } + + pub fn insert(&mut self, view: View) -> Key { + let node = self.nodes.insert(Node::view(view)); + let (id, pos) = self.current; + let container = match &mut self.nodes[id] { + Node::Container(container) => container, + _ => unreachable!(), + }; + + // insert node after the current item if there is children already + let pos = if container.children.is_empty() { + pos + } else { + pos + 1 + }; + + container.children.insert(pos, node); + // focus the new node + self.current = (id, pos); + self.focus = node; + + // recalculate all the sizes + self.traverse(); + + node + } + + pub fn views(&mut self) -> impl Iterator<Item = (&mut View, bool)> { + let focus = self.focus; + self.nodes + .iter_mut() + .filter_map(move |(key, node)| match node { + Node::View(view) => Some((view.as_mut(), focus == key)), + Node::Container(..) => None, + }) + } + + pub fn get(&self, index: Key) -> &View { + match &self.nodes[index] { + Node::View(view) => view, + _ => unreachable!(), + } + } + + pub fn get_mut(&mut self, index: Key) -> &mut View { + match &mut self.nodes[index] { + Node::View(view) => view, + _ => unreachable!(), + } + } + + pub fn resize(&mut self, area: Rect) { + self.area = area; + self.traverse(); + } + + pub fn traverse(&mut self) { + self.stack.push((self.root, self.area)); + + // take the area + // fetch the node + // a) node is view, give it whole area + // b) node is container, calculate areas for each child and push them on the stack + + while let Some((key, area)) = self.stack.pop() { + let node = &mut self.nodes[key]; + + match node { + Node::View(view) => { + // debug!!("setting view area {:?}", area); + view.area = area; + } // TODO: call f() + Node::Container(container) => { + // debug!!("setting container area {:?}", area); + container.area = area; + + match container.layout { + Layout::Vertical => unimplemented!(), + Layout::Horizontal => { + let len = container.children.len() as u16; + + let width = area.width / len; + + let mut child_x = area.x; + + for (_i, child) in container.children.iter().enumerate() { + let area = Rect::new(child_x, area.y, width, area.height); + child_x += width; + + self.stack.push((*child, area)); + } + } + } + } + } + } + } +} |