use std::collections::HashMap;
type ComponentId = u64;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutConstraints {
pub min_width: f32,
pub max_width: f32,
pub min_height: f32,
pub max_height: f32,
}
impl Default for LayoutConstraints {
fn default() -> Self {
Self {
min_width: 0.0,
max_width: f32::INFINITY,
min_height: 0.0,
max_height: f32::INFINITY,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct LayoutResult {
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
}
pub trait LayoutStrategy {
fn calculate_layout(&self, constraints: LayoutConstraints) -> LayoutResult;
fn add_child(&mut self, child_id: ComponentId, weight: f32);
fn remove_child(&mut self, child_id: ComponentId);
fn update_child_constraints(&self,
child_id: ComponentId,
parent_constraints: LayoutConstraints) -> LayoutConstraints;
}
pub struct LayoutManager {
strategies: HashMap<ComponentId, Box<dyn LayoutStrategy>>,
child_map: HashMap<ComponentId, Vec<ComponentId>>,
parent_map: HashMap<ComponentId, ComponentId>,
layouts: HashMap<ComponentId, LayoutResult>,
next_id: ComponentId,
}
impl LayoutManager {
pub fn new() -> Self {
Self {
strategies: HashMap::new(),
child_map: HashMap::new(),
parent_map: HashMap::new(),
layouts: HashMap::new(),
next_id: 1,
}
}
pub fn generate_id(&mut self) -> ComponentId {
let id = self.next_id;
self.next_id += 1;
id
}
pub fn register_strategy(&mut self,
component_id: ComponentId,
strategy: Box<dyn LayoutStrategy>) {
self.strategies.insert(component_id, strategy);
self.child_map.entry(component_id).or_insert_with(Vec::new);
}
pub fn add_child(&mut self, parent_id: ComponentId, child_id: ComponentId, weight: f32) {
if self.strategies.contains_key(&parent_id) {
if let Some(old_parent) = self.parent_map.get(&child_id) {
self.remove_child(*old_parent, child_id);
}
self.child_map.entry(parent_id).and_modify(|children| {
if !children.contains(&child_id) {
children.push(child_id);
}
});
self.parent_map.insert(child_id, parent_id);
if let Some(strategy) = self.strategies.get_mut(&parent_id) {
strategy.add_child(child_id, weight);
}
}
}
pub fn remove_child(&mut self, parent_id: ComponentId, child_id: ComponentId) {
self.parent_map.remove(&child_id);
if let Some(children) = self.child_map.get_mut(&parent_id) {
if let Some(index) = children.iter().position(|&id| id == child_id) {
children.remove(index);
}
}
if let Some(strategy) = self.strategies.get_mut(&parent_id) {
strategy.remove_child(child_id);
}
}
pub fn calculate_layout(&mut self, component_id: ComponentId, constraints: LayoutConstraints) {
if let Some(strategy) = self.strategies.get(&component_id) {
let layout = strategy.calculate_layout(constraints);
self.layouts.insert(component_id, layout);
if let Some(children) = self.child_map.get(&component_id) {
let child_constraints = LayoutConstraints {
min_width: 0.0,
max_width: layout.width,
min_height: 0.0,
max_height: layout.height,
};
for &child_id in children {
if let Some(child_strategy) = self.strategies.get(&child_id) {
let child_constraint = child_strategy.update_child_constraints(
child_id,
child_constraints
);
self.calculate_layout(child_id, child_constraint);
}
}
}
}
}
pub fn get_layout(&self, component_id: ComponentId) -> Option<&LayoutResult> {
self.layouts.get(&component_id)
}
pub fn get_parent(&self, component_id: ComponentId) -> Option<ComponentId> {
self.parent_map.get(&component_id).copied()
}
pub fn get_children(&self, component_id: ComponentId) -> Option<&Vec<ComponentId>> {
self.child_map.get(&component_id)
}
}