1use crate::items::{
9 CrossAxisAlignment, DialogButtonRole, FlexboxLayoutAlignContent, FlexboxLayoutAlignSelf,
10 FlexboxLayoutDirection, FlexboxLayoutWrap, LayoutAlignment,
11};
12use crate::{Coord, SharedVector, slice::Slice};
13use alloc::format;
14use alloc::string::String;
15use alloc::vec::Vec;
16use num_traits::Float;
17
18pub use crate::items::Orientation;
19
20#[repr(C)]
23#[derive(Clone, Copy, Debug, PartialEq)]
24pub struct LayoutInfo {
25 pub max: Coord,
27 pub max_percent: Coord,
29 pub min: Coord,
31 pub min_percent: Coord,
33 pub preferred: Coord,
35 pub stretch: f32,
37}
38
39impl Default for LayoutInfo {
40 fn default() -> Self {
41 LayoutInfo {
42 min: 0 as _,
43 max: Coord::MAX,
44 min_percent: 0 as _,
45 max_percent: 100 as _,
46 preferred: 0 as _,
47 stretch: 0 as _,
48 }
49 }
50}
51
52impl LayoutInfo {
53 #[must_use]
55 pub fn merge(&self, other: &LayoutInfo) -> Self {
56 Self {
57 min: self.min.max(other.min),
58 max: self.max.min(other.max),
59 min_percent: self.min_percent.max(other.min_percent),
60 max_percent: self.max_percent.min(other.max_percent),
61 preferred: self.preferred.max(other.preferred),
62 stretch: self.stretch.min(other.stretch),
63 }
64 }
65
66 #[must_use]
68 pub fn preferred_bounded(&self) -> Coord {
69 self.preferred.min(self.max).max(self.min)
70 }
71}
72
73impl core::ops::Add for LayoutInfo {
74 type Output = Self;
75
76 fn add(self, rhs: Self) -> Self::Output {
77 self.merge(&rhs)
78 }
79}
80
81pub fn min_max_size_for_layout_constraints(
83 constraints_horizontal: LayoutInfo,
84 constraints_vertical: LayoutInfo,
85) -> (Option<crate::api::LogicalSize>, Option<crate::api::LogicalSize>) {
86 let min_width = constraints_horizontal.min.min(constraints_horizontal.max) as f32;
87 let min_height = constraints_vertical.min.min(constraints_vertical.max) as f32;
88 let max_width = constraints_horizontal.max.max(constraints_horizontal.min) as f32;
89 let max_height = constraints_vertical.max.max(constraints_vertical.min) as f32;
90
91 let min_size = if min_width > 0. || min_height > 0. || cfg!(target_arch = "wasm32") {
95 Some(crate::api::LogicalSize::new(min_width, min_height))
96 } else {
97 None
98 };
99
100 let max_size = if (max_width > 0.
101 && max_height > 0.
102 && (max_width < i32::MAX as f32 || max_height < i32::MAX as f32))
103 || cfg!(target_arch = "wasm32")
104 {
105 let window_size_max = 16_777_215.;
107 Some(crate::api::LogicalSize::new(
108 max_width.min(window_size_max),
109 max_height.min(window_size_max),
110 ))
111 } else {
112 None
113 };
114
115 (min_size, max_size)
116}
117
118trait Saturating {
121 fn add(_: Self, _: Self) -> Self;
122}
123impl Saturating for i32 {
124 #[inline]
125 fn add(a: Self, b: Self) -> Self {
126 a.saturating_add(b)
127 }
128}
129impl Saturating for f32 {
130 #[inline]
131 fn add(a: Self, b: Self) -> Self {
132 a + b
133 }
134}
135
136mod grid_internal {
137 use super::*;
138
139 fn order_coord<T: PartialOrd>(a: &T, b: &T) -> core::cmp::Ordering {
140 a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal)
141 }
142
143 #[derive(Debug, Clone)]
144 pub struct LayoutData {
145 pub min: Coord,
147 pub max: Coord,
148 pub pref: Coord,
149 pub stretch: f32,
150
151 pub pos: Coord,
153 pub size: Coord,
154 }
155
156 impl Default for LayoutData {
157 fn default() -> Self {
158 LayoutData {
159 min: 0 as _,
160 max: Coord::MAX,
161 pref: 0 as _,
162 stretch: f32::MAX,
163 pos: 0 as _,
164 size: 0 as _,
165 }
166 }
167 }
168
169 trait Adjust {
170 fn can_grow(_: &LayoutData) -> Coord;
171 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord;
172 fn distribute(_: &mut LayoutData, val: Coord);
173 }
174
175 struct Grow;
176 impl Adjust for Grow {
177 fn can_grow(it: &LayoutData) -> Coord {
178 it.max - it.size
179 }
180
181 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord {
182 expected_size - current_size
183 }
184
185 fn distribute(it: &mut LayoutData, val: Coord) {
186 it.size += val;
187 }
188 }
189
190 struct Shrink;
191 impl Adjust for Shrink {
192 fn can_grow(it: &LayoutData) -> Coord {
193 it.size - it.min
194 }
195
196 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord {
197 current_size - expected_size
198 }
199
200 fn distribute(it: &mut LayoutData, val: Coord) {
201 it.size -= val;
202 }
203 }
204
205 #[allow(clippy::unnecessary_cast)] fn adjust_items<A: Adjust>(data: &mut [LayoutData], size_without_spacing: Coord) -> Option<()> {
207 loop {
208 let size_cannot_grow: Coord = data
209 .iter()
210 .filter(|it| A::can_grow(it) <= 0 as _)
211 .map(|it| it.size)
212 .fold(0 as Coord, Saturating::add);
213
214 let total_stretch: f32 =
215 data.iter().filter(|it| A::can_grow(it) > 0 as _).map(|it| it.stretch).sum();
216
217 let actual_stretch = |s: f32| if total_stretch <= 0. { 1. } else { s };
218
219 let max_grow = data
220 .iter()
221 .filter(|it| A::can_grow(it) > 0 as _)
222 .map(|it| A::can_grow(it) as f32 / actual_stretch(it.stretch))
223 .min_by(order_coord)?;
224
225 let current_size: Coord = data
226 .iter()
227 .filter(|it| A::can_grow(it) > 0 as _)
228 .map(|it| it.size)
229 .fold(0 as _, Saturating::add);
230
231 let to_distribute =
233 A::to_distribute(size_without_spacing, size_cannot_grow + current_size) as f32;
234 if to_distribute <= 0. || max_grow <= 0. {
235 return Some(());
236 }
237
238 let grow = if total_stretch <= 0. {
239 to_distribute
240 / (data.iter().filter(|it| A::can_grow(it) > 0 as _).count() as Coord) as f32
241 } else {
242 to_distribute / total_stretch
243 }
244 .min(max_grow);
245
246 let mut distributed = 0 as Coord;
247 for it in data.iter_mut().filter(|it| A::can_grow(it) > 0 as Coord) {
248 let val = (grow * actual_stretch(it.stretch)) as Coord;
249 A::distribute(it, val);
250 distributed += val;
251 }
252
253 if distributed <= 0 as Coord {
254 if let Some(it) = data
257 .iter_mut()
258 .filter(|it| A::can_grow(it) > 0 as _)
259 .max_by(|a, b| actual_stretch(a.stretch).total_cmp(&b.stretch))
260 {
261 A::distribute(it, to_distribute as Coord);
262 }
263 return Some(());
264 }
265 }
266 }
267
268 pub fn layout_items(data: &mut [LayoutData], start_pos: Coord, size: Coord, spacing: Coord) {
269 let size_without_spacing = size - spacing * (data.len() - 1) as Coord;
270
271 let mut pref = 0 as Coord;
272 for it in data.iter_mut() {
273 it.size = it.pref;
274 pref += it.pref;
275 }
276 if size_without_spacing >= pref {
277 adjust_items::<Grow>(data, size_without_spacing);
278 } else if size_without_spacing < pref {
279 adjust_items::<Shrink>(data, size_without_spacing);
280 }
281
282 let mut pos = start_pos;
283 for it in data.iter_mut() {
284 it.pos = pos;
285 pos = Saturating::add(pos, Saturating::add(it.size, spacing));
286 }
287 }
288
289 #[test]
290 #[allow(clippy::float_cmp)] fn test_layout_items() {
292 let my_items = &mut [
293 LayoutData { min: 100., max: 200., pref: 100., stretch: 1., ..Default::default() },
294 LayoutData { min: 50., max: 300., pref: 100., stretch: 1., ..Default::default() },
295 LayoutData { min: 50., max: 150., pref: 100., stretch: 1., ..Default::default() },
296 ];
297
298 layout_items(my_items, 100., 650., 0.);
299 assert_eq!(my_items[0].size, 200.);
300 assert_eq!(my_items[1].size, 300.);
301 assert_eq!(my_items[2].size, 150.);
302
303 layout_items(my_items, 100., 200., 0.);
304 assert_eq!(my_items[0].size, 100.);
305 assert_eq!(my_items[1].size, 50.);
306 assert_eq!(my_items[2].size, 50.);
307
308 layout_items(my_items, 100., 300., 0.);
309 assert_eq!(my_items[0].size, 100.);
310 assert_eq!(my_items[1].size, 100.);
311 assert_eq!(my_items[2].size, 100.);
312 }
313
314 pub fn to_layout_data(
317 organized_data: &GridLayoutOrganizedData,
318 constraints: Slice<LayoutItemInfo>,
319 orientation: Orientation,
320 repeater_indices: Slice<u32>,
321 repeater_steps: Slice<u32>,
322 spacing: Coord,
323 size: Option<Coord>,
324 ) -> Vec<LayoutData> {
325 assert!(organized_data.len().is_multiple_of(4));
326 let num = organized_data.max_value(
327 constraints.len(),
328 orientation,
329 &repeater_indices,
330 &repeater_steps,
331 ) as usize;
332 if num < 1 {
333 return Default::default();
334 }
335 let marker_for_empty = -1.;
336 let mut layout_data = alloc::vec![grid_internal::LayoutData { max: 0 as Coord, stretch: marker_for_empty, ..Default::default() }; num];
337 let mut has_spans = false;
338 for (idx, cell_data) in constraints.iter().enumerate() {
339 let constraint = &cell_data.constraint;
340 let mut max = constraint.max;
341 if let Some(size) = size {
342 max = max.min(size * constraint.max_percent / 100 as Coord);
343 }
344 let (col_or_row, span) = organized_data.col_or_row_and_span(
345 idx,
346 orientation,
347 &repeater_indices,
348 &repeater_steps,
349 );
350 for c in 0..(span as usize) {
351 let cdata = &mut layout_data[col_or_row as usize + c];
352 if cdata.stretch == marker_for_empty {
355 cdata.max = Coord::MAX;
356 cdata.stretch = 1.;
357 }
358 cdata.max = cdata.max.min(max);
359 }
360 if span == 1 {
361 let mut min = constraint.min;
362 if let Some(size) = size {
363 min = min.max(size * constraint.min_percent / 100 as Coord);
364 }
365 let pref = constraint.preferred.min(max).max(min);
366 let cdata = &mut layout_data[col_or_row as usize];
367 cdata.min = cdata.min.max(min);
368 cdata.pref = cdata.pref.max(pref);
369 cdata.stretch = cdata.stretch.min(constraint.stretch);
370 } else {
371 has_spans = true;
372 }
373 }
374 if has_spans {
375 for (idx, cell_data) in constraints.iter().enumerate() {
376 let constraint = &cell_data.constraint;
377 let (col_or_row, span) = organized_data.col_or_row_and_span(
378 idx,
379 orientation,
380 &repeater_indices,
381 &repeater_steps,
382 );
383 if span > 1 {
384 let span_data =
385 &mut layout_data[(col_or_row as usize)..(col_or_row + span) as usize];
386
387 let mut min = constraint.min;
389 if let Some(size) = size {
390 min = min.max(size * constraint.min_percent / 100 as Coord);
391 }
392 grid_internal::layout_items(span_data, 0 as _, min, spacing);
393 for cdata in span_data.iter_mut() {
394 if cdata.min < cdata.size {
395 cdata.min = cdata.size;
396 }
397 }
398
399 let mut max = constraint.max;
401 if let Some(size) = size {
402 max = max.min(size * constraint.max_percent / 100 as Coord);
403 }
404 grid_internal::layout_items(span_data, 0 as _, max, spacing);
405 for cdata in span_data.iter_mut() {
406 if cdata.max > cdata.size {
407 cdata.max = cdata.size;
408 }
409 }
410
411 grid_internal::layout_items(span_data, 0 as _, constraint.preferred, spacing);
413 for cdata in span_data.iter_mut() {
414 cdata.pref = cdata.pref.max(cdata.size).min(cdata.max).max(cdata.min);
415 }
416
417 let total_stretch: f32 = span_data.iter().map(|c| c.stretch).sum();
419 if total_stretch > constraint.stretch {
420 for cdata in span_data.iter_mut() {
421 cdata.stretch *= constraint.stretch / total_stretch;
422 }
423 }
424 }
425 }
426 }
427 for cdata in layout_data.iter_mut() {
428 if cdata.stretch == marker_for_empty {
429 cdata.stretch = 0.;
430 }
431 }
432 layout_data
433 }
434}
435
436#[repr(C)]
437pub struct Constraint {
438 pub min: Coord,
439 pub max: Coord,
440}
441
442impl Default for Constraint {
443 fn default() -> Self {
444 Constraint { min: 0 as Coord, max: Coord::MAX }
445 }
446}
447
448#[repr(C)]
449#[derive(Copy, Clone, Debug, Default)]
450pub struct Padding {
451 pub begin: Coord,
452 pub end: Coord,
453}
454
455#[repr(C)]
456#[derive(Debug)]
457pub struct GridLayoutData {
459 pub size: Coord,
460 pub spacing: Coord,
461 pub padding: Padding,
462 pub organized_data: GridLayoutOrganizedData,
463}
464
465#[repr(C)]
468#[derive(Debug, Clone)]
469pub struct GridLayoutInputData {
470 pub new_row: bool,
472 pub col: f32,
476 pub row: f32,
477 pub colspan: f32,
480 pub rowspan: f32,
481}
482
483impl Default for GridLayoutInputData {
484 fn default() -> Self {
485 Self {
486 new_row: false,
487 col: i_slint_common::ROW_COL_AUTO,
488 row: i_slint_common::ROW_COL_AUTO,
489 colspan: 1.0,
490 rowspan: 1.0,
491 }
492 }
493}
494
495pub type GridLayoutOrganizedData = SharedVector<u16>;
498
499impl GridLayoutOrganizedData {
500 fn push_cell(&mut self, col: u16, colspan: u16, row: u16, rowspan: u16) {
501 self.push(col);
502 self.push(colspan);
503 self.push(row);
504 self.push(rowspan);
505 }
506
507 fn col_or_row_and_span(
508 &self,
509 cell_number: usize,
510 orientation: Orientation,
511 repeater_indices: &Slice<u32>,
512 repeater_steps: &Slice<u32>,
513 ) -> (u16, u16) {
514 let mut final_idx = 0;
523 let mut cell_nr_adj = 0i32; let cell_number = cell_number as i32;
525 for rep_idx in 0..(repeater_indices.len() / 2) {
527 let ri_start_cell = repeater_indices[rep_idx * 2] as i32;
528 if cell_number < ri_start_cell {
529 break;
530 }
531 let ri_cell_count = repeater_indices[rep_idx * 2 + 1] as i32;
532 let step = repeater_steps.get(rep_idx).copied().unwrap_or(1) as i32;
533 let cells_in_repeater = ri_cell_count * step;
534 if cells_in_repeater > 0
535 && cell_number >= ri_start_cell
536 && cell_number < ri_start_cell + cells_in_repeater
537 {
538 let cell_in_rep = cell_number - ri_start_cell;
539 let row_in_rep = cell_in_rep / step;
540 let col_in_rep = cell_in_rep % step;
541 let jump_pos = (ri_start_cell - cell_nr_adj) as usize * 4;
542 let data_base = self[jump_pos] as usize;
543 let stride = self[jump_pos + 1] as usize;
544 final_idx = data_base + row_in_rep as usize * stride + col_in_rep as usize * 4;
545 break;
546 }
547 cell_nr_adj += cells_in_repeater - 1;
550 }
551 if final_idx == 0 {
552 final_idx = ((cell_number - cell_nr_adj) * 4) as usize;
553 }
554 let offset = if orientation == Orientation::Horizontal { 0 } else { 2 };
555 (self[final_idx + offset], self[final_idx + offset + 1])
556 }
557
558 fn max_value(
559 &self,
560 num_cells: usize,
561 orientation: Orientation,
562 repeater_indices: &Slice<u32>,
563 repeater_steps: &Slice<u32>,
564 ) -> u16 {
565 let mut max = 0;
566 for idx in 0..num_cells {
569 let (col_or_row, span) =
570 self.col_or_row_and_span(idx, orientation, repeater_indices, repeater_steps);
571 max = max.max(col_or_row + span.max(1));
572 }
573 max
574 }
575}
576
577struct OrganizedDataGenerator<'a> {
584 repeater_indices: &'a [u32],
586 repeater_steps: &'a [u32],
587 counter: usize,
589 repeat_u16_offset: usize,
591 next_rep: usize,
593 current_offset: usize,
595 result: &'a mut GridLayoutOrganizedData,
597}
598
599impl<'a> OrganizedDataGenerator<'a> {
600 fn new(
601 repeater_indices: &'a [u32],
602 repeater_steps: &'a [u32],
603 static_cells: usize,
604 num_repeaters: usize,
605 total_repeated_cells_count: usize,
606 result: &'a mut GridLayoutOrganizedData,
607 ) -> Self {
608 result.resize((static_cells + num_repeaters + total_repeated_cells_count) * 4, 0 as _);
609 let repeat_u16_offset = (static_cells + num_repeaters) * 4;
610 Self {
611 repeater_indices,
612 repeater_steps,
613 counter: 0,
614 repeat_u16_offset,
615 next_rep: 0,
616 current_offset: 0,
617 result,
618 }
619 }
620 fn add(&mut self, col: u16, colspan: u16, row: u16, rowspan: u16) {
621 let res = self.result.make_mut_slice();
622 loop {
623 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
624 let nr = *nr as usize;
625 let step = self.repeater_steps.get(self.next_rep).copied().unwrap_or(1) as usize;
626 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
627
628 if nr == self.counter {
629 let data_u16_start = self.repeat_u16_offset;
631 let stride = step * 4;
632
633 res[self.current_offset * 4] = data_u16_start as _;
635 res[self.current_offset * 4 + 1] = stride as _;
636 self.current_offset += 1;
637 }
638 if self.counter >= nr {
639 let cells_in_repeater = rep_count * step;
640 if self.counter - nr == cells_in_repeater {
641 self.repeat_u16_offset += cells_in_repeater * 4;
643 self.next_rep += 1;
644 continue;
645 }
646 let cell_in_rep = self.counter - nr;
648 let row_in_rep = cell_in_rep / step;
649 let col_in_rep = cell_in_rep % step;
650 let data_u16_start = self.repeat_u16_offset;
651 let u16_pos = data_u16_start + row_in_rep * step * 4 + col_in_rep * 4;
652 res[u16_pos] = col;
653 res[u16_pos + 1] = colspan;
654 res[u16_pos + 2] = row;
655 res[u16_pos + 3] = rowspan;
656 self.counter += 1;
657 return;
658 }
659 }
660 res[self.current_offset * 4] = col;
662 res[self.current_offset * 4 + 1] = colspan;
663 res[self.current_offset * 4 + 2] = row;
664 res[self.current_offset * 4 + 3] = rowspan;
665 self.current_offset += 1;
666 self.counter += 1;
667 return;
668 }
669 }
670}
671
672pub fn organize_dialog_button_layout(
675 input_data: Slice<GridLayoutInputData>,
676 dialog_button_roles: Slice<DialogButtonRole>,
677) -> GridLayoutOrganizedData {
678 let mut organized_data = GridLayoutOrganizedData::default();
679 organized_data.reserve(input_data.len() * 4);
680
681 #[cfg(feature = "std")]
682 fn is_kde() -> bool {
683 std::env::var("XDG_CURRENT_DESKTOP")
685 .ok()
686 .and_then(|v| v.as_bytes().first().copied())
687 .is_some_and(|x| x.eq_ignore_ascii_case(&b'K'))
688 }
689 #[cfg(not(feature = "std"))]
690 let is_kde = || true;
691
692 let expected_order: &[DialogButtonRole] = match crate::detect_operating_system() {
693 crate::items::OperatingSystemType::Windows => {
694 &[
695 DialogButtonRole::Reset,
696 DialogButtonRole::None, DialogButtonRole::Accept,
698 DialogButtonRole::Action,
699 DialogButtonRole::Reject,
700 DialogButtonRole::Apply,
701 DialogButtonRole::Help,
702 ]
703 }
704 crate::items::OperatingSystemType::Macos | crate::items::OperatingSystemType::Ios => {
705 &[
706 DialogButtonRole::Help,
707 DialogButtonRole::Reset,
708 DialogButtonRole::Apply,
709 DialogButtonRole::Action,
710 DialogButtonRole::None, DialogButtonRole::Reject,
712 DialogButtonRole::Accept,
713 ]
714 }
715 _ if is_kde() => {
716 &[
718 DialogButtonRole::Help,
719 DialogButtonRole::Reset,
720 DialogButtonRole::None, DialogButtonRole::Action,
722 DialogButtonRole::Accept,
723 DialogButtonRole::Apply,
724 DialogButtonRole::Reject,
725 ]
726 }
727 _ => {
728 &[
730 DialogButtonRole::Help,
731 DialogButtonRole::Reset,
732 DialogButtonRole::None, DialogButtonRole::Action,
734 DialogButtonRole::Accept,
735 DialogButtonRole::Apply,
736 DialogButtonRole::Reject,
737 ]
738 }
739 };
740
741 let mut column_for_input: Vec<usize> = Vec::with_capacity(dialog_button_roles.len());
743 for role in expected_order.iter() {
744 if role == &DialogButtonRole::None {
745 column_for_input.push(usize::MAX); continue;
747 }
748 for (idx, r) in dialog_button_roles.as_slice().iter().enumerate() {
749 if *r == *role {
750 column_for_input.push(idx);
751 }
752 }
753 }
754
755 for (input_index, cell) in input_data.as_slice().iter().enumerate() {
756 let col = column_for_input.iter().position(|&x| x == input_index);
757 if let Some(col) = col {
758 organized_data.push_cell(col as _, cell.colspan as _, cell.row as _, cell.rowspan as _);
759 } else {
760 organized_data.push_cell(
763 cell.col as _,
764 cell.colspan as _,
765 cell.row as _,
766 cell.rowspan as _,
767 );
768 }
769 }
770 organized_data
771}
772
773fn total_repeated_cells<'a>(repeater_indices: &'a [u32], repeater_steps: &'a [u32]) -> usize {
775 repeater_indices
776 .chunks(2)
777 .enumerate()
778 .map(|(i, chunk)| {
779 let count = chunk.get(1).copied().unwrap_or(0) as usize;
780 let step = repeater_steps.get(i).copied().unwrap_or(1) as usize;
781 count * step
782 })
783 .sum()
784}
785
786type Errors = Vec<String>;
787
788pub fn organize_grid_layout(
789 input_data: Slice<GridLayoutInputData>,
790 repeater_indices: Slice<u32>,
791 repeater_steps: Slice<u32>,
792) -> GridLayoutOrganizedData {
793 let (organized_data, errors) =
794 organize_grid_layout_impl(input_data, repeater_indices, repeater_steps);
795 for error in errors {
796 crate::debug_log!("Slint layout error: {}", error);
797 }
798 organized_data
799}
800
801fn organize_grid_layout_impl(
803 input_data: Slice<GridLayoutInputData>,
804 repeater_indices: Slice<u32>,
805 repeater_steps: Slice<u32>,
806) -> (GridLayoutOrganizedData, Errors) {
807 let mut organized_data = GridLayoutOrganizedData::default();
808 let num_repeaters = repeater_indices.len() / 2;
811 let total_repeated_cells =
812 total_repeated_cells(repeater_indices.as_slice(), repeater_steps.as_slice());
813 let static_cells = input_data.len() - total_repeated_cells;
814 let mut generator = OrganizedDataGenerator::new(
815 repeater_indices.as_slice(),
816 repeater_steps.as_slice(),
817 static_cells,
818 num_repeaters,
819 total_repeated_cells,
820 &mut organized_data,
821 );
822 let mut errors = Vec::new();
823
824 fn clamp_to_u16(value: f32, field_name: &str, errors: &mut Vec<String>) -> u16 {
825 if value < 0.0 {
826 errors.push(format!("cell {field_name} {value} is negative, clamping to 0"));
827 0
828 } else if value > u16::MAX as f32 {
829 errors
830 .push(format!("cell {field_name} {value} is too large, clamping to {}", u16::MAX));
831 u16::MAX
832 } else {
833 value as u16
834 }
835 }
836
837 let mut row = 0;
838 let mut col = 0;
839 let mut first = true;
840 for cell in input_data.as_slice().iter() {
841 if cell.new_row && !first {
842 row += 1;
843 col = 0;
844 }
845 first = false;
846
847 if cell.row != i_slint_common::ROW_COL_AUTO {
848 let cell_row = clamp_to_u16(cell.row, "row", &mut errors);
849 if row != cell_row {
850 row = cell_row;
851 col = 0;
852 }
853 }
854 if cell.col != i_slint_common::ROW_COL_AUTO {
855 col = clamp_to_u16(cell.col, "col", &mut errors);
856 }
857
858 let colspan = clamp_to_u16(cell.colspan, "colspan", &mut errors);
859 let rowspan = clamp_to_u16(cell.rowspan, "rowspan", &mut errors);
860 col = col.min(u16::MAX - colspan); generator.add(col, colspan, row, rowspan);
862 col += colspan;
863 }
864 (organized_data, errors)
865}
866
867struct LayoutCacheGenerator<'a> {
875 repeater_indices: &'a [u32],
877 counter: usize,
879 repeat_offset: usize,
881 next_rep: usize,
883 current_offset: usize,
885 result: &'a mut SharedVector<Coord>,
887}
888
889impl<'a> LayoutCacheGenerator<'a> {
890 fn new(repeater_indices: &'a [u32], result: &'a mut SharedVector<Coord>) -> Self {
891 let total_repeated_cells: usize = repeater_indices
892 .chunks(2)
893 .map(|chunk| chunk.get(1).copied().unwrap_or(0) as usize)
894 .sum();
895 assert!(result.len() >= total_repeated_cells * 2);
896 let repeat_offset = result.len() / 2 - total_repeated_cells;
897 Self { repeater_indices, counter: 0, repeat_offset, next_rep: 0, current_offset: 0, result }
898 }
899 fn add(&mut self, pos: Coord, size: Coord) {
900 let res = self.result.make_mut_slice();
901 let o = loop {
902 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
903 let nr = *nr as usize;
904 if nr == self.counter {
905 for o in 0..2 {
907 res[self.current_offset * 2 + o] = (self.repeat_offset * 2 + o) as _;
908 }
909 self.current_offset += 1;
910 }
911 if self.counter >= nr {
912 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
913 if self.counter - nr == rep_count {
914 self.repeat_offset += rep_count;
915 self.next_rep += 1;
916 continue;
917 }
918 let offset = self.repeat_offset + (self.counter - nr);
919 break offset;
920 }
921 }
922 self.current_offset += 1;
923 break self.current_offset - 1;
924 };
925 res[o * 2] = pos;
926 res[o * 2 + 1] = size;
927 self.counter += 1;
928 }
929}
930
931struct GridLayoutCacheGenerator<'a> {
935 repeater_indices: &'a [u32],
937 repeater_steps: &'a [u32],
938 counter: usize,
940 repeat_f32_offset: usize,
942 next_rep: usize,
944 current_offset: usize,
946 result: &'a mut SharedVector<Coord>,
948}
949
950impl<'a> GridLayoutCacheGenerator<'a> {
951 fn new(
952 repeater_indices: &'a [u32],
953 repeater_steps: &'a [u32],
954 static_cells: usize,
955 num_repeaters: usize,
956 total_repeated_cells_count: usize,
957 result: &'a mut SharedVector<Coord>,
958 ) -> Self {
959 result.resize((static_cells + num_repeaters + total_repeated_cells_count) * 2, 0 as _);
960 let repeat_f32_offset = (static_cells + num_repeaters) * 2;
961 Self {
962 repeater_indices,
963 repeater_steps,
964 counter: 0,
965 repeat_f32_offset,
966 next_rep: 0,
967 current_offset: 0,
968 result,
969 }
970 }
971 fn add(&mut self, pos: Coord, size: Coord) {
972 let res = self.result.make_mut_slice();
973 loop {
974 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
975 let nr = *nr as usize;
976 let step = self.repeater_steps.get(self.next_rep).copied().unwrap_or(1) as usize;
977 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
978
979 if nr == self.counter {
980 let data_f32_start = self.repeat_f32_offset;
982 let stride = step * 2;
983
984 res[self.current_offset * 2] = data_f32_start as _;
986 res[self.current_offset * 2 + 1] = stride as _;
987 self.current_offset += 1;
988 }
989 if self.counter >= nr {
990 let cells_in_repeater = rep_count * step;
991 if self.counter - nr == cells_in_repeater {
992 self.repeat_f32_offset += cells_in_repeater * 2;
994 self.next_rep += 1;
995 continue;
996 }
997 let cell_in_rep = self.counter - nr;
999 let row_in_rep = cell_in_rep / step;
1000 let col_in_rep = cell_in_rep % step;
1001 let data_f32_start = self.repeat_f32_offset;
1002 let f32_pos = data_f32_start + row_in_rep * step * 2 + col_in_rep * 2;
1003 res[f32_pos] = pos;
1004 res[f32_pos + 1] = size;
1005 self.counter += 1;
1006 return;
1007 }
1008 }
1009 res[self.current_offset * 2] = pos;
1011 res[self.current_offset * 2 + 1] = size;
1012 self.current_offset += 1;
1013 self.counter += 1;
1014 return;
1015 }
1016 }
1017}
1018
1019pub fn solve_grid_layout(
1022 data: &GridLayoutData,
1023 constraints: Slice<LayoutItemInfo>,
1024 orientation: Orientation,
1025 repeater_indices: Slice<u32>,
1026 repeater_steps: Slice<u32>,
1027) -> SharedVector<Coord> {
1028 let mut layout_data = grid_internal::to_layout_data(
1029 &data.organized_data,
1030 constraints,
1031 orientation,
1032 repeater_indices,
1033 repeater_steps,
1034 data.spacing,
1035 Some(data.size),
1036 );
1037
1038 if layout_data.is_empty() {
1039 return Default::default();
1040 }
1041
1042 grid_internal::layout_items(
1043 &mut layout_data,
1044 data.padding.begin,
1045 data.size - (data.padding.begin + data.padding.end),
1046 data.spacing,
1047 );
1048
1049 let mut result = SharedVector::<Coord>::default();
1050 let num_repeaters = repeater_indices.len() / 2;
1051 let total_repeated_cells =
1052 total_repeated_cells(repeater_indices.as_slice(), repeater_steps.as_slice());
1053 let static_cells = constraints.len() - total_repeated_cells;
1054 let mut generator = GridLayoutCacheGenerator::new(
1055 repeater_indices.as_slice(),
1056 repeater_steps.as_slice(),
1057 static_cells,
1058 num_repeaters,
1059 total_repeated_cells,
1060 &mut result,
1061 );
1062
1063 for idx in 0..constraints.len() {
1064 let (col_or_row, span) = data.organized_data.col_or_row_and_span(
1065 idx,
1066 orientation,
1067 &repeater_indices,
1068 &repeater_steps,
1069 );
1070 let cdata = &layout_data[col_or_row as usize];
1071 let size = if span > 0 {
1072 let last_cell = &layout_data[col_or_row as usize + span as usize - 1];
1073 last_cell.pos + last_cell.size - cdata.pos
1074 } else {
1075 0 as Coord
1076 };
1077 generator.add(cdata.pos, size);
1078 }
1079 result
1080}
1081
1082pub fn grid_layout_info(
1083 organized_data: GridLayoutOrganizedData, constraints: Slice<LayoutItemInfo>,
1085 repeater_indices: Slice<u32>,
1086 repeater_steps: Slice<u32>,
1087 spacing: Coord,
1088 padding: &Padding,
1089 orientation: Orientation,
1090) -> LayoutInfo {
1091 let layout_data = grid_internal::to_layout_data(
1092 &organized_data,
1093 constraints,
1094 orientation,
1095 repeater_indices,
1096 repeater_steps,
1097 spacing,
1098 None,
1099 );
1100 if layout_data.is_empty() {
1101 let mut info = LayoutInfo::default();
1102 info.min = padding.begin + padding.end;
1103 info.preferred = info.min;
1104 info.max = info.min;
1105 return info;
1106 }
1107 let spacing_w = spacing * (layout_data.len() - 1) as Coord + padding.begin + padding.end;
1108 let min = layout_data.iter().map(|data| data.min).sum::<Coord>() + spacing_w;
1109 let max = layout_data.iter().map(|data| data.max).fold(spacing_w, Saturating::add);
1110 let preferred = layout_data.iter().map(|data| data.pref).sum::<Coord>() + spacing_w;
1111 let stretch = layout_data.iter().map(|data| data.stretch).sum::<f32>();
1112 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
1113}
1114
1115#[repr(C)]
1116#[derive(Debug)]
1117pub struct BoxLayoutData<'a> {
1121 pub size: Coord,
1122 pub spacing: Coord,
1123 pub padding: Padding,
1124 pub alignment: LayoutAlignment,
1125 pub cells: Slice<'a, LayoutItemInfo>,
1126}
1127
1128#[repr(C)]
1130#[derive(Debug)]
1131pub struct BoxLayoutOrthoData<'a> {
1132 pub size: Coord,
1133 pub padding: Padding,
1134 pub cross_axis_alignment: CrossAxisAlignment,
1135 pub cells: Slice<'a, LayoutItemInfo>,
1136}
1137
1138#[repr(C)]
1139#[derive(Debug)]
1140pub struct FlexboxLayoutData<'a> {
1142 pub width: Coord,
1143 pub height: Coord,
1144 pub spacing_h: Coord,
1145 pub spacing_v: Coord,
1146 pub padding_h: Padding,
1147 pub padding_v: Padding,
1148 pub alignment: LayoutAlignment,
1149 pub direction: FlexboxLayoutDirection,
1150 pub align_content: FlexboxLayoutAlignContent,
1151 pub cross_axis_alignment: CrossAxisAlignment,
1152 pub flex_wrap: FlexboxLayoutWrap,
1153 pub cells_h: Slice<'a, FlexboxLayoutItemInfo>,
1155 pub cells_v: Slice<'a, FlexboxLayoutItemInfo>,
1157}
1158
1159#[repr(C)]
1160#[derive(Debug, Clone, Default)]
1161pub struct LayoutItemInfo {
1163 pub constraint: LayoutInfo,
1164}
1165
1166#[repr(C)]
1167#[derive(Debug, Clone)]
1168pub struct FlexboxLayoutItemInfo {
1170 pub constraint: LayoutInfo,
1171 pub flex_grow: f32,
1173 pub flex_shrink: f32,
1175 pub flex_basis: Coord,
1177 pub flex_align_self: FlexboxLayoutAlignSelf,
1179 pub flex_order: i32,
1181}
1182
1183impl Default for FlexboxLayoutItemInfo {
1184 fn default() -> Self {
1185 Self {
1186 constraint: LayoutInfo::default(),
1187 flex_grow: 0.0,
1188 flex_shrink: 1.0,
1189 flex_basis: -1 as _,
1190 flex_align_self: FlexboxLayoutAlignSelf::Auto,
1191 flex_order: 0,
1192 }
1193 }
1194}
1195
1196impl From<LayoutItemInfo> for FlexboxLayoutItemInfo {
1197 fn from(info: LayoutItemInfo) -> Self {
1198 Self { constraint: info.constraint, ..Default::default() }
1199 }
1200}
1201
1202pub fn solve_box_layout(data: &BoxLayoutData, repeater_indices: Slice<u32>) -> SharedVector<Coord> {
1204 let mut result = SharedVector::<Coord>::default();
1205 result.resize(data.cells.len() * 2 + repeater_indices.len(), 0 as _);
1207
1208 if data.cells.is_empty() {
1209 return result;
1210 }
1211
1212 let size_without_padding = data.size - data.padding.begin - data.padding.end;
1213 let num_spacings = (data.cells.len() - 1) as Coord;
1214 let spacings = data.spacing * num_spacings;
1215 let content_size = size_without_padding - spacings; let mut layout_data: Vec<_> = data
1217 .cells
1218 .iter()
1219 .map(|c| {
1220 let min = c.constraint.min.max(c.constraint.min_percent * content_size / 100 as Coord);
1221 let max = c.constraint.max.min(c.constraint.max_percent * content_size / 100 as Coord);
1222 grid_internal::LayoutData {
1223 min,
1224 max,
1225 pref: c.constraint.preferred.min(max).max(min),
1226 stretch: c.constraint.stretch,
1227 ..Default::default()
1228 }
1229 })
1230 .collect();
1231
1232 let pref_size: Coord = layout_data.iter().map(|it| it.pref).sum();
1233
1234 let align = match data.alignment {
1235 LayoutAlignment::Stretch => {
1236 grid_internal::layout_items(
1237 &mut layout_data,
1238 data.padding.begin,
1239 size_without_padding,
1240 data.spacing,
1241 );
1242 None
1243 }
1244 _ if size_without_padding <= pref_size + spacings => {
1245 grid_internal::layout_items(
1246 &mut layout_data,
1247 data.padding.begin,
1248 size_without_padding,
1249 data.spacing,
1250 );
1251 None
1252 }
1253 LayoutAlignment::Center => Some((
1254 data.padding.begin + (size_without_padding - pref_size - spacings) / 2 as Coord,
1255 data.spacing,
1256 )),
1257 LayoutAlignment::Start => Some((data.padding.begin, data.spacing)),
1258 LayoutAlignment::End => {
1259 Some((data.padding.begin + (size_without_padding - pref_size - spacings), data.spacing))
1260 }
1261 LayoutAlignment::SpaceBetween => {
1262 Some((data.padding.begin, (size_without_padding - pref_size) / num_spacings))
1263 }
1264 LayoutAlignment::SpaceAround => {
1265 let spacing = (size_without_padding - pref_size) / (num_spacings + 1 as Coord);
1266 Some((data.padding.begin + spacing / 2 as Coord, spacing))
1267 }
1268 LayoutAlignment::SpaceEvenly => {
1269 let spacing = (size_without_padding - pref_size) / (num_spacings + 2 as Coord);
1270 Some((data.padding.begin + spacing, spacing))
1271 }
1272 };
1273 if let Some((mut pos, spacing)) = align {
1274 for it in &mut layout_data {
1275 it.pos = pos;
1276 it.size = it.pref;
1277 pos += spacing + it.size;
1278 }
1279 }
1280
1281 let mut generator = LayoutCacheGenerator::new(&repeater_indices, &mut result);
1282 for layout in layout_data.iter() {
1283 generator.add(layout.pos, layout.size);
1284 }
1285 result
1286}
1287
1288pub fn solve_box_layout_ortho(
1290 data: &BoxLayoutOrthoData,
1291 repeater_indices: Slice<u32>,
1292) -> SharedVector<Coord> {
1293 let mut result = SharedVector::<Coord>::default();
1294 result.resize(data.cells.len() * 2 + repeater_indices.len(), 0 as _);
1295 if data.cells.is_empty() {
1296 return result;
1297 }
1298 let size_without_padding = data.size - data.padding.begin - data.padding.end;
1299 let mut generator = LayoutCacheGenerator::new(&repeater_indices, &mut result);
1300 for c in data.cells.iter() {
1301 let min =
1302 c.constraint.min.max(c.constraint.min_percent * size_without_padding / 100 as Coord);
1303 let max =
1304 c.constraint.max.min(c.constraint.max_percent * size_without_padding / 100 as Coord);
1305 let size = match data.cross_axis_alignment {
1306 CrossAxisAlignment::Stretch => size_without_padding,
1307 _ => c.constraint.preferred,
1308 }
1309 .min(max)
1310 .max(min);
1311 let pos = match data.cross_axis_alignment {
1312 CrossAxisAlignment::Stretch | CrossAxisAlignment::Start => data.padding.begin,
1313 CrossAxisAlignment::End => data.padding.begin + size_without_padding - size,
1314 CrossAxisAlignment::Center => {
1315 data.padding.begin + (size_without_padding - size) / 2 as Coord
1316 }
1317 };
1318 generator.add(pos, size);
1319 }
1320 result
1321}
1322
1323pub fn box_layout_info(
1325 cells: Slice<LayoutItemInfo>,
1326 spacing: Coord,
1327 padding: &Padding,
1328 alignment: LayoutAlignment,
1329) -> LayoutInfo {
1330 let count = cells.len();
1331 let is_stretch = alignment == LayoutAlignment::Stretch;
1332 if count < 1 {
1333 let mut info = LayoutInfo::default();
1334 info.min = padding.begin + padding.end;
1335 info.preferred = info.min;
1336 if is_stretch {
1337 info.max = info.min;
1338 }
1339 return info;
1340 };
1341 let extra_w = padding.begin + padding.end + spacing * (count - 1) as Coord;
1342 let min = cells.iter().map(|c| c.constraint.min).sum::<Coord>() + extra_w; let max = if is_stretch {
1344 (cells.iter().map(|c| c.constraint.max).fold(extra_w, Saturating::add)).max(min)
1345 } else {
1346 Coord::MAX
1347 }; let preferred = cells.iter().map(|c| c.constraint.preferred_bounded()).sum::<Coord>() + extra_w;
1349 let stretch = cells.iter().map(|c| c.constraint.stretch).sum::<f32>();
1350 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
1351}
1352
1353pub fn box_layout_info_ortho(cells: Slice<LayoutItemInfo>, padding: &Padding) -> LayoutInfo {
1354 let extra_w = padding.begin + padding.end;
1355 let mut fold =
1356 cells.iter().fold(LayoutInfo { stretch: f32::MAX, ..Default::default() }, |a, b| {
1357 a.merge(&b.constraint)
1358 });
1359 fold.max = fold.max.max(fold.min);
1360 fold.preferred = fold.preferred.clamp(fold.min, fold.max);
1361 fold.min += extra_w;
1362 fold.max = Saturating::add(fold.max, extra_w);
1363 fold.preferred += extra_w;
1364 fold.min_percent = 0 as _;
1367 fold.max_percent = 100 as _;
1368 fold
1369}
1370
1371mod flexbox_taffy {
1373 use super::{
1374 Coord, CrossAxisAlignment, FlexboxLayoutAlignContent, FlexboxLayoutAlignSelf,
1375 FlexboxLayoutItemInfo, FlexboxLayoutWrap as SlintFlexboxLayoutWrap, LayoutAlignment,
1376 Padding, Slice,
1377 };
1378 use alloc::vec::Vec;
1379 pub use taffy::prelude::FlexDirection as TaffyFlexDirection;
1380 use taffy::prelude::*;
1381
1382 pub struct FlexboxLayoutParams<'a> {
1384 pub cells_h: &'a Slice<'a, FlexboxLayoutItemInfo>,
1385 pub cells_v: &'a Slice<'a, FlexboxLayoutItemInfo>,
1386 pub spacing_h: Coord,
1387 pub spacing_v: Coord,
1388 pub padding_h: &'a Padding,
1389 pub padding_v: &'a Padding,
1390 pub alignment: LayoutAlignment,
1391 pub align_content: FlexboxLayoutAlignContent,
1392 pub cross_axis_alignment: CrossAxisAlignment,
1393 pub flex_wrap: SlintFlexboxLayoutWrap,
1394 pub flex_direction: TaffyFlexDirection,
1395 pub container_width: Option<Coord>,
1396 pub container_height: Option<Coord>,
1397 pub use_measure_for_cross_axis: bool,
1400 }
1401
1402 pub struct FlexboxTaffyBuilder {
1405 pub taffy: TaffyTree<usize>,
1406 pub children: Vec<NodeId>,
1407 pub container: NodeId,
1408 pub order_map: Vec<usize>,
1410 }
1411
1412 impl FlexboxTaffyBuilder {
1413 pub fn new(params: FlexboxLayoutParams) -> Self {
1415 let mut taffy = TaffyTree::<usize>::new();
1416
1417 let column_cross_cap = match params.flex_direction {
1421 TaffyFlexDirection::Column | TaffyFlexDirection::ColumnReverse => params
1422 .container_width
1423 .map(|cw| (cw - params.padding_h.begin - params.padding_h.end).max(0 as Coord)),
1424 _ => None,
1425 };
1426
1427 let mut children: Vec<NodeId> = params
1429 .cells_h
1430 .iter()
1431 .enumerate()
1432 .map(|(idx, cell_h)| {
1433 let cell_v = params.cells_v.get(idx);
1434 let h_constraint = &cell_h.constraint;
1435 let v_constraint = cell_v.map(|c| &c.constraint);
1436
1437 let preferred_width = h_constraint.preferred_bounded();
1439 let preferred_height =
1440 v_constraint.map(|vc| vc.preferred_bounded()).unwrap_or(0 as Coord);
1441
1442 let flex_basis = if cell_h.flex_basis >= 0 as Coord {
1444 Dimension::length(cell_h.flex_basis as _)
1445 } else {
1446 match params.flex_direction {
1447 TaffyFlexDirection::Row | TaffyFlexDirection::RowReverse => {
1448 Dimension::length(preferred_width as _)
1449 }
1450 TaffyFlexDirection::Column | TaffyFlexDirection::ColumnReverse => {
1451 Dimension::length(preferred_height as _)
1452 }
1453 }
1454 };
1455
1456 let max_width = h_constraint.max.min(column_cross_cap.unwrap_or(Coord::MAX));
1457 let max_width_dim = if max_width < Coord::MAX {
1458 Dimension::length(max_width as _)
1459 } else {
1460 Dimension::auto()
1461 };
1462
1463 taffy
1464 .new_leaf_with_context(
1465 Style {
1466 flex_basis,
1467 size: Size {
1468 width: match params.flex_direction {
1469 TaffyFlexDirection::Column
1470 | TaffyFlexDirection::ColumnReverse => {
1471 let stretches = match cell_h.flex_align_self {
1477 FlexboxLayoutAlignSelf::Auto => {
1478 params.cross_axis_alignment
1479 == CrossAxisAlignment::Stretch
1480 }
1481 FlexboxLayoutAlignSelf::Stretch => true,
1482 _ => false,
1483 };
1484 if stretches {
1485 Dimension::auto()
1486 } else if preferred_width > 0 as Coord {
1487 Dimension::length(preferred_width as _)
1488 } else {
1489 Dimension::auto()
1490 }
1491 }
1492 _ => Dimension::auto(),
1493 },
1494 height: match params.flex_direction {
1495 TaffyFlexDirection::Row
1496 | TaffyFlexDirection::RowReverse => {
1497 if params.use_measure_for_cross_axis {
1500 Dimension::auto()
1501 } else if preferred_height > 0 as Coord {
1502 Dimension::length(preferred_height as _)
1503 } else {
1504 Dimension::auto()
1505 }
1506 }
1507 _ => Dimension::auto(),
1508 },
1509 },
1510 min_size: Size {
1511 width: Dimension::length(h_constraint.min as _),
1512 height: Dimension::length(
1513 v_constraint.map(|vc| vc.min as f32).unwrap_or(0.0),
1514 ),
1515 },
1516 max_size: Size {
1517 width: max_width_dim,
1518 height: if let Some(vc) = v_constraint {
1519 if vc.max < Coord::MAX {
1520 Dimension::length(vc.max as _)
1521 } else {
1522 Dimension::auto()
1523 }
1524 } else {
1525 Dimension::auto()
1526 },
1527 },
1528 flex_grow: cell_h.flex_grow,
1529 flex_shrink: cell_h.flex_shrink,
1530 align_self: match cell_h.flex_align_self {
1531 FlexboxLayoutAlignSelf::Auto => None,
1532 FlexboxLayoutAlignSelf::Stretch => Some(AlignSelf::Stretch),
1533 FlexboxLayoutAlignSelf::Start => Some(AlignSelf::FlexStart),
1534 FlexboxLayoutAlignSelf::End => Some(AlignSelf::FlexEnd),
1535 FlexboxLayoutAlignSelf::Center => Some(AlignSelf::Center),
1536 },
1537 ..Default::default()
1538 },
1539 idx,
1540 )
1541 .unwrap() })
1543 .collect();
1544
1545 let has_order = params.cells_h.iter().any(|c| c.flex_order != 0);
1548 let order_map: Vec<usize> = if has_order {
1549 let mut indices: Vec<usize> = (0..children.len()).collect();
1550 indices.sort_by_key(|&i| params.cells_h.get(i).map_or(0, |c| c.flex_order));
1552 let sorted_children: Vec<NodeId> = indices.iter().map(|&i| children[i]).collect();
1553 children = sorted_children;
1554 indices
1555 } else {
1556 Vec::new()
1557 };
1558
1559 let container = taffy
1561 .new_with_children(
1562 Style {
1563 display: Display::Flex,
1564 flex_direction: params.flex_direction,
1565 flex_wrap: match params.flex_wrap {
1566 SlintFlexboxLayoutWrap::Wrap => FlexWrap::Wrap,
1567 SlintFlexboxLayoutWrap::NoWrap => FlexWrap::NoWrap,
1568 SlintFlexboxLayoutWrap::WrapReverse => FlexWrap::WrapReverse,
1569 },
1570 justify_content: Some(match params.alignment {
1571 LayoutAlignment::Start => AlignContent::FlexStart,
1574 LayoutAlignment::End => AlignContent::FlexEnd,
1575 LayoutAlignment::Center => AlignContent::Center,
1576 LayoutAlignment::Stretch => AlignContent::Stretch,
1577 LayoutAlignment::SpaceBetween => AlignContent::SpaceBetween,
1578 LayoutAlignment::SpaceAround => AlignContent::SpaceAround,
1579 LayoutAlignment::SpaceEvenly => AlignContent::SpaceEvenly,
1580 }),
1581 align_items: Some(match params.cross_axis_alignment {
1582 CrossAxisAlignment::Stretch => AlignItems::Stretch,
1583 CrossAxisAlignment::Start => AlignItems::FlexStart,
1584 CrossAxisAlignment::End => AlignItems::FlexEnd,
1585 CrossAxisAlignment::Center => AlignItems::Center,
1586 }),
1587 align_content: Some(match params.align_content {
1588 FlexboxLayoutAlignContent::Stretch => AlignContent::Stretch,
1589 FlexboxLayoutAlignContent::Start => AlignContent::FlexStart,
1590 FlexboxLayoutAlignContent::End => AlignContent::FlexEnd,
1591 FlexboxLayoutAlignContent::Center => AlignContent::Center,
1592 FlexboxLayoutAlignContent::SpaceBetween => AlignContent::SpaceBetween,
1593 FlexboxLayoutAlignContent::SpaceAround => AlignContent::SpaceAround,
1594 FlexboxLayoutAlignContent::SpaceEvenly => AlignContent::SpaceEvenly,
1595 }),
1596 gap: Size {
1597 width: LengthPercentage::length(params.spacing_h as _),
1598 height: LengthPercentage::length(params.spacing_v as _),
1599 },
1600 padding: Rect {
1601 left: LengthPercentage::length(params.padding_h.begin as _),
1602 right: LengthPercentage::length(params.padding_h.end as _),
1603 top: LengthPercentage::length(params.padding_v.begin as _),
1604 bottom: LengthPercentage::length(params.padding_v.end as _),
1605 },
1606 size: Size {
1607 width: params
1608 .container_width
1609 .map(|w| Dimension::length(w as _))
1610 .unwrap_or(Dimension::auto()),
1611 height: params
1612 .container_height
1613 .map(|h| Dimension::length(h as _))
1614 .unwrap_or(Dimension::auto()),
1615 },
1616 ..Default::default()
1617 },
1618 &children,
1619 )
1620 .unwrap(); Self { taffy, children, container, order_map }
1623 }
1624
1625 pub fn compute_layout(
1633 &mut self,
1634 available_width: Coord,
1635 available_height: Coord,
1636 mut measure: Option<
1637 &mut dyn FnMut(usize, Option<Coord>, Option<Coord>) -> (Coord, Coord),
1638 >,
1639 ) {
1640 let available_space = taffy::prelude::Size {
1641 width: if available_width < Coord::MAX {
1642 AvailableSpace::Definite(available_width as _)
1643 } else {
1644 AvailableSpace::MaxContent
1645 },
1646 height: if available_height < Coord::MAX {
1647 AvailableSpace::Definite(available_height as _)
1648 } else {
1649 AvailableSpace::MaxContent
1650 },
1651 };
1652 self.taffy
1653 .compute_layout_with_measure(
1654 self.container,
1655 available_space,
1656 |known_dimensions, _available_space, _node_id, node_context, _style| {
1657 if let (Some(measure), Some(&mut child_index)) =
1658 (measure.as_deref_mut(), node_context)
1659 {
1660 let known_w = known_dimensions.width.map(|w| w as Coord);
1661 let known_h = known_dimensions.height.map(|h| h as Coord);
1662 let (w, h) = measure(child_index, known_w, known_h);
1663 taffy::prelude::Size { width: w as f32, height: h as f32 }
1664 } else {
1665 taffy::prelude::Size::ZERO
1666 }
1667 },
1668 )
1669 .unwrap_or_else(|e| {
1670 crate::debug_log!("FlexboxLayout computation error: {}", e);
1671 });
1672 }
1673
1674 pub fn container_size(&self) -> (Coord, Coord) {
1676 let layout = self.taffy.layout(self.container).unwrap();
1677 (layout.size.width as Coord, layout.size.height as Coord)
1678 }
1679
1680 pub fn child_geometry(&self, idx: usize) -> (Coord, Coord, Coord, Coord) {
1682 let layout = self.taffy.layout(self.children[idx]).unwrap();
1683 (
1684 layout.location.x as Coord,
1685 layout.location.y as Coord,
1686 layout.size.width as Coord,
1687 layout.size.height as Coord,
1688 )
1689 }
1690
1691 pub fn original_index(&self, taffy_idx: usize) -> usize {
1693 if self.order_map.is_empty() { taffy_idx } else { self.order_map[taffy_idx] }
1694 }
1695 }
1696}
1697
1698struct FlexboxLayoutCacheGenerator<'a> {
1700 repeater_indices: &'a [u32],
1702 counter: usize,
1704 repeat_offset: usize,
1706 next_rep: usize,
1708 current_offset: usize,
1710 result: &'a mut SharedVector<Coord>,
1712}
1713
1714impl<'a> FlexboxLayoutCacheGenerator<'a> {
1715 fn new(repeater_indices: &'a [u32], result: &'a mut SharedVector<Coord>) -> Self {
1716 let total_repeated_cells: usize = repeater_indices
1718 .chunks(2)
1719 .map(|chunk| chunk.get(1).copied().unwrap_or(0) as usize)
1720 .sum();
1721 assert!(result.len() >= total_repeated_cells * 4);
1722 let repeat_offset = result.len() / 4 - total_repeated_cells;
1723 Self { repeater_indices, counter: 0, repeat_offset, next_rep: 0, current_offset: 0, result }
1724 }
1725
1726 fn add(&mut self, x: Coord, y: Coord, w: Coord, h: Coord) {
1727 let res = self.result.make_mut_slice();
1728 let o = loop {
1729 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
1730 let nr = *nr as usize;
1731 if nr == self.counter {
1732 res[self.current_offset * 4] = (self.repeat_offset * 4) as Coord;
1735 res[self.current_offset * 4 + 1] = (self.repeat_offset * 4 + 1) as Coord;
1736 res[self.current_offset * 4 + 2] = (self.repeat_offset * 4 + 2) as Coord;
1737 res[self.current_offset * 4 + 3] = (self.repeat_offset * 4 + 3) as Coord;
1738 self.current_offset += 1;
1739 }
1740 if self.counter >= nr {
1741 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
1742 if self.counter - nr == rep_count {
1743 self.repeat_offset += rep_count;
1745 self.next_rep += 1;
1746 continue;
1747 }
1748 let cell_in_rep = self.counter - nr;
1750 let offset = self.repeat_offset + cell_in_rep;
1751 break offset;
1752 }
1753 }
1754 self.current_offset += 1;
1755 break self.current_offset - 1;
1756 };
1757 res[o * 4] = x;
1758 res[o * 4 + 1] = y;
1759 res[o * 4 + 2] = w;
1760 res[o * 4 + 3] = h;
1761 self.counter += 1;
1762 }
1763}
1764
1765pub type FlexboxMeasureFn<'a> =
1772 Option<&'a mut dyn FnMut(usize, Option<Coord>, Option<Coord>) -> (Coord, Coord)>;
1773
1774pub fn solve_flexbox_layout(
1775 data: &FlexboxLayoutData,
1776 repeater_indices: Slice<u32>,
1777) -> SharedVector<Coord> {
1778 let mut measure = |child_index: usize,
1784 known_w: Option<Coord>,
1785 known_h: Option<Coord>|
1786 -> (Coord, Coord) {
1787 let w = known_w.unwrap_or_else(|| {
1788 data.cells_h.get(child_index).map_or(0 as Coord, |c| c.constraint.preferred_bounded())
1789 });
1790 let h = known_h.unwrap_or_else(|| {
1791 data.cells_v.get(child_index).map_or(0 as Coord, |c| c.constraint.preferred_bounded())
1792 });
1793 (w, h)
1794 };
1795 solve_flexbox_layout_with_measure(data, repeater_indices, Some(&mut measure))
1796}
1797
1798pub fn solve_flexbox_layout_with_measure(
1801 data: &FlexboxLayoutData,
1802 repeater_indices: Slice<u32>,
1803 measure: FlexboxMeasureFn<'_>,
1804) -> SharedVector<Coord> {
1805 let mut result = SharedVector::<Coord>::default();
1807 result.resize(data.cells_h.len() * 4 + repeater_indices.len() * 2, 0 as _);
1808
1809 if data.cells_h.is_empty() {
1810 return result;
1811 }
1812
1813 let taffy_direction = match data.direction {
1814 FlexboxLayoutDirection::Row => flexbox_taffy::TaffyFlexDirection::Row,
1815 FlexboxLayoutDirection::RowReverse => flexbox_taffy::TaffyFlexDirection::RowReverse,
1816 FlexboxLayoutDirection::Column => flexbox_taffy::TaffyFlexDirection::Column,
1817 FlexboxLayoutDirection::ColumnReverse => flexbox_taffy::TaffyFlexDirection::ColumnReverse,
1818 };
1819
1820 let (container_width, container_height) = (
1821 if data.width > 0 as Coord { Some(data.width) } else { None },
1822 if data.height > 0 as Coord { Some(data.height) } else { None },
1823 );
1824
1825 let use_measure = measure.is_some();
1826 let mut builder = flexbox_taffy::FlexboxTaffyBuilder::new(flexbox_taffy::FlexboxLayoutParams {
1827 cells_h: &data.cells_h,
1828 cells_v: &data.cells_v,
1829 spacing_h: data.spacing_h,
1830 spacing_v: data.spacing_v,
1831 padding_h: &data.padding_h,
1832 padding_v: &data.padding_v,
1833 alignment: data.alignment,
1834 align_content: data.align_content,
1835 cross_axis_alignment: data.cross_axis_alignment,
1836 flex_wrap: data.flex_wrap,
1837 flex_direction: taffy_direction,
1838 container_width,
1839 container_height,
1840 use_measure_for_cross_axis: use_measure,
1841 });
1842
1843 let (available_width, available_height) = match data.direction {
1844 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1845 (data.width, Coord::MAX)
1846 }
1847 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1848 (Coord::MAX, data.height)
1849 }
1850 };
1851
1852 builder.compute_layout(available_width, available_height, measure);
1853
1854 if builder.order_map.is_empty() {
1858 let mut generator = FlexboxLayoutCacheGenerator::new(&repeater_indices, &mut result);
1859 for idx in 0..data.cells_h.len() {
1860 let (x, y, w, h) = builder.child_geometry(idx);
1861 generator.add(x, y, w, h);
1862 }
1863 } else {
1864 let count = data.cells_h.len();
1865 let mut geom = alloc::vec![(0 as Coord, 0 as Coord, 0 as Coord, 0 as Coord); count];
1866 for taffy_idx in 0..count {
1867 let orig_idx = builder.original_index(taffy_idx);
1868 geom[orig_idx] = builder.child_geometry(taffy_idx);
1869 }
1870 let mut generator = FlexboxLayoutCacheGenerator::new(&repeater_indices, &mut result);
1871 for (x, y, w, h) in geom {
1872 generator.add(x, y, w, h);
1873 }
1874 }
1875
1876 result
1877}
1878
1879pub fn flexbox_layout_info_main_axis(
1882 cells: Slice<FlexboxLayoutItemInfo>,
1883 spacing: Coord,
1884 padding: &Padding,
1885 flex_wrap: FlexboxLayoutWrap,
1886) -> LayoutInfo {
1887 let extra_pad = padding.begin + padding.end;
1888 if cells.is_empty() {
1889 return LayoutInfo {
1890 min: extra_pad,
1891 preferred: extra_pad,
1892 max: extra_pad,
1893 ..Default::default()
1894 };
1895 }
1896 let num_spacings = cells.len().saturating_sub(1) as Coord;
1897 let min = if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) {
1898 cells.iter().map(|c| c.constraint.min).sum::<Coord>() + spacing * num_spacings + extra_pad
1899 } else {
1900 cells.iter().map(|c| c.constraint.min).fold(0.0 as Coord, |a, b| a.max(b)) + extra_pad
1902 };
1903 let preferred = if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) {
1904 cells.iter().map(|c| c.constraint.preferred_bounded()).sum::<Coord>()
1906 + spacing * num_spacings
1907 + extra_pad
1908 } else {
1909 let total_area: Coord = cells
1913 .iter()
1914 .map(|c| {
1915 let w = c.constraint.preferred_bounded();
1916 w * w
1917 })
1918 .sum();
1919 let count = cells.len();
1920 Float::sqrt(total_area as f32) as Coord + spacing * (count - 1) as Coord + extra_pad
1921 };
1922 let stretch = cells.iter().map(|c| c.constraint.stretch).sum::<f32>();
1923 LayoutInfo {
1924 min,
1925 max: Coord::MAX,
1926 min_percent: 0 as _,
1927 max_percent: 100 as _,
1928 preferred,
1929 stretch,
1930 }
1931}
1932
1933pub fn flexbox_layout_info_cross_axis(
1942 cells_h: Slice<FlexboxLayoutItemInfo>,
1943 cells_v: Slice<FlexboxLayoutItemInfo>,
1944 spacing_h: Coord,
1945 spacing_v: Coord,
1946 padding_h: &Padding,
1947 padding_v: &Padding,
1948 direction: FlexboxLayoutDirection,
1949 flex_wrap: FlexboxLayoutWrap,
1950 constraint_size: Coord,
1951) -> LayoutInfo {
1952 if cells_h.is_empty() {
1953 assert!(cells_v.is_empty());
1954 let orientation = match direction {
1955 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1956 Orientation::Vertical
1957 }
1958 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1959 Orientation::Horizontal
1960 }
1961 };
1962 let padding = match orientation {
1963 Orientation::Horizontal => padding_h,
1964 Orientation::Vertical => padding_v,
1965 };
1966 let pad = padding.begin + padding.end;
1967 return LayoutInfo { min: pad, preferred: pad, max: pad, ..Default::default() };
1968 }
1969
1970 let cross_cells = match direction {
1972 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => &cells_v,
1973 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => &cells_h,
1974 };
1975
1976 let (main_cells, main_spacing, main_padding) = match direction {
1979 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1980 (&cells_h, spacing_h, padding_h)
1981 }
1982 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1983 (&cells_v, spacing_v, padding_v)
1984 }
1985 };
1986 let main_extra_pad = main_padding.begin + main_padding.end;
1987 let main_axis_constraint = if constraint_size > 0 as Coord && constraint_size < Coord::MAX {
1988 constraint_size
1990 } else if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) || constraint_size >= Coord::MAX {
1991 Coord::MAX
1998 } else {
1999 let total_area: Coord = main_cells
2002 .iter()
2003 .zip(cross_cells.iter())
2004 .map(|(m, c)| m.constraint.preferred_bounded() * c.constraint.preferred_bounded())
2005 .sum();
2006 let count = main_cells.len();
2007 Float::sqrt(total_area as f32) as Coord
2008 + main_spacing * (count - 1) as Coord
2009 + main_extra_pad
2010 };
2011
2012 let taffy_direction = match direction {
2013 FlexboxLayoutDirection::Row => flexbox_taffy::TaffyFlexDirection::Row,
2014 FlexboxLayoutDirection::RowReverse => flexbox_taffy::TaffyFlexDirection::RowReverse,
2015 FlexboxLayoutDirection::Column => flexbox_taffy::TaffyFlexDirection::Column,
2016 FlexboxLayoutDirection::ColumnReverse => flexbox_taffy::TaffyFlexDirection::ColumnReverse,
2017 };
2018
2019 let (container_width, container_height) = match direction {
2020 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
2021 (Some(main_axis_constraint), None)
2022 }
2023 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
2024 (None, Some(main_axis_constraint))
2025 }
2026 };
2027
2028 let mut builder = flexbox_taffy::FlexboxTaffyBuilder::new(flexbox_taffy::FlexboxLayoutParams {
2029 cells_h: &cells_h,
2030 cells_v: &cells_v,
2031 spacing_h,
2032 spacing_v,
2033 padding_h,
2034 padding_v,
2035 alignment: LayoutAlignment::Start,
2036 align_content: FlexboxLayoutAlignContent::Stretch,
2037 cross_axis_alignment: CrossAxisAlignment::Stretch,
2038 flex_wrap,
2039 flex_direction: taffy_direction,
2040 container_width,
2041 container_height,
2042 use_measure_for_cross_axis: false,
2043 });
2044
2045 let (available_width, available_height) = match direction {
2046 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
2047 (main_axis_constraint, Coord::MAX)
2048 }
2049 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
2050 (Coord::MAX, main_axis_constraint)
2051 }
2052 };
2053
2054 builder.compute_layout(available_width, available_height, None);
2055
2056 let (total_width, total_height) = builder.container_size();
2057 let cross_size = match direction {
2058 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => total_height,
2059 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => total_width,
2060 };
2061
2062 LayoutInfo {
2063 min: cross_size,
2064 max: Coord::MAX,
2065 min_percent: 0 as _,
2066 max_percent: 100 as _,
2067 preferred: cross_size,
2068 stretch: 0.0,
2069 }
2070}
2071
2072#[cfg(feature = "ffi")]
2073pub(crate) mod ffi {
2074 #![allow(unsafe_code)]
2075
2076 use super::*;
2077
2078 #[unsafe(no_mangle)]
2079 pub extern "C" fn slint_organize_grid_layout(
2080 input_data: Slice<GridLayoutInputData>,
2081 repeater_indices: Slice<u32>,
2082 repeater_steps: Slice<u32>,
2083 result: &mut GridLayoutOrganizedData,
2084 ) {
2085 *result = super::organize_grid_layout(input_data, repeater_indices, repeater_steps);
2086 }
2087
2088 #[unsafe(no_mangle)]
2089 pub extern "C" fn slint_organize_dialog_button_layout(
2090 input_data: Slice<GridLayoutInputData>,
2091 dialog_button_roles: Slice<DialogButtonRole>,
2092 result: &mut GridLayoutOrganizedData,
2093 ) {
2094 *result = super::organize_dialog_button_layout(input_data, dialog_button_roles);
2095 }
2096
2097 #[unsafe(no_mangle)]
2098 pub extern "C" fn slint_solve_grid_layout(
2099 data: &GridLayoutData,
2100 constraints: Slice<LayoutItemInfo>,
2101 orientation: Orientation,
2102 repeater_indices: Slice<u32>,
2103 repeater_steps: Slice<u32>,
2104 result: &mut SharedVector<Coord>,
2105 ) {
2106 *result = super::solve_grid_layout(
2107 data,
2108 constraints,
2109 orientation,
2110 repeater_indices,
2111 repeater_steps,
2112 )
2113 }
2114
2115 #[unsafe(no_mangle)]
2116 pub extern "C" fn slint_grid_layout_info(
2117 organized_data: &GridLayoutOrganizedData,
2118 constraints: Slice<LayoutItemInfo>,
2119 repeater_indices: Slice<u32>,
2120 repeater_steps: Slice<u32>,
2121 spacing: Coord,
2122 padding: &Padding,
2123 orientation: Orientation,
2124 ) -> LayoutInfo {
2125 super::grid_layout_info(
2126 organized_data.clone(),
2127 constraints,
2128 repeater_indices,
2129 repeater_steps,
2130 spacing,
2131 padding,
2132 orientation,
2133 )
2134 }
2135
2136 #[unsafe(no_mangle)]
2137 pub extern "C" fn slint_solve_box_layout(
2138 data: &BoxLayoutData,
2139 repeater_indices: Slice<u32>,
2140 result: &mut SharedVector<Coord>,
2141 ) {
2142 *result = super::solve_box_layout(data, repeater_indices)
2143 }
2144
2145 #[unsafe(no_mangle)]
2146 pub extern "C" fn slint_solve_box_layout_ortho(
2147 data: &BoxLayoutOrthoData,
2148 repeater_indices: Slice<u32>,
2149 result: &mut SharedVector<Coord>,
2150 ) {
2151 *result = super::solve_box_layout_ortho(data, repeater_indices)
2152 }
2153
2154 #[unsafe(no_mangle)]
2155 pub extern "C" fn slint_box_layout_info(
2157 cells: Slice<LayoutItemInfo>,
2158 spacing: Coord,
2159 padding: &Padding,
2160 alignment: LayoutAlignment,
2161 ) -> LayoutInfo {
2162 super::box_layout_info(cells, spacing, padding, alignment)
2163 }
2164
2165 #[unsafe(no_mangle)]
2166 pub extern "C" fn slint_box_layout_info_ortho(
2168 cells: Slice<LayoutItemInfo>,
2169 padding: &Padding,
2170 ) -> LayoutInfo {
2171 super::box_layout_info_ortho(cells, padding)
2172 }
2173
2174 pub type FlexboxMeasureFnC = unsafe extern "C" fn(
2178 user_data: *mut core::ffi::c_void,
2179 child_index: usize,
2180 known_width: Coord,
2181 known_height: Coord,
2182 out_width: *mut Coord,
2183 out_height: *mut Coord,
2184 );
2185
2186 #[unsafe(no_mangle)]
2187 #[allow(unsafe_code)]
2188 pub extern "C" fn slint_solve_flexbox_layout(
2189 data: &FlexboxLayoutData,
2190 repeater_indices: Slice<u32>,
2191 result: &mut SharedVector<Coord>,
2192 measure_fn: *const core::ffi::c_void,
2193 measure_user_data: *mut core::ffi::c_void,
2194 ) {
2195 const {
2198 assert!(
2199 core::mem::size_of::<*const core::ffi::c_void>()
2200 == core::mem::size_of::<FlexboxMeasureFnC>()
2201 );
2202 }
2203 let measure_fn: Option<FlexboxMeasureFnC> = if measure_fn.is_null() {
2204 None
2205 } else {
2206 Some(unsafe {
2207 core::mem::transmute::<*const core::ffi::c_void, FlexboxMeasureFnC>(measure_fn)
2208 })
2209 };
2210 if let Some(c_measure) = measure_fn {
2211 let mut measure = |child_index: usize,
2212 known_w: Option<Coord>,
2213 known_h: Option<Coord>|
2214 -> (Coord, Coord) {
2215 let mut out_w: Coord = 0 as _;
2216 let mut out_h: Coord = 0 as _;
2217 unsafe {
2220 c_measure(
2221 measure_user_data,
2222 child_index,
2223 known_w.unwrap_or(-1 as _),
2224 known_h.unwrap_or(-1 as _),
2225 &mut out_w,
2226 &mut out_h,
2227 );
2228 }
2229 (out_w, out_h)
2230 };
2231 *result = super::solve_flexbox_layout_with_measure(
2232 data,
2233 repeater_indices,
2234 Some(&mut measure),
2235 );
2236 } else {
2237 *result = super::solve_flexbox_layout(data, repeater_indices);
2238 }
2239 }
2240
2241 #[unsafe(no_mangle)]
2242 pub extern "C" fn slint_flexbox_layout_info_main_axis(
2244 cells: Slice<FlexboxLayoutItemInfo>,
2245 spacing: Coord,
2246 padding: &Padding,
2247 flex_wrap: FlexboxLayoutWrap,
2248 ) -> LayoutInfo {
2249 super::flexbox_layout_info_main_axis(cells, spacing, padding, flex_wrap)
2250 }
2251
2252 #[unsafe(no_mangle)]
2253 pub extern "C" fn slint_flexbox_layout_info_cross_axis(
2255 cells_h: Slice<FlexboxLayoutItemInfo>,
2256 cells_v: Slice<FlexboxLayoutItemInfo>,
2257 spacing_h: Coord,
2258 spacing_v: Coord,
2259 padding_h: &Padding,
2260 padding_v: &Padding,
2261 direction: FlexboxLayoutDirection,
2262 flex_wrap: FlexboxLayoutWrap,
2263 constraint_size: Coord,
2264 ) -> LayoutInfo {
2265 super::flexbox_layout_info_cross_axis(
2266 cells_h,
2267 cells_v,
2268 spacing_h,
2269 spacing_v,
2270 padding_h,
2271 padding_v,
2272 direction,
2273 flex_wrap,
2274 constraint_size,
2275 )
2276 }
2277}
2278
2279#[cfg(test)]
2280mod tests {
2281 use super::*;
2282
2283 fn collect_from_organized_data(
2284 organized_data: &GridLayoutOrganizedData,
2285 num_cells: usize,
2286 repeater_indices: Slice<u32>,
2287 repeater_steps: Slice<u32>,
2288 ) -> Vec<(u16, u16, u16, u16)> {
2289 let mut result = Vec::new();
2290 for i in 0..num_cells {
2291 let col_and_span = organized_data.col_or_row_and_span(
2292 i,
2293 Orientation::Horizontal,
2294 &repeater_indices,
2295 &repeater_steps,
2296 );
2297 let row_and_span = organized_data.col_or_row_and_span(
2298 i,
2299 Orientation::Vertical,
2300 &repeater_indices,
2301 &repeater_steps,
2302 );
2303 result.push((col_and_span.0, col_and_span.1, row_and_span.0, row_and_span.1));
2304 }
2305 result
2306 }
2307
2308 #[test]
2309 fn test_organized_data_generator_2_fixed_cells() {
2310 let mut result = GridLayoutOrganizedData::default();
2312 let num_cells = 2;
2313 let mut generator = OrganizedDataGenerator::new(&[], &[], num_cells, 0, 0, &mut result);
2314 generator.add(0, 1, 0, 1);
2315 generator.add(1, 2, 0, 3);
2316 assert_eq!(result.as_slice(), &[0, 1, 0, 1, 1, 2, 0, 3]);
2317
2318 let repeater_indices = Slice::from_slice(&[]);
2319 let empty_steps = Slice::from_slice(&[]);
2320 let collected_data =
2321 collect_from_organized_data(&result, num_cells, repeater_indices, empty_steps);
2322 assert_eq!(collected_data.as_slice(), &[(0, 1, 0, 1), (1, 2, 0, 3)]);
2323
2324 assert_eq!(
2325 result.max_value(num_cells, Orientation::Horizontal, &repeater_indices, &empty_steps),
2326 3
2327 );
2328 assert_eq!(
2329 result.max_value(num_cells, Orientation::Vertical, &repeater_indices, &empty_steps),
2330 3
2331 );
2332 }
2333
2334 #[test]
2335 fn test_organized_data_generator_1_fixed_cell_1_repeater() {
2336 let mut result = GridLayoutOrganizedData::default();
2338 let num_cells = 4;
2339 let repeater_indices = &[1u32, 3u32];
2340 let mut generator =
2341 OrganizedDataGenerator::new(repeater_indices, &[], 1, 1, 3, &mut result);
2342 generator.add(0, 1, 0, 2); generator.add(1, 2, 1, 3); generator.add(1, 1, 2, 4);
2345 generator.add(2, 2, 3, 5);
2346 assert_eq!(
2347 result.as_slice(),
2348 &[
2349 0, 1, 0, 2, 8, 4, 0, 0, 1, 2, 1, 3, 1, 1, 2, 4, 2, 2, 3, 5, ]
2355 );
2356 let repeater_indices = Slice::from_slice(repeater_indices);
2357 let empty_steps = Slice::from_slice(&[]);
2358 let collected_data =
2359 collect_from_organized_data(&result, num_cells, repeater_indices, empty_steps);
2360 assert_eq!(
2361 collected_data.as_slice(),
2362 &[(0, 1, 0, 2), (1, 2, 1, 3), (1, 1, 2, 4), (2, 2, 3, 5)]
2363 );
2364
2365 assert_eq!(
2366 result.max_value(num_cells, Orientation::Horizontal, &repeater_indices, &empty_steps),
2367 4
2368 );
2369 assert_eq!(
2370 result.max_value(num_cells, Orientation::Vertical, &repeater_indices, &empty_steps),
2371 8
2372 );
2373 }
2374
2375 #[test]
2376
2377 fn test_organize_data_with_auto_and_spans() {
2378 let auto = i_slint_common::ROW_COL_AUTO;
2379 let input = std::vec![
2380 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 2., rowspan: -1. },
2381 GridLayoutInputData { new_row: false, col: auto, row: auto, colspan: 1., rowspan: 2. },
2382 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 2., rowspan: 1. },
2383 GridLayoutInputData { new_row: true, col: -2., row: 80000., colspan: 2., rowspan: 1. },
2384 ];
2385 let repeater_indices = Slice::from_slice(&[]);
2386 let (organized_data, errors) = organize_grid_layout_impl(
2387 Slice::from_slice(&input),
2388 repeater_indices,
2389 Slice::from_slice(&[]),
2390 );
2391 assert_eq!(
2392 organized_data.as_slice(),
2393 &[
2394 0, 2, 0, 0, 2, 1, 0, 2, 0, 2, 1, 1, 0, 2, 65535, 1, ]
2399 );
2400 assert_eq!(errors.len(), 3);
2401 assert_eq!(errors[0], "cell rowspan -1 is negative, clamping to 0");
2403 assert_eq!(errors[1], "cell row 80000 is too large, clamping to 65535");
2404 assert_eq!(errors[2], "cell col -2 is negative, clamping to 0");
2405 let empty_steps = Slice::from_slice(&[]);
2406 let collected_data = collect_from_organized_data(
2407 &organized_data,
2408 input.len(),
2409 repeater_indices,
2410 empty_steps,
2411 );
2412 assert_eq!(
2413 collected_data.as_slice(),
2414 &[(0, 2, 0, 0), (2, 1, 0, 2), (0, 2, 1, 1), (0, 2, 65535, 1)]
2415 );
2416 assert_eq!(
2417 organized_data.max_value(3, Orientation::Horizontal, &repeater_indices, &empty_steps),
2418 3
2419 );
2420 assert_eq!(
2421 organized_data.max_value(3, Orientation::Vertical, &repeater_indices, &empty_steps),
2422 2
2423 );
2424 }
2425
2426 #[test]
2427 fn test_organize_data_1_empty_repeater() {
2428 let auto = i_slint_common::ROW_COL_AUTO;
2430 let cell =
2431 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2432 let input = std::vec![cell];
2433 let repeater_indices = Slice::from_slice(&[1u32, 0u32]);
2434 let (organized_data, errors) = organize_grid_layout_impl(
2435 Slice::from_slice(&input),
2436 repeater_indices,
2437 Slice::from_slice(&[]),
2438 );
2439 assert_eq!(
2440 organized_data.as_slice(),
2441 &[
2442 0, 1, 0, 1, 0, 0, 0, 0
2444 ] );
2446 assert_eq!(errors.len(), 0);
2447 let empty_steps = Slice::from_slice(&[]);
2448 let collected_data = collect_from_organized_data(
2449 &organized_data,
2450 input.len(),
2451 repeater_indices,
2452 empty_steps,
2453 );
2454 assert_eq!(collected_data.as_slice(), &[(0, 1, 0, 1)]);
2455 assert_eq!(
2456 organized_data.max_value(1, Orientation::Horizontal, &repeater_indices, &empty_steps),
2457 1
2458 );
2459 }
2460
2461 #[test]
2462 fn test_organize_data_4_repeaters() {
2463 let auto = i_slint_common::ROW_COL_AUTO;
2464 let mut cell =
2465 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2466 let mut input = std::vec![cell.clone()];
2467 for _ in 0..8 {
2468 cell.new_row = false;
2469 input.push(cell.clone());
2470 }
2471 let repeater_indices = Slice::from_slice(&[0u32, 0u32, 1u32, 4u32, 6u32, 2u32, 8u32, 0u32]);
2472 let (organized_data, errors) = organize_grid_layout_impl(
2473 Slice::from_slice(&input),
2474 repeater_indices,
2475 Slice::from_slice(&[]),
2476 );
2477 assert_eq!(
2478 organized_data.as_slice(),
2479 &[
2480 28, 4, 0, 0, 0, 1, 0, 1, 28, 4, 0, 0, 5, 1, 0, 1, 44, 4, 0, 0, 52, 4, 0, 0, 8, 1, 0, 1, 1, 1, 0, 1, 2, 1, 0, 1, 3, 1, 0, 1, 4, 1, 0, 1, 6, 1, 0, 1, 7, 1, 0, 1, ]
2494 );
2495 assert_eq!(errors.len(), 0);
2496 let empty_steps = Slice::from_slice(&[]);
2497 let collected_data = collect_from_organized_data(
2498 &organized_data,
2499 input.len(),
2500 repeater_indices,
2501 empty_steps,
2502 );
2503 assert_eq!(
2504 collected_data.as_slice(),
2505 &[
2506 (0, 1, 0, 1),
2507 (1, 1, 0, 1),
2508 (2, 1, 0, 1),
2509 (3, 1, 0, 1),
2510 (4, 1, 0, 1),
2511 (5, 1, 0, 1),
2512 (6, 1, 0, 1),
2513 (7, 1, 0, 1),
2514 (8, 1, 0, 1),
2515 ]
2516 );
2517 let empty_steps = Slice::from_slice(&[]);
2518 assert_eq!(
2519 organized_data.max_value(
2520 input.len(),
2521 Orientation::Horizontal,
2522 &repeater_indices,
2523 &empty_steps
2524 ),
2525 9
2526 );
2527 }
2528
2529 #[test]
2530 fn test_organize_data_repeated_rows() {
2531 let auto = i_slint_common::ROW_COL_AUTO;
2532 let mut input = Vec::new();
2533 let num_rows: u32 = 3;
2534 let num_columns: u32 = 2;
2535 for _ in 0..num_rows {
2537 let mut cell = GridLayoutInputData {
2538 new_row: true,
2539 col: auto,
2540 row: auto,
2541 colspan: 1.,
2542 rowspan: 1.,
2543 };
2544 input.push(cell.clone());
2545 cell.new_row = false;
2546 input.push(cell.clone());
2547 }
2548 let repeater_indices_arr = [0_u32, num_rows];
2550 let repeater_steps_arr = [num_columns];
2551 let repeater_steps = Slice::from_slice(&repeater_steps_arr);
2552 let repeater_indices = Slice::from_slice(&repeater_indices_arr);
2553 let (organized_data, errors) =
2554 organize_grid_layout_impl(Slice::from_slice(&input), repeater_indices, repeater_steps);
2555 assert_eq!(
2556 organized_data.as_slice(),
2557 &[
2558 4, 8, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 1, 1, 2, 1, ]
2563 );
2564 assert_eq!(errors.len(), 0);
2565 let collected_data = collect_from_organized_data(
2566 &organized_data,
2567 input.len(),
2568 repeater_indices,
2569 repeater_steps,
2570 );
2571 assert_eq!(
2572 collected_data.as_slice(),
2573 &[(0, 1, 0, 1), (1, 1, 0, 1), (0, 1, 1, 1), (1, 1, 1, 1), (0, 1, 2, 1), (1, 1, 2, 1),]
2575 );
2576 assert_eq!(
2577 organized_data.max_value(
2578 input.len(),
2579 Orientation::Horizontal,
2580 &repeater_indices,
2581 &repeater_steps
2582 ),
2583 2
2584 );
2585 assert_eq!(
2586 organized_data.max_value(
2587 input.len(),
2588 Orientation::Vertical,
2589 &repeater_indices,
2590 &repeater_steps
2591 ),
2592 3
2593 );
2594
2595 let mut layout_cache_v = SharedVector::<Coord>::default();
2597 let mut generator = GridLayoutCacheGenerator::new(
2598 repeater_indices.as_slice(),
2599 repeater_steps.as_slice(),
2600 0, 1, 6, &mut layout_cache_v,
2604 );
2605 generator.add(0., 50.);
2607 generator.add(0., 50.);
2608 generator.add(50., 50.);
2610 generator.add(50., 50.);
2611 generator.add(100., 50.);
2613 generator.add(100., 50.);
2614 assert_eq!(
2615 layout_cache_v.as_slice(),
2616 &[
2617 2., 4., 0., 50., 0., 50., 50., 50., 50., 50., 100., 50., 100., 50., ]
2622 );
2623
2624 let layout_cache_v_access = |jump_index: usize,
2626 repeater_index: usize,
2627 stride: usize,
2628 child_offset: usize|
2629 -> Coord {
2630 let base = layout_cache_v[jump_index] as usize;
2631 let data_idx = base + repeater_index * stride + child_offset;
2632 layout_cache_v[data_idx]
2633 };
2634 assert_eq!(layout_cache_v_access(0, 0, 4, 0), 0.);
2637 assert_eq!(layout_cache_v_access(0, 1, 4, 0), 50.);
2638 assert_eq!(layout_cache_v_access(0, 2, 4, 0), 100.);
2639 assert_eq!(layout_cache_v_access(0, 0, 4, 2), 0.);
2641 assert_eq!(layout_cache_v_access(0, 1, 4, 2), 50.);
2642 assert_eq!(layout_cache_v_access(0, 2, 4, 2), 100.);
2643 }
2644
2645 #[test]
2646 fn test_organize_data_repeated_rows_multiple_repeaters() {
2647 let auto = i_slint_common::ROW_COL_AUTO;
2648 let mut input = Vec::new();
2649 let num_rows: u32 = 5;
2650 let mut cell =
2651 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2652 for _ in 0..3 {
2654 cell.new_row = true;
2655 input.push(cell.clone());
2656 cell.new_row = false;
2657 input.push(cell.clone());
2658 }
2659 for _ in 0..2 {
2661 cell.new_row = true;
2662 input.push(cell.clone());
2663 cell.new_row = false;
2664 input.push(cell.clone());
2665 cell.new_row = false;
2666 input.push(cell.clone());
2667 }
2668 let repeater_indices_arr = [0_u32, 3, 6, 2];
2671 let repeater_steps_arr = [2, 3];
2672 let repeater_steps = Slice::from_slice(&repeater_steps_arr);
2673 let repeater_indices = Slice::from_slice(&repeater_indices_arr);
2674 let (organized_data, errors) =
2675 organize_grid_layout_impl(Slice::from_slice(&input), repeater_indices, repeater_steps);
2676 assert_eq!(
2677 organized_data.as_slice(),
2678 &[
2679 8, 8, 0, 0, 32, 12, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 1, 1, 2, 1, 0, 1, 3, 1, 1, 1, 3, 1, 2, 1, 3, 1, 0, 1, 4, 1, 1, 1, 4, 1, 2, 1, 4, 1, ]
2689 );
2690 assert_eq!(errors.len(), 0);
2691 let collected_data = collect_from_organized_data(
2692 &organized_data,
2693 input.len(),
2694 repeater_indices,
2695 repeater_steps,
2696 );
2697 assert_eq!(
2698 collected_data.as_slice(),
2699 &[
2701 (0, 1, 0, 1),
2702 (1, 1, 0, 1),
2703 (0, 1, 1, 1),
2704 (1, 1, 1, 1),
2705 (0, 1, 2, 1),
2706 (1, 1, 2, 1),
2707 (0, 1, 3, 1),
2708 (1, 1, 3, 1),
2709 (2, 1, 3, 1),
2710 (0, 1, 4, 1),
2711 (1, 1, 4, 1),
2712 (2, 1, 4, 1)
2713 ]
2714 );
2715 assert_eq!(
2716 organized_data.max_value(
2717 input.len(),
2718 Orientation::Horizontal,
2719 &repeater_indices,
2720 &repeater_steps
2721 ),
2722 3 );
2724 assert_eq!(
2725 organized_data.max_value(
2726 input.len(),
2727 Orientation::Vertical,
2728 &repeater_indices,
2729 &repeater_steps
2730 ),
2731 num_rows as u16 );
2733
2734 let mut layout_cache_v = SharedVector::<Coord>::default();
2736 let mut generator = GridLayoutCacheGenerator::new(
2737 repeater_indices.as_slice(),
2738 repeater_steps.as_slice(),
2739 0, 2, 12, &mut layout_cache_v,
2743 );
2744 generator.add(0., 50.);
2746 generator.add(0., 50.);
2747 generator.add(50., 50.);
2749 generator.add(50., 50.);
2750 generator.add(100., 50.);
2752 generator.add(100., 50.);
2753 generator.add(150., 50.);
2755 generator.add(150., 50.);
2756 generator.add(150., 50.);
2757 generator.add(200., 50.);
2759 generator.add(200., 50.);
2760 generator.add(200., 50.);
2761 assert_eq!(
2762 layout_cache_v.as_slice(),
2763 &[
2764 4., 4., 16., 6., 0., 50., 0., 50., 50., 50., 50., 50., 100., 50., 100., 50., 150., 50., 150., 50., 150., 50., 200., 50., 200., 50., 200., 50., ]
2772 );
2773
2774 let layout_cache_v_access = |jump_index: usize,
2776 repeater_index: usize,
2777 stride: usize,
2778 child_offset: usize|
2779 -> Coord {
2780 let base = layout_cache_v[jump_index] as usize;
2781 let data_idx = base + repeater_index * stride + child_offset;
2782 layout_cache_v[data_idx]
2783 };
2784 assert_eq!(layout_cache_v_access(0, 0, 4, 0), 0.);
2786 assert_eq!(layout_cache_v_access(0, 1, 4, 0), 50.);
2787 assert_eq!(layout_cache_v_access(0, 2, 4, 0), 100.);
2788 assert_eq!(layout_cache_v_access(0, 0, 4, 2), 0.);
2790 assert_eq!(layout_cache_v_access(0, 1, 4, 2), 50.);
2791 assert_eq!(layout_cache_v_access(0, 2, 4, 2), 100.);
2792 assert_eq!(layout_cache_v_access(2, 0, 6, 0), 150.);
2794 assert_eq!(layout_cache_v_access(2, 1, 6, 0), 200.);
2795 assert_eq!(layout_cache_v_access(2, 0, 6, 4), 150.);
2797 assert_eq!(layout_cache_v_access(2, 1, 6, 4), 200.);
2798 }
2799
2800 #[test]
2801 fn test_layout_cache_generator_2_fixed_cells() {
2802 let mut result = SharedVector::<Coord>::default();
2804 result.resize(2 * 2, 0 as _);
2805 let mut generator = LayoutCacheGenerator::new(&[], &mut result);
2806 generator.add(0., 50.); generator.add(80., 50.); assert_eq!(result.as_slice(), &[0., 50., 80., 50.]);
2809 }
2810
2811 #[test]
2812 fn test_layout_cache_generator_1_fixed_cell_1_repeater() {
2813 let mut result = SharedVector::<Coord>::default();
2815 let repeater_indices = &[1, 3];
2816 result.resize(4 * 2 + repeater_indices.len(), 0 as _);
2817 let mut generator = LayoutCacheGenerator::new(repeater_indices, &mut result);
2818 generator.add(0., 50.); generator.add(80., 50.); generator.add(160., 50.);
2821 generator.add(240., 50.);
2822 assert_eq!(
2823 result.as_slice(),
2824 &[
2825 0., 50., 4., 5., 80., 50., 160., 50., 240., 50. ]
2829 );
2830 }
2831
2832 #[test]
2833 fn test_layout_cache_generator_4_repeaters() {
2834 let mut result = SharedVector::<Coord>::default();
2836 let repeater_indices = &[1, 0, 1, 4, 6, 2, 8, 0];
2837 result.resize(8 * 2 + repeater_indices.len(), 0 as _);
2838 let mut generator = LayoutCacheGenerator::new(repeater_indices, &mut result);
2839 generator.add(0., 50.); generator.add(80., 10.); generator.add(160., 10.);
2842 generator.add(240., 10.);
2843 generator.add(320., 10.); generator.add(400., 80.); generator.add(500., 20.); generator.add(600., 20.); assert_eq!(
2848 result.as_slice(),
2849 &[
2850 0., 50., 12., 13., 12., 13., 400., 80., 20., 21., 0., 0., 80., 10., 160., 10., 240., 10., 320., 10., 500., 20., 600., 20. ]
2859 );
2860 }
2861}