use super::{Align, AxisInfo, Margins, SizeRules};
use crate::cast::Conv;
use crate::event::ConfigCx;
use crate::geom::{Rect, Size};
use crate::theme::SizeCx;
use crate::util::WidgetHierarchy;
use crate::{Layout, Node};
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 fn solve_size_rules<W: Layout + ?Sized>(
widget: &mut W,
sizer: SizeCx,
x_size: Option<i32>,
y_size: Option<i32>,
h_align: Option<Align>,
v_align: Option<Align>,
) {
widget.size_rules(sizer.re(), AxisInfo::new(false, y_size, h_align));
widget.size_rules(sizer.re(), AxisInfo::new(true, x_size, v_align));
}
pub struct SolveCache {
min: Size,
ideal: Size,
margins: Margins,
refresh_rules: bool,
last_width: i32,
}
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(mut widget: Node<'_>, sizer: SizeCx) -> Self {
let start = std::time::Instant::now();
let w = widget.size_rules(sizer.re(), AxisInfo::new(false, None, None));
let h = widget.size_rules(sizer.re(), AxisInfo::new(true, Some(w.ideal_size()), None));
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());
log::trace!(
target: "kas_perf::layout", "find_constraints: {}μs",
start.elapsed().as_micros(),
);
log::debug!("find_constraints: min={min:?}, ideal={ideal:?}, margins={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,
mut widget: Node<'_>,
cx: &mut ConfigCx,
mut rect: Rect,
inner_margin: bool,
) {
let start = std::time::Instant::now();
let mut width = rect.size.0;
if inner_margin {
width -= self.margins.sum_horiz();
}
if self.refresh_rules || width != self.last_width {
if self.refresh_rules {
let w = widget.size_rules(cx.size_cx(), AxisInfo::new(false, None, None));
self.min.0 = w.min_size();
self.ideal.0 = w.ideal_size();
self.margins.horiz = w.margins();
width = rect.size.0 - self.margins.sum_horiz();
}
let h = widget.size_rules(cx.size_cx(), AxisInfo::new(true, Some(width), None));
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 += Size::conv((self.margins.horiz.0, self.margins.vert.0));
rect.size.0 = width;
rect.size.1 -= self.margins.sum_vert();
}
widget.set_rect(cx, rect);
log::trace!(target: "kas_perf::layout", "apply_rect: {}μs", start.elapsed().as_micros());
self.refresh_rules = false;
}
pub fn print_widget_heirarchy(&mut self, widget: &dyn Layout) {
let rect = widget.rect();
let hier = WidgetHierarchy::new(widget);
log::trace!(
target: "kas_core::layout::hierarchy",
"apply_rect: rect={rect:?}:{hier}",
);
}
}