1use std::marker::PhantomData;
9use std::ops::Range;
10
11use super::{AxisInfo, SizeRules};
12use super::{RowStorage, RowTemp, RulesSetter, RulesSolver};
13use crate::dir::{Direction, Directional};
14use crate::geom::{Coord, Rect};
15use crate::{Collection, Tile};
16
17pub struct RowSolver<S: RowStorage> {
23 axis: AxisInfo,
25 axis_is_vertical: bool,
26 axis_is_reversed: bool,
27 rules: Option<SizeRules>,
28 _s: PhantomData<S>,
29}
30
31impl<S: RowStorage> RowSolver<S> {
32 pub fn new<D: Directional>(axis: AxisInfo, (dir, len): (D, usize), storage: &mut S) -> Self {
40 storage.set_dim(len);
41
42 let axis_is_vertical = axis.is_vertical() ^ dir.is_vertical();
43
44 if axis.has_fixed && axis_is_vertical {
45 let (widths, rules) = storage.widths_and_rules();
47 SizeRules::solve_widths(widths, rules, axis.other_axis);
48 }
49
50 RowSolver {
51 axis,
52 axis_is_vertical,
53 axis_is_reversed: dir.is_reversed(),
54 rules: None,
55 _s: Default::default(),
56 }
57 }
58}
59
60impl<S: RowStorage> RulesSolver for RowSolver<S> {
61 type Storage = S;
62 type ChildInfo = usize;
63
64 fn for_child<CR: FnOnce(AxisInfo) -> SizeRules>(
65 &mut self,
66 storage: &mut Self::Storage,
67 index: Self::ChildInfo,
68 child_rules: CR,
69 ) {
70 if self.axis.has_fixed && self.axis_is_vertical {
71 self.axis.other_axis = storage.widths()[index];
72 }
73 let child_rules = child_rules(self.axis);
74
75 if !self.axis_is_vertical {
76 storage.rules()[index] = child_rules;
77 if let Some(rules) = self.rules {
78 if self.axis_is_reversed {
79 self.rules = Some(child_rules.appended(rules));
80 } else {
81 self.rules = Some(rules.appended(child_rules));
82 }
83 } else {
84 self.rules = Some(child_rules);
85 }
86 } else {
87 self.rules = Some(
88 self.rules
89 .map(|rules| rules.max(child_rules))
90 .unwrap_or(child_rules),
91 );
92 }
93 }
94
95 fn finish(self, _: &mut Self::Storage) -> SizeRules {
96 self.rules.unwrap_or(SizeRules::EMPTY)
97 }
98}
99
100pub struct RowSetter<D, T: RowTemp, S: RowStorage> {
108 rect: Rect,
109 offsets: T,
110 direction: D,
111 _s: PhantomData<S>,
112}
113
114impl<D: Directional, T: RowTemp, S: RowStorage> RowSetter<D, T, S> {
115 pub fn new(rect: Rect, (direction, len): (D, usize), storage: &mut S) -> Self {
123 let mut offsets = T::default();
124 offsets.set_len(len);
125 storage.set_dim(len);
126
127 if len > 0 {
128 let is_horiz = direction.is_horizontal();
129 let width = if is_horiz { rect.size.0 } else { rect.size.1 };
130 let (widths, rules) = storage.widths_and_rules();
131 SizeRules::solve_widths(widths, rules, width);
132 }
133
134 let _s = Default::default();
135 let mut row = RowSetter {
136 rect,
137 offsets,
138 direction,
139 _s,
140 };
141 row.update_offsets(storage);
142 row
143 }
144
145 pub fn new_unsolved(rect: Rect, (direction, len): (D, usize), storage: &mut S) -> Self {
152 let mut offsets = T::default();
153 offsets.set_len(len);
154 storage.set_dim(len);
155
156 let _s = Default::default();
157 RowSetter {
158 rect,
159 offsets,
160 direction,
161 _s,
162 }
163 }
164
165 pub fn update_offsets(&mut self, storage: &mut S) {
166 let offsets = self.offsets.as_mut();
167 let len = offsets.len();
168 if len == 0 {
169 return;
170 }
171
172 let pos = if self.direction.is_horizontal() {
173 self.rect.pos.0
174 } else {
175 self.rect.pos.1
176 };
177
178 if self.direction.is_reversed() {
179 offsets[len - 1] = pos;
180 for i in (0..(len - 1)).rev() {
181 let i1 = i + 1;
182 let m1 = storage.rules()[i1].margins_i32().1;
183 let m0 = storage.rules()[i].margins_i32().0;
184 offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
185 }
186 } else {
187 offsets[0] = pos;
188 for i in 1..len {
189 let i1 = i - 1;
190 let m1 = storage.rules()[i1].margins_i32().1;
191 let m0 = storage.rules()[i].margins_i32().0;
192 offsets[i] = offsets[i1] + storage.widths()[i1] + m1.max(m0);
193 }
194 }
195 }
196
197 pub fn solve_range(&mut self, storage: &mut S, range: Range<usize>, width: i32, last: bool) {
202 assert!(range.end <= self.offsets.as_mut().len());
203
204 let (widths, rules) = storage.widths_and_rules();
205 SizeRules::solve_widths_with_priority(
206 &mut widths[range.clone()],
207 &rules[range],
208 width,
209 last,
210 );
211 }
212
213 pub fn maximal_rect_of(&mut self, storage: &mut S, index: usize) -> Rect {
217 let pre_rules = SizeRules::min_sum(&storage.rules()[0..index]);
218 let m = storage.rules()[index].margins();
219 let len = storage.widths().len();
220 let post_rules = SizeRules::min_sum(&storage.rules()[(index + 1)..len]);
221
222 let size1 = pre_rules.min_size() + i32::from(pre_rules.margins().1.max(m.0));
223 let size2 = size1 + post_rules.min_size() + i32::from(post_rules.margins().0.max(m.1));
224
225 let mut rect = self.rect;
226 if self.direction.is_horizontal() {
227 rect.pos.0 = self.rect.pos.0 + size1;
228 rect.size.0 = (self.rect.size.0 - size2).max(0);
229 } else {
230 rect.pos.1 = self.rect.pos.1 + size1;
231 rect.size.1 = (self.rect.size.1 - size2).max(0);
232 }
233 rect
234 }
235}
236
237impl<D: Directional, T: RowTemp, S: RowStorage> RulesSetter for RowSetter<D, T, S> {
238 type Storage = S;
239 type ChildInfo = usize;
240
241 fn child_rect(&mut self, storage: &mut Self::Storage, index: Self::ChildInfo) -> Rect {
242 let mut rect = self.rect;
243 if self.direction.is_horizontal() {
244 rect.pos.0 = self.offsets.as_mut()[index];
245 rect.size.0 = storage.widths()[index];
246 } else {
247 rect.pos.1 = self.offsets.as_mut()[index];
248 rect.size.1 = storage.widths()[index];
249 }
250 rect
251 }
252}
253
254#[derive(Clone, Copy, Debug)]
259pub struct RowPositionSolver<D: Directional> {
260 direction: D,
261}
262
263impl<D: Directional> RowPositionSolver<D> {
264 pub fn new(direction: D) -> Self {
266 RowPositionSolver { direction }
267 }
268
269 fn binary_search<C: Collection + ?Sized>(
270 self,
271 widgets: &C,
272 coord: Coord,
273 ) -> Option<Result<usize, usize>> {
274 match self.direction.as_direction() {
275 Direction::Right => widgets.binary_search_by(|w| w.rect().pos.0.cmp(&coord.0)),
276 Direction::Down => widgets.binary_search_by(|w| w.rect().pos.1.cmp(&coord.1)),
277 Direction::Left => widgets.binary_search_by(|w| w.rect().pos.0.cmp(&coord.0).reverse()),
278 Direction::Up => widgets.binary_search_by(|w| w.rect().pos.1.cmp(&coord.1).reverse()),
279 }
280 }
281
282 pub fn find_child_index<C: Collection + ?Sized>(
289 self,
290 widgets: &C,
291 coord: Coord,
292 ) -> Option<usize> {
293 match self.binary_search(widgets, coord)? {
294 Ok(i) => Some(i),
295 Err(i) if self.direction.is_reversed() => {
296 if i == widgets.len() || !widgets.get_tile(i)?.rect().contains(coord) {
297 None
298 } else {
299 Some(i)
300 }
301 }
302 Err(i) => {
303 if i == 0 || !widgets.get_tile(i - 1)?.rect().contains(coord) {
304 None
305 } else {
306 Some(i - 1)
307 }
308 }
309 }
310 }
311
312 #[inline]
319 pub fn find_child<C: Collection + ?Sized>(
320 self,
321 widgets: &C,
322 coord: Coord,
323 ) -> Option<&dyn Tile> {
324 self.find_child_index(widgets, coord)
325 .and_then(|i| widgets.get_tile(i))
326 }
327
328 #[inline]
335 pub fn find_child_mut<C: Collection + ?Sized>(
336 self,
337 widgets: &mut C,
338 coord: Coord,
339 ) -> Option<&mut dyn Tile> {
340 self.find_child_index(widgets, coord)
341 .and_then(|i| widgets.get_mut_tile(i))
342 }
343
344 pub fn for_children<C: Collection + ?Sized, F: FnMut(&dyn Tile)>(
346 self,
347 widgets: &C,
348 rect: Rect,
349 mut f: F,
350 ) {
351 let (pos, end) = match self.direction.is_reversed() {
352 false => (rect.pos, rect.pos2()),
353 true => (rect.pos2(), rect.pos),
354 };
355 let start = match self.binary_search(widgets, pos) {
356 Some(Ok(i)) => i,
357 Some(Err(i)) if i > 0 => {
358 let mut j = i - 1;
359 if let Some(rect) = widgets.get_tile(j).map(|l| l.rect()) {
360 let cond = match self.direction.as_direction() {
361 Direction::Right => pos.0 < rect.pos2().0,
362 Direction::Down => pos.1 < rect.pos2().1,
363 Direction::Left => rect.pos.0 <= pos.0,
364 Direction::Up => rect.pos.1 <= pos.1,
365 };
366 if !cond {
367 j = i;
368 }
369 }
370 j
371 }
372 _ => 0,
373 };
374
375 for i in start..widgets.len() {
376 if let Some(child) = widgets.get_tile(i) {
377 let do_break = match self.direction.as_direction() {
378 Direction::Right => child.rect().pos.0 >= end.0,
379 Direction::Down => child.rect().pos.1 >= end.1,
380 Direction::Left => child.rect().pos2().0 < end.0,
381 Direction::Up => child.rect().pos2().1 < end.1,
382 };
383 if do_break {
384 break;
385 }
386 f(child);
387 }
388 }
389 }
390}