use super::{AxisInfo, SizeRules};
use super::{RulesSetter, RulesSolver};
use crate::dir::{Direction, Directional};
use crate::geom::{Coord, Rect, Size};
#[derive(Clone, Debug, Default)]
pub struct FlowStorage {
rules: Vec<SizeRules>,
height_rules: Vec<SizeRules>,
widths: Vec<i32>,
breaks: Vec<usize>,
}
pub struct FlowSolver {
axis: AxisInfo,
direction: Direction,
secondary_is_reversed: bool,
opt_rules: Option<SizeRules>,
rules: SizeRules,
min_cols: i32,
ideal_cols: i32,
}
impl FlowSolver {
pub fn new(
axis: AxisInfo,
direction: Direction,
secondary_is_reversed: bool,
len: usize,
storage: &mut FlowStorage,
) -> Self {
storage.rules.resize(len, SizeRules::EMPTY);
storage.widths.resize(len, 0);
if direction.is_horizontal() || axis.is_horizontal() {
storage.breaks.clear();
storage.height_rules.clear();
}
if axis.has_fixed && direction.is_horizontal() && len != 0 {
debug_assert!(axis.is_vertical());
let width = axis.other_axis;
let mut total = storage.rules[0];
let mut start = 0;
for i in 1..storage.rules.len() {
let sum = total.appended(storage.rules[i]);
if sum.min_size() <= width {
total = sum;
continue;
}
SizeRules::solve_widths_with_total(
&mut storage.widths[start..i],
&storage.rules[start..i],
total,
width,
);
storage.breaks.push(i);
start = i;
total = storage.rules[i];
}
SizeRules::solve_widths_with_total(
&mut storage.widths[start..],
&storage.rules[start..],
total,
width,
);
}
storage.height_rules.reserve_exact(storage.breaks.len() + 1);
FlowSolver {
axis,
direction,
secondary_is_reversed,
opt_rules: None,
rules: SizeRules::EMPTY,
min_cols: 1,
ideal_cols: 3,
}
}
#[inline]
pub fn set_num_columns(&mut self, min: i32, ideal: i32) {
self.min_cols = min;
self.ideal_cols = ideal;
}
pub fn set_column_properties(&mut self, width: i32) {
if self.direction.is_vertical() && self.axis.is_vertical() {
self.axis.map_other(|w| w.min(width));
}
}
}
impl RulesSolver for FlowSolver {
type Storage = FlowStorage;
type ChildInfo = usize;
fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
&mut self,
storage: &mut Self::Storage,
index: Self::ChildInfo,
child_rules: CR,
) {
if self.direction.is_horizontal() && self.axis.has_fixed {
self.axis.other_axis = storage.widths[index];
}
let child_rules = child_rules(self.axis);
if self.direction.is_horizontal() == self.axis.is_horizontal() {
storage.rules[index] = child_rules;
}
if self.direction.is_horizontal() == self.axis.is_horizontal() {
self.opt_rules = Some(if let Some(rules) = self.opt_rules {
if self.direction.is_reversed() {
child_rules.appended(rules)
} else {
rules.appended(child_rules)
}
} else {
child_rules
});
} else {
if storage.breaks.contains(&index) {
storage.height_rules.push(self.rules);
self.opt_rules = Some(if let Some(rules) = self.opt_rules {
if self.secondary_is_reversed {
rules.appended(self.rules)
} else {
self.rules.appended(rules)
}
} else {
self.rules
});
self.rules = SizeRules::EMPTY;
}
}
self.rules = self.rules.max(child_rules);
}
fn finish(self, storage: &mut Self::Storage) -> SizeRules {
if self.direction.is_horizontal() == self.axis.is_horizontal() {
let mut col_limited_rules = self.rules;
col_limited_rules.multiply_with_margin(self.min_cols, self.ideal_cols);
let unwrapped_width = self.opt_rules.unwrap_or(SizeRules::EMPTY).ideal_size();
let min = col_limited_rules.min_size();
let ideal = unwrapped_width.min(col_limited_rules.ideal_size());
let stretch = self.rules.stretch();
SizeRules::new(min, ideal, stretch).with_margins(self.rules.margins())
} else {
let rules = if let Some(rules) = self.opt_rules {
if self.secondary_is_reversed {
rules.appended(self.rules)
} else {
self.rules.appended(rules)
}
} else {
self.rules
};
storage.height_rules.push(rules);
debug_assert_eq!(storage.breaks.len() + 1, storage.height_rules.len());
rules
}
}
}
pub struct FlowSetter {
pos: Coord,
offsets: Vec<i32>,
direction: Direction,
secondary_is_reversed: bool,
heights: Vec<i32>, row_offsets: Vec<i32>,
}
impl FlowSetter {
pub fn new(
rect: Rect,
direction: Direction,
secondary_is_reversed: bool,
len: usize,
storage: &mut FlowStorage,
) -> Self {
let offsets = vec![0; len];
assert_eq!(storage.rules.len(), len);
let mut heights = vec![];
if direction.is_vertical() {
todo!()
}
if len != 0 {
let height = rect.size.extract(direction.flipped());
heights = vec![0; storage.height_rules.len()];
SizeRules::solve_widths(&mut heights, &storage.height_rules, height);
}
let mut row = FlowSetter {
pos: rect.pos,
offsets,
direction,
secondary_is_reversed,
heights,
row_offsets: vec![],
};
row.update_offsets(storage);
row
}
fn update_offsets(&mut self, storage: &mut FlowStorage) {
fn set_offsets(
pos: i32,
rules: &[SizeRules],
sizes: &[i32],
offsets: &mut [i32],
iter: impl ExactSizeIterator<Item = usize>,
is_break: impl Fn(usize) -> bool,
) {
debug_assert_eq!(rules.len(), sizes.len());
debug_assert_eq!(rules.len(), iter.len());
let mut caret = pos;
let mut margin = 0;
for i in iter {
let margins = rules[i].margins_i32();
if is_break(i) {
caret = pos;
} else {
caret += margin.max(margins.0);
}
margin = margins.1;
offsets[i] = caret;
caret += sizes[i];
}
}
let len = self.offsets.len();
if len == 0 {
return;
}
let pos = self.pos.extract(self.direction);
if !self.direction.is_reversed() {
set_offsets(
pos,
&storage.rules,
&storage.widths,
&mut self.offsets,
0..len,
|i| i == 0 || storage.breaks.contains(&i),
);
} else {
set_offsets(
pos,
&storage.rules,
&storage.widths,
&mut self.offsets,
(0..len).rev(),
|i| i == len - 1 || storage.breaks.contains(&(i + 1)),
);
}
let pos = self.pos.extract(self.direction.flipped());
let len = storage.height_rules.len();
self.row_offsets.resize(len, 0);
let offsets = &mut self.row_offsets;
if !self.secondary_is_reversed {
set_offsets(
pos,
&storage.height_rules,
&self.heights,
offsets,
0..len,
|_| false,
);
} else {
set_offsets(
pos,
&storage.height_rules,
&self.heights,
offsets,
(0..len).rev(),
|_| false,
);
}
}
}
impl RulesSetter for FlowSetter {
type Storage = FlowStorage;
type ChildInfo = usize;
fn child_rect(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
let row = match storage.breaks.binary_search(&index) {
Ok(i) => i + 1,
Err(i) => i,
};
let w = storage.widths[index];
let h = self.heights[row];
let x = self.offsets[index];
let y = self.row_offsets[row];
if self.direction.is_horizontal() {
Rect::new(Coord(x, y), Size(w, h))
} else {
Rect::new(Coord(y, x), Size(h, w))
}
}
}