use std::marker::PhantomData;
use std::ops::Range;
use super::{AxisInfo, SizeRules};
use super::{RowStorage, RowTemp, RulesSetter, RulesSolver};
use crate::dir::{Direction, Directional};
use crate::geom::{Coord, Rect};
use crate::Widget;
pub struct RowSolver<S: RowStorage> {
axis: AxisInfo,
axis_is_vertical: bool,
axis_is_reversed: bool,
rules: Option<SizeRules>,
_s: PhantomData<S>,
}
impl<S: RowStorage> RowSolver<S> {
pub fn new<D: Directional>(axis: AxisInfo, (dir, len): (D, usize), storage: &mut S) -> Self {
storage.set_dim(len);
let axis_is_vertical = axis.is_vertical() ^ dir.is_vertical();
if axis.has_fixed && axis_is_vertical {
let (widths, rules) = storage.widths_and_rules();
SizeRules::solve_seq(widths, rules, axis.other_axis);
}
RowSolver {
axis,
axis_is_vertical,
axis_is_reversed: dir.is_reversed(),
rules: None,
_s: Default::default(),
}
}
}
impl<S: RowStorage> RulesSolver for RowSolver<S> {
type Storage = S;
type ChildInfo = usize;
fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
&mut self,
storage: &mut Self::Storage,
index: Self::ChildInfo,
child_rules: CR,
) {
if self.axis.has_fixed && self.axis_is_vertical {
self.axis.other_axis = storage.widths()[index];
}
let child_rules = child_rules(self.axis);
if !self.axis_is_vertical {
storage.rules()[index] = child_rules;
if let Some(rules) = self.rules {
if self.axis_is_reversed {
self.rules = Some(child_rules.appended(rules));
} else {
self.rules = Some(rules.appended(child_rules));
}
} else {
self.rules = Some(child_rules);
}
} else {
self.rules = Some(
self.rules
.map(|rules| rules.max(child_rules))
.unwrap_or(child_rules),
);
}
}
fn finish(self, _: &mut Self::Storage) -> SizeRules {
self.rules.unwrap_or(SizeRules::EMPTY)
}
}
pub struct RowSetter<D, T: RowTemp, S: RowStorage> {
rect: Rect,
offsets: T,
direction: D,
_s: PhantomData<S>,
}
impl<D: Directional, T: RowTemp, S: RowStorage> RowSetter<D, T, S> {
pub fn new(rect: Rect, (direction, len): (D, usize), storage: &mut S) -> Self {
let mut offsets = T::default();
offsets.set_len(len);
storage.set_dim(len);
if len > 0 {
let is_horiz = direction.is_horizontal();
let width = if is_horiz { rect.size.0 } else { rect.size.1 };
let (widths, rules) = storage.widths_and_rules();
SizeRules::solve_seq(widths, rules, width);
}
let _s = Default::default();
let mut row = RowSetter {
rect,
offsets,
direction,
_s,
};
row.update_offsets(storage);
row
}
pub fn new_unsolved(rect: Rect, (direction, len): (D, usize), storage: &mut S) -> Self {
let mut offsets = T::default();
offsets.set_len(len);
storage.set_dim(len);
let _s = Default::default();
RowSetter {
rect,
offsets,
direction,
_s,
}
}
pub fn update_offsets(&mut self, storage: &mut S) {
let offsets = self.offsets.as_mut();
let len = offsets.len();
if len == 0 {
return;
}
let pos = if self.direction.is_horizontal() {
self.rect.pos.0
} else {
self.rect.pos.1
};
if self.direction.is_reversed() {
offsets[len - 1] = pos;
for i in (0..(len - 1)).rev() {
let i1 = i + 1;
let m1 = storage.rules()[i1].margins_i32().1;
let m0 = storage.rules()[i].margins_i32().0;
offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
}
} else {
offsets[0] = pos;
for i in 1..len {
let i1 = i - 1;
let m1 = storage.rules()[i1].margins_i32().1;
let m0 = storage.rules()[i].margins_i32().0;
offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
}
}
}
pub fn solve_range(&mut self, storage: &mut S, range: Range<usize>, width: i32) {
assert!(range.end <= self.offsets.as_mut().len());
let (widths, rules) = storage.widths_and_rules();
SizeRules::solve_seq(&mut widths[range.clone()], &rules[range], width);
}
}
impl<D: Directional, T: RowTemp, S: RowStorage> RulesSetter for RowSetter<D, T, S> {
type Storage = S;
type ChildInfo = usize;
fn child_rect(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
let mut rect = self.rect;
if self.direction.is_horizontal() {
rect.pos.0 = self.offsets.as_mut()[index];
rect.size.0 = storage.widths()[index];
} else {
rect.pos.1 = self.offsets.as_mut()[index];
rect.size.1 = storage.widths()[index];
}
rect
}
fn maximal_rect_of(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
let pre_rules = SizeRules::min_sum(&storage.rules()[0..index]);
let m = storage.rules()[index].margins();
let len = storage.widths().len();
let post_rules = SizeRules::min_sum(&storage.rules()[(index + 1)..len]);
let size1 = pre_rules.min_size() + i32::from(pre_rules.margins().1.max(m.0));
let size2 = size1 + post_rules.min_size() + i32::from(post_rules.margins().0.max(m.1));
let mut rect = self.rect;
if self.direction.is_horizontal() {
rect.pos.0 = self.rect.pos.0 + size1;
rect.size.0 = (self.rect.size.0 - size2).max(0);
} else {
rect.pos.1 = self.rect.pos.1 + size1;
rect.size.1 = (self.rect.size.1 - size2).max(0);
}
rect
}
}
#[derive(Clone, Copy, Debug)]
pub struct RowPositionSolver<D: Directional> {
direction: D,
}
impl<D: Directional> RowPositionSolver<D> {
pub fn new(direction: D) -> Self {
RowPositionSolver { direction }
}
fn binary_search<W: Widget>(self, widgets: &[W], coord: Coord) -> Result<usize, usize> {
match self.direction.as_direction() {
Direction::Right => widgets.binary_search_by_key(&coord.0, |w| w.rect().pos.0),
Direction::Down => widgets.binary_search_by_key(&coord.1, |w| w.rect().pos.1),
Direction::Left => widgets.binary_search_by(|w| w.rect().pos.0.cmp(&coord.0).reverse()),
Direction::Up => widgets.binary_search_by(|w| w.rect().pos.1.cmp(&coord.1).reverse()),
}
}
pub fn find_child_index<W: Widget>(self, widgets: &[W], coord: Coord) -> Option<usize> {
match self.binary_search(widgets, coord) {
Ok(i) => Some(i),
Err(i) if self.direction.is_reversed() => {
if i == widgets.len() || !widgets[i].rect().contains(coord) {
None
} else {
Some(i)
}
}
Err(i) => {
if i == 0 || !widgets[i - 1].rect().contains(coord) {
None
} else {
Some(i - 1)
}
}
}
}
#[inline]
pub fn find_child<W: Widget>(self, widgets: &[W], coord: Coord) -> Option<&W> {
self.find_child_index(widgets, coord).map(|i| &widgets[i])
}
#[inline]
pub fn find_child_mut<W: Widget>(self, widgets: &mut [W], coord: Coord) -> Option<&mut W> {
self.find_child_index(widgets, coord)
.map(|i| &mut widgets[i])
}
pub fn for_children<W: Widget, F: FnMut(&mut W)>(
self,
widgets: &mut [W],
rect: Rect,
mut f: F,
) {
let (pos, end) = match self.direction.is_reversed() {
false => (rect.pos, rect.pos2()),
true => (rect.pos2(), rect.pos),
};
let start = match self.binary_search(widgets, pos) {
Ok(i) => i,
Err(i) if i > 0 => {
let j = i - 1;
let rect = widgets[j].rect();
let cond = match self.direction.as_direction() {
Direction::Right => pos.0 < rect.pos2().0,
Direction::Down => pos.1 < rect.pos2().1,
Direction::Left => rect.pos.0 <= pos.0,
Direction::Up => rect.pos.1 <= pos.1,
};
if cond {
j
} else {
i
}
}
Err(_) => 0,
};
for child in widgets[start..].iter_mut() {
let do_break = match self.direction.as_direction() {
Direction::Right => child.rect().pos.0 >= end.0,
Direction::Down => child.rect().pos.1 >= end.1,
Direction::Left => child.rect().pos2().0 < end.0,
Direction::Up => child.rect().pos2().1 < end.1,
};
if do_break {
break;
}
f(child);
}
}
}