use indexmap::IndexSet;
use thunderdome::Index as NodeIndex;
use crate::{Alignment, NodeCache, Rect, UiNode, UiTree};
pub struct HStack {
align: (Alignment, Alignment),
children: IndexSet<NodeIndex>,
pub spacing: f32,
}
impl HStack {
pub fn new() -> Self {
Self {
align: (Alignment::Full, Alignment::Full),
children: IndexSet::new(),
spacing: 0.0,
}
}
pub fn with_child(mut self, index: NodeIndex) -> Self {
self.children.insert(index);
self
}
pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
self.align = align;
self
}
pub fn with_spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn add_child(&mut self, index: NodeIndex) {
self.children.insert(index);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn remove_child(&mut self, index: usize, tree: &mut UiTree) -> bool {
let Some(ti) = self.children.shift_remove_index(index) else {
return false;
};
if tree.get_node(ti).is_none() {
return false;
}
tree.remove_node(ti);
true
}
pub fn set_child_position(&mut self, index: usize, position: usize) -> bool {
self.children.move_index(index, position);
true
}
pub fn get_child_index(&self, index: usize) -> Option<NodeIndex> {
self.children.get_index(index).copied()
}
}
impl Default for HStack {
fn default() -> Self {
Self::new()
}
}
impl UiNode for HStack {
fn get_align(&self) -> (Alignment, Alignment) {
self.align
}
fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
(&mut self.align.0, &mut self.align.1)
}
fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
if self.children.is_empty() {
return (0.0, 0.0);
}
let mut w = 0.0f32;
let mut h = 0.0f32;
for child in &self.children {
let child = tree.get_cache(*child).expect("Child not in cache");
let (cw, ch) = child.min_size;
w += cw + self.spacing;
h = h.max(ch);
}
(w - self.spacing, h)
}
fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
let mut child_rects = Vec::with_capacity(self.children.len());
let mut x = cache.rect.x;
for child in &self.children {
let child_min = tree.get_cache(*child).expect("Child not in cache").min_size;
let child = tree.get_node(*child).expect("Child not in arena");
let space = Rect::new(x, cache.rect.y, child_min.0, cache.rect.h)
.align(child.get_align(), child_min);
child_rects.push(space);
x += child_min.0 + self.spacing;
}
child_rects
}
fn get_children(&self) -> Vec<NodeIndex> {
self.children.iter().copied().collect()
}
}
pub struct VStack {
align: (Alignment, Alignment),
children: IndexSet<NodeIndex>,
spacing: f32,
}
impl VStack {
pub fn new() -> Self {
Self {
align: (Alignment::Full, Alignment::Full),
children: IndexSet::new(),
spacing: 0.0,
}
}
pub fn with_child(mut self, index: NodeIndex) -> Self {
self.children.insert(index);
self
}
pub fn with_align(mut self, align: (Alignment, Alignment)) -> Self {
self.align = align;
self
}
pub fn with_spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn set_spacing(&mut self, spacing: f32) {
self.spacing = spacing;
}
pub fn add_child(&mut self, index: NodeIndex) {
self.children.insert(index);
}
pub fn len(&self) -> usize {
self.children.len()
}
pub fn is_empty(&self) -> bool {
self.children.is_empty()
}
pub fn remove_child(&mut self, index: usize, tree: &mut UiTree) -> bool {
let Some(ti) = self.children.shift_remove_index(index) else {
return false;
};
if tree.get_node(ti).is_none() {
return false;
}
tree.remove_node(ti);
true
}
pub fn set_child_position(&mut self, index: usize, position: usize) -> bool {
self.children.move_index(index, position);
true
}
pub fn get_child_index(&self, index: usize) -> Option<NodeIndex> {
self.children.get_index(index).copied()
}
}
impl Default for VStack {
fn default() -> Self {
Self::new()
}
}
impl UiNode for VStack {
fn get_align(&self) -> (Alignment, Alignment) {
self.align
}
fn get_align_mut(&mut self) -> (&mut Alignment, &mut Alignment) {
(&mut self.align.0, &mut self.align.1)
}
fn calculate_min_size(&self, tree: &UiTree) -> (f32, f32) {
if self.children.is_empty() {
return (0.0, 0.0);
}
let mut w = 0.0f32;
let mut h = 0.0f32;
for child in &self.children {
let child = tree.get_cache(*child).expect("Child not in cache");
let (cw, ch) = child.min_size;
w = w.max(cw);
h += ch + self.spacing;
}
(w, h - self.spacing)
}
fn calculate_rects(&self, cache: &NodeCache, tree: &UiTree) -> Vec<Rect> {
let mut child_rects = Vec::with_capacity(self.children.len());
let mut y = cache.rect.y;
for child in &self.children {
let child_min = tree.get_cache(*child).expect("Child not in cache").min_size;
let child = tree.get_node(*child).expect("Child not in arena");
let space = Rect::new(cache.rect.x, y, cache.rect.w, child_min.1)
.align(child.get_align(), child_min);
child_rects.push(space);
y += child_min.1 + self.spacing;
}
child_rects
}
fn get_children(&self) -> Vec<NodeIndex> {
self.children.iter().copied().collect()
}
}