use log::trace;
use std::fmt;
use super::{AxisInfo, Margins, SizeRules};
use crate::draw::SizeHandle;
use crate::geom::{Coord, Rect, Size};
use crate::{AlignHints, WidgetConfig};
pub trait RulesSolver {
type Storage: Clone;
type ChildInfo;
fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
&mut self,
storage: &mut Self::Storage,
child_info: Self::ChildInfo,
child_rules: CR,
);
fn finish(self, storage: &mut Self::Storage) -> SizeRules;
}
pub trait RulesSetter {
type Storage: Clone;
type ChildInfo;
fn child_rect(&mut self, storage: &mut Self::Storage, child_info: Self::ChildInfo) -> Rect;
fn maximal_rect_of(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect;
}
pub struct SolveCache {
min: Size,
ideal: Size,
margins: Margins,
refresh_rules: bool,
last_width: u32,
}
impl SolveCache {
pub fn min(&self, inner_margin: bool) -> Size {
if inner_margin {
self.margins.pad(self.min)
} else {
self.min
}
}
pub fn ideal(&self, inner_margin: bool) -> Size {
if inner_margin {
self.margins.pad(self.ideal)
} else {
self.ideal
}
}
pub fn margins(&self) -> Margins {
self.margins
}
pub fn find_constraints(
widget: &mut dyn WidgetConfig,
size_handle: &mut dyn SizeHandle,
) -> Self {
let w = widget.size_rules(size_handle, AxisInfo::new(false, None));
let h = widget.size_rules(size_handle, AxisInfo::new(true, Some(w.ideal_size())));
let min = Size(w.min_size(), h.min_size());
let ideal = Size(w.ideal_size(), h.ideal_size());
let margins = Margins::hv(w.margins(), h.margins());
trace!(
"layout::solve: min={:?}, ideal={:?}, margins={:?}",
min,
ideal,
margins
);
let refresh_rules = false;
let last_width = ideal.0;
SolveCache {
min,
ideal,
margins,
refresh_rules,
last_width,
}
}
pub fn invalidate_rule_cache(&mut self) {
self.refresh_rules = true;
}
pub fn apply_rect(
&mut self,
widget: &mut dyn WidgetConfig,
size_handle: &mut dyn SizeHandle,
mut rect: Rect,
inner_margin: bool,
) {
if self.refresh_rules {
let w = widget.size_rules(size_handle, AxisInfo::new(false, None));
self.min.0 = w.min_size();
self.ideal.0 = w.ideal_size();
self.margins.horiz = w.margins();
}
let mut width = rect.size.0;
if inner_margin {
width -= (self.margins.horiz.0 + self.margins.horiz.1) as u32;
}
if self.refresh_rules || width != self.last_width {
let h = widget.size_rules(size_handle, AxisInfo::new(true, Some(width)));
self.min.1 = h.min_size();
self.ideal.1 = h.ideal_size();
self.margins.vert = h.margins();
self.last_width = width;
}
if inner_margin {
rect.pos += Coord(self.margins.horiz.0 as i32, self.margins.vert.0 as i32);
rect.size.0 = width;
rect.size.1 -= (self.margins.vert.0 + self.margins.vert.1) as u32;
}
widget.set_rect(rect, AlignHints::NONE);
trace!(
"layout::solve_and_set for size={:?} has hierarchy:{}",
rect.size,
WidgetHeirarchy(widget, 0),
);
self.refresh_rules = false;
}
}
struct WidgetHeirarchy<'a>(&'a dyn WidgetConfig, usize);
impl<'a> fmt::Display for WidgetHeirarchy<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(
f,
"\n{}{}\t{}\tpos={:?}\tsize={:?}",
"- ".repeat(self.1),
self.0.id(),
self.0.widget_name(),
self.0.rect().pos,
self.0.rect().size,
)?;
for i in 0..self.0.len() {
WidgetHeirarchy(self.0.get(i).unwrap(), self.1 + 1).fmt(f)?;
}
Ok(())
}
}