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 );
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 = &mut layout_data
385 [(col_or_row as usize)..(col_or_row as usize + 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 ) -> usize {
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 as usize + span.max(1) as usize);
574 }
575 max
576 }
577}
578
579struct OrganizedDataGenerator<'a> {
586 repeater_indices: &'a [u32],
588 repeater_steps: &'a [u32],
589 counter: usize,
591 repeat_u16_offset: usize,
593 next_rep: usize,
595 current_offset: usize,
597 result: &'a mut GridLayoutOrganizedData,
599}
600
601impl<'a> OrganizedDataGenerator<'a> {
602 fn new(
603 repeater_indices: &'a [u32],
604 repeater_steps: &'a [u32],
605 static_cells: usize,
606 num_repeaters: usize,
607 total_repeated_cells_count: usize,
608 result: &'a mut GridLayoutOrganizedData,
609 ) -> Self {
610 result.resize((static_cells + num_repeaters + total_repeated_cells_count) * 4, 0 as _);
611 let repeat_u16_offset = (static_cells + num_repeaters) * 4;
612 Self {
613 repeater_indices,
614 repeater_steps,
615 counter: 0,
616 repeat_u16_offset,
617 next_rep: 0,
618 current_offset: 0,
619 result,
620 }
621 }
622 fn add(&mut self, col: u16, colspan: u16, row: u16, rowspan: u16) {
623 let res = self.result.make_mut_slice();
624 loop {
625 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
626 let nr = *nr as usize;
627 let step = self.repeater_steps.get(self.next_rep).copied().unwrap_or(1) as usize;
628 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
629
630 if nr == self.counter {
631 let data_u16_start = self.repeat_u16_offset;
633 let stride = step * 4;
634
635 res[self.current_offset * 4] = data_u16_start as _;
637 res[self.current_offset * 4 + 1] = stride as _;
638 self.current_offset += 1;
639 }
640 if self.counter >= nr {
641 let cells_in_repeater = rep_count * step;
642 if self.counter - nr == cells_in_repeater {
643 self.repeat_u16_offset += cells_in_repeater * 4;
645 self.next_rep += 1;
646 continue;
647 }
648 let cell_in_rep = self.counter - nr;
650 let row_in_rep = cell_in_rep / step;
651 let col_in_rep = cell_in_rep % step;
652 let data_u16_start = self.repeat_u16_offset;
653 let u16_pos = data_u16_start + row_in_rep * step * 4 + col_in_rep * 4;
654 res[u16_pos] = col;
655 res[u16_pos + 1] = colspan;
656 res[u16_pos + 2] = row;
657 res[u16_pos + 3] = rowspan;
658 self.counter += 1;
659 return;
660 }
661 }
662 res[self.current_offset * 4] = col;
664 res[self.current_offset * 4 + 1] = colspan;
665 res[self.current_offset * 4 + 2] = row;
666 res[self.current_offset * 4 + 3] = rowspan;
667 self.current_offset += 1;
668 self.counter += 1;
669 return;
670 }
671 }
672}
673
674pub fn organize_dialog_button_layout(
677 input_data: Slice<GridLayoutInputData>,
678 dialog_button_roles: Slice<DialogButtonRole>,
679) -> GridLayoutOrganizedData {
680 let mut organized_data = GridLayoutOrganizedData::default();
681 organized_data.reserve(input_data.len() * 4);
682
683 #[cfg(feature = "std")]
684 fn is_kde() -> bool {
685 std::env::var("XDG_CURRENT_DESKTOP")
687 .ok()
688 .and_then(|v| v.as_bytes().first().copied())
689 .is_some_and(|x| x.eq_ignore_ascii_case(&b'K'))
690 }
691 #[cfg(not(feature = "std"))]
692 let is_kde = || true;
693
694 let expected_order: &[DialogButtonRole] = match crate::detect_operating_system() {
695 crate::items::OperatingSystemType::Windows => {
696 &[
697 DialogButtonRole::Reset,
698 DialogButtonRole::None, DialogButtonRole::Accept,
700 DialogButtonRole::Action,
701 DialogButtonRole::Reject,
702 DialogButtonRole::Apply,
703 DialogButtonRole::Help,
704 ]
705 }
706 crate::items::OperatingSystemType::Macos | crate::items::OperatingSystemType::Ios => {
707 &[
708 DialogButtonRole::Help,
709 DialogButtonRole::Reset,
710 DialogButtonRole::Apply,
711 DialogButtonRole::Action,
712 DialogButtonRole::None, DialogButtonRole::Reject,
714 DialogButtonRole::Accept,
715 ]
716 }
717 _ if is_kde() => {
718 &[
720 DialogButtonRole::Help,
721 DialogButtonRole::Reset,
722 DialogButtonRole::None, DialogButtonRole::Action,
724 DialogButtonRole::Accept,
725 DialogButtonRole::Apply,
726 DialogButtonRole::Reject,
727 ]
728 }
729 _ => {
730 &[
732 DialogButtonRole::Help,
733 DialogButtonRole::Reset,
734 DialogButtonRole::None, DialogButtonRole::Action,
736 DialogButtonRole::Accept,
737 DialogButtonRole::Apply,
738 DialogButtonRole::Reject,
739 ]
740 }
741 };
742
743 let mut column_for_input: Vec<usize> = Vec::with_capacity(dialog_button_roles.len());
745 for role in expected_order.iter() {
746 if role == &DialogButtonRole::None {
747 column_for_input.push(usize::MAX); continue;
749 }
750 for (idx, r) in dialog_button_roles.as_slice().iter().enumerate() {
751 if *r == *role {
752 column_for_input.push(idx);
753 }
754 }
755 }
756
757 for (input_index, cell) in input_data.as_slice().iter().enumerate() {
758 let col = column_for_input.iter().position(|&x| x == input_index);
759 if let Some(col) = col {
760 organized_data.push_cell(col as _, cell.colspan as _, cell.row as _, cell.rowspan as _);
761 } else {
762 organized_data.push_cell(
765 cell.col as _,
766 cell.colspan as _,
767 cell.row as _,
768 cell.rowspan as _,
769 );
770 }
771 }
772 organized_data
773}
774
775fn total_repeated_cells<'a>(repeater_indices: &'a [u32], repeater_steps: &'a [u32]) -> usize {
777 repeater_indices
778 .chunks(2)
779 .enumerate()
780 .map(|(i, chunk)| {
781 let count = chunk.get(1).copied().unwrap_or(0) as usize;
782 let step = repeater_steps.get(i).copied().unwrap_or(1) as usize;
783 count * step
784 })
785 .sum()
786}
787
788type Errors = Vec<String>;
789
790pub fn organize_grid_layout(
791 input_data: Slice<GridLayoutInputData>,
792 repeater_indices: Slice<u32>,
793 repeater_steps: Slice<u32>,
794) -> GridLayoutOrganizedData {
795 let (organized_data, errors) =
796 organize_grid_layout_impl(input_data, repeater_indices, repeater_steps);
797 for error in errors {
798 crate::debug_log!("Slint layout error: {}", error);
799 }
800 organized_data
801}
802
803fn organize_grid_layout_impl(
805 input_data: Slice<GridLayoutInputData>,
806 repeater_indices: Slice<u32>,
807 repeater_steps: Slice<u32>,
808) -> (GridLayoutOrganizedData, Errors) {
809 let mut organized_data = GridLayoutOrganizedData::default();
810 let num_repeaters = repeater_indices.len() / 2;
813 let total_repeated_cells =
814 total_repeated_cells(repeater_indices.as_slice(), repeater_steps.as_slice());
815 let static_cells = input_data.len() - total_repeated_cells;
816 let mut generator = OrganizedDataGenerator::new(
817 repeater_indices.as_slice(),
818 repeater_steps.as_slice(),
819 static_cells,
820 num_repeaters,
821 total_repeated_cells,
822 &mut organized_data,
823 );
824 let mut errors = Vec::new();
825
826 fn clamp_to_u16(value: f32, field_name: &str, errors: &mut Vec<String>) -> u16 {
827 if value < 0.0 {
828 errors.push(format!("cell {field_name} {value} is negative, clamping to 0"));
829 0
830 } else if value > u16::MAX as f32 {
831 errors
832 .push(format!("cell {field_name} {value} is too large, clamping to {}", u16::MAX));
833 u16::MAX
834 } else {
835 value as u16
836 }
837 }
838
839 let mut row = 0;
840 let mut col = 0;
841 let mut first = true;
842 for cell in input_data.as_slice().iter() {
843 if cell.new_row && !first {
844 row += 1;
845 col = 0;
846 }
847 first = false;
848
849 if cell.row != i_slint_common::ROW_COL_AUTO {
850 let cell_row = clamp_to_u16(cell.row, "row", &mut errors);
851 if row != cell_row {
852 row = cell_row;
853 col = 0;
854 }
855 }
856 if cell.col != i_slint_common::ROW_COL_AUTO {
857 col = clamp_to_u16(cell.col, "col", &mut errors);
858 }
859
860 let colspan = clamp_to_u16(cell.colspan, "colspan", &mut errors);
861 let rowspan = clamp_to_u16(cell.rowspan, "rowspan", &mut errors);
862 col = col.min(u16::MAX - colspan); generator.add(col, colspan, row, rowspan);
864 col += colspan;
865 }
866 (organized_data, errors)
867}
868
869struct LayoutCacheGenerator<'a> {
877 repeater_indices: &'a [u32],
879 counter: usize,
881 repeat_offset: usize,
883 next_rep: usize,
885 current_offset: usize,
887 result: &'a mut SharedVector<Coord>,
889}
890
891impl<'a> LayoutCacheGenerator<'a> {
892 fn new(repeater_indices: &'a [u32], result: &'a mut SharedVector<Coord>) -> Self {
893 let total_repeated_cells: usize = repeater_indices
894 .chunks(2)
895 .map(|chunk| chunk.get(1).copied().unwrap_or(0) as usize)
896 .sum();
897 assert!(result.len() >= total_repeated_cells * 2);
898 let repeat_offset = result.len() / 2 - total_repeated_cells;
899 Self { repeater_indices, counter: 0, repeat_offset, next_rep: 0, current_offset: 0, result }
900 }
901 fn add(&mut self, pos: Coord, size: Coord) {
902 let res = self.result.make_mut_slice();
903 let o = loop {
904 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
905 let nr = *nr as usize;
906 if nr == self.counter {
907 for o in 0..2 {
909 res[self.current_offset * 2 + o] = (self.repeat_offset * 2 + o) as _;
910 }
911 self.current_offset += 1;
912 }
913 if self.counter >= nr {
914 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
915 if self.counter - nr == rep_count {
916 self.repeat_offset += rep_count;
917 self.next_rep += 1;
918 continue;
919 }
920 let offset = self.repeat_offset + (self.counter - nr);
921 break offset;
922 }
923 }
924 self.current_offset += 1;
925 break self.current_offset - 1;
926 };
927 res[o * 2] = pos;
928 res[o * 2 + 1] = size;
929 self.counter += 1;
930 }
931}
932
933struct GridLayoutCacheGenerator<'a> {
937 repeater_indices: &'a [u32],
939 repeater_steps: &'a [u32],
940 counter: usize,
942 repeat_f32_offset: usize,
944 next_rep: usize,
946 current_offset: usize,
948 result: &'a mut SharedVector<Coord>,
950}
951
952impl<'a> GridLayoutCacheGenerator<'a> {
953 fn new(
954 repeater_indices: &'a [u32],
955 repeater_steps: &'a [u32],
956 static_cells: usize,
957 num_repeaters: usize,
958 total_repeated_cells_count: usize,
959 result: &'a mut SharedVector<Coord>,
960 ) -> Self {
961 result.resize((static_cells + num_repeaters + total_repeated_cells_count) * 2, 0 as _);
962 let repeat_f32_offset = (static_cells + num_repeaters) * 2;
963 Self {
964 repeater_indices,
965 repeater_steps,
966 counter: 0,
967 repeat_f32_offset,
968 next_rep: 0,
969 current_offset: 0,
970 result,
971 }
972 }
973 fn add(&mut self, pos: Coord, size: Coord) {
974 let res = self.result.make_mut_slice();
975 loop {
976 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
977 let nr = *nr as usize;
978 let step = self.repeater_steps.get(self.next_rep).copied().unwrap_or(1) as usize;
979 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
980
981 if nr == self.counter {
982 let data_f32_start = self.repeat_f32_offset;
984 let stride = step * 2;
985
986 res[self.current_offset * 2] = data_f32_start as _;
988 res[self.current_offset * 2 + 1] = stride as _;
989 self.current_offset += 1;
990 }
991 if self.counter >= nr {
992 let cells_in_repeater = rep_count * step;
993 if self.counter - nr == cells_in_repeater {
994 self.repeat_f32_offset += cells_in_repeater * 2;
996 self.next_rep += 1;
997 continue;
998 }
999 let cell_in_rep = self.counter - nr;
1001 let row_in_rep = cell_in_rep / step;
1002 let col_in_rep = cell_in_rep % step;
1003 let data_f32_start = self.repeat_f32_offset;
1004 let f32_pos = data_f32_start + row_in_rep * step * 2 + col_in_rep * 2;
1005 res[f32_pos] = pos;
1006 res[f32_pos + 1] = size;
1007 self.counter += 1;
1008 return;
1009 }
1010 }
1011 res[self.current_offset * 2] = pos;
1013 res[self.current_offset * 2 + 1] = size;
1014 self.current_offset += 1;
1015 self.counter += 1;
1016 return;
1017 }
1018 }
1019}
1020
1021pub fn solve_grid_layout(
1024 data: &GridLayoutData,
1025 constraints: Slice<LayoutItemInfo>,
1026 orientation: Orientation,
1027 repeater_indices: Slice<u32>,
1028 repeater_steps: Slice<u32>,
1029) -> SharedVector<Coord> {
1030 let mut layout_data = grid_internal::to_layout_data(
1031 &data.organized_data,
1032 constraints,
1033 orientation,
1034 repeater_indices,
1035 repeater_steps,
1036 data.spacing,
1037 Some(data.size),
1038 );
1039
1040 if layout_data.is_empty() {
1041 return Default::default();
1042 }
1043
1044 grid_internal::layout_items(
1045 &mut layout_data,
1046 data.padding.begin,
1047 data.size - (data.padding.begin + data.padding.end),
1048 data.spacing,
1049 );
1050
1051 let mut result = SharedVector::<Coord>::default();
1052 let num_repeaters = repeater_indices.len() / 2;
1053 let total_repeated_cells =
1054 total_repeated_cells(repeater_indices.as_slice(), repeater_steps.as_slice());
1055 let static_cells = constraints.len() - total_repeated_cells;
1056 let mut generator = GridLayoutCacheGenerator::new(
1057 repeater_indices.as_slice(),
1058 repeater_steps.as_slice(),
1059 static_cells,
1060 num_repeaters,
1061 total_repeated_cells,
1062 &mut result,
1063 );
1064
1065 for idx in 0..constraints.len() {
1066 let (col_or_row, span) = data.organized_data.col_or_row_and_span(
1067 idx,
1068 orientation,
1069 &repeater_indices,
1070 &repeater_steps,
1071 );
1072 let cdata = &layout_data[col_or_row as usize];
1073 let size = if span > 0 {
1074 let last_cell = &layout_data[col_or_row as usize + span as usize - 1];
1075 last_cell.pos + last_cell.size - cdata.pos
1076 } else {
1077 0 as Coord
1078 };
1079 generator.add(cdata.pos, size);
1080 }
1081 result
1082}
1083
1084pub fn grid_layout_info(
1085 organized_data: GridLayoutOrganizedData, constraints: Slice<LayoutItemInfo>,
1087 repeater_indices: Slice<u32>,
1088 repeater_steps: Slice<u32>,
1089 spacing: Coord,
1090 padding: &Padding,
1091 orientation: Orientation,
1092) -> LayoutInfo {
1093 let layout_data = grid_internal::to_layout_data(
1094 &organized_data,
1095 constraints,
1096 orientation,
1097 repeater_indices,
1098 repeater_steps,
1099 spacing,
1100 None,
1101 );
1102 if layout_data.is_empty() {
1103 let mut info = LayoutInfo::default();
1104 info.min = padding.begin + padding.end;
1105 info.preferred = info.min;
1106 info.max = info.min;
1107 return info;
1108 }
1109 let spacing_w = spacing * (layout_data.len() - 1) as Coord + padding.begin + padding.end;
1110 let min = layout_data.iter().map(|data| data.min).sum::<Coord>() + spacing_w;
1111 let max = layout_data.iter().map(|data| data.max).fold(spacing_w, Saturating::add);
1112 let preferred = layout_data.iter().map(|data| data.pref).sum::<Coord>() + spacing_w;
1113 let stretch = layout_data.iter().map(|data| data.stretch).sum::<f32>();
1114 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
1115}
1116
1117#[repr(C)]
1118#[derive(Debug)]
1119pub struct BoxLayoutData<'a> {
1123 pub size: Coord,
1124 pub spacing: Coord,
1125 pub padding: Padding,
1126 pub alignment: LayoutAlignment,
1127 pub cells: Slice<'a, LayoutItemInfo>,
1128}
1129
1130#[repr(C)]
1132#[derive(Debug)]
1133pub struct BoxLayoutOrthoData<'a> {
1134 pub size: Coord,
1135 pub padding: Padding,
1136 pub cross_axis_alignment: CrossAxisAlignment,
1137 pub cells: Slice<'a, LayoutItemInfo>,
1138}
1139
1140#[repr(C)]
1141#[derive(Debug)]
1142pub struct FlexboxLayoutData<'a> {
1144 pub width: Coord,
1145 pub height: Coord,
1146 pub spacing_h: Coord,
1147 pub spacing_v: Coord,
1148 pub padding_h: Padding,
1149 pub padding_v: Padding,
1150 pub alignment: LayoutAlignment,
1151 pub direction: FlexboxLayoutDirection,
1152 pub align_content: FlexboxLayoutAlignContent,
1153 pub cross_axis_alignment: CrossAxisAlignment,
1154 pub flex_wrap: FlexboxLayoutWrap,
1155 pub cells_h: Slice<'a, FlexboxLayoutItemInfo>,
1157 pub cells_v: Slice<'a, FlexboxLayoutItemInfo>,
1159}
1160
1161#[repr(C)]
1162#[derive(Debug, Clone, Default)]
1163pub struct LayoutItemInfo {
1165 pub constraint: LayoutInfo,
1166}
1167
1168#[repr(C)]
1169#[derive(Debug, Clone)]
1170pub struct FlexboxLayoutItemInfo {
1172 pub constraint: LayoutInfo,
1173 pub flex_grow: f32,
1175 pub flex_shrink: f32,
1177 pub flex_basis: Coord,
1179 pub flex_align_self: FlexboxLayoutAlignSelf,
1181 pub flex_order: i32,
1183}
1184
1185impl Default for FlexboxLayoutItemInfo {
1186 fn default() -> Self {
1187 Self {
1188 constraint: LayoutInfo::default(),
1189 flex_grow: 0.0,
1190 flex_shrink: 1.0,
1191 flex_basis: -1 as _,
1192 flex_align_self: FlexboxLayoutAlignSelf::Auto,
1193 flex_order: 0,
1194 }
1195 }
1196}
1197
1198impl From<LayoutItemInfo> for FlexboxLayoutItemInfo {
1199 fn from(info: LayoutItemInfo) -> Self {
1200 Self { constraint: info.constraint, ..Default::default() }
1201 }
1202}
1203
1204pub fn solve_box_layout(data: &BoxLayoutData, repeater_indices: Slice<u32>) -> SharedVector<Coord> {
1206 let mut result = SharedVector::<Coord>::default();
1207 result.resize(data.cells.len() * 2 + repeater_indices.len(), 0 as _);
1209
1210 if data.cells.is_empty() {
1211 return result;
1212 }
1213
1214 let size_without_padding = data.size - data.padding.begin - data.padding.end;
1215 let num_spacings = (data.cells.len() - 1) as Coord;
1216 let spacings = data.spacing * num_spacings;
1217 let content_size = size_without_padding - spacings; let mut layout_data: Vec<_> = data
1219 .cells
1220 .iter()
1221 .map(|c| {
1222 let min = c.constraint.min.max(c.constraint.min_percent * content_size / 100 as Coord);
1223 let max = c.constraint.max.min(c.constraint.max_percent * content_size / 100 as Coord);
1224 grid_internal::LayoutData {
1225 min,
1226 max,
1227 pref: c.constraint.preferred.min(max).max(min),
1228 stretch: c.constraint.stretch,
1229 ..Default::default()
1230 }
1231 })
1232 .collect();
1233
1234 let pref_size: Coord = layout_data.iter().map(|it| it.pref).sum();
1235
1236 let align = match data.alignment {
1237 LayoutAlignment::Stretch => {
1238 grid_internal::layout_items(
1239 &mut layout_data,
1240 data.padding.begin,
1241 size_without_padding,
1242 data.spacing,
1243 );
1244 None
1245 }
1246 _ if size_without_padding <= pref_size + spacings => {
1247 grid_internal::layout_items(
1248 &mut layout_data,
1249 data.padding.begin,
1250 size_without_padding,
1251 data.spacing,
1252 );
1253 None
1254 }
1255 LayoutAlignment::Center => Some((
1256 data.padding.begin + (size_without_padding - pref_size - spacings) / 2 as Coord,
1257 data.spacing,
1258 )),
1259 LayoutAlignment::Start => Some((data.padding.begin, data.spacing)),
1260 LayoutAlignment::End => {
1261 Some((data.padding.begin + (size_without_padding - pref_size - spacings), data.spacing))
1262 }
1263 LayoutAlignment::SpaceBetween => {
1264 Some((data.padding.begin, (size_without_padding - pref_size) / num_spacings))
1265 }
1266 LayoutAlignment::SpaceAround => {
1267 let spacing = (size_without_padding - pref_size) / (num_spacings + 1 as Coord);
1268 Some((data.padding.begin + spacing / 2 as Coord, spacing))
1269 }
1270 LayoutAlignment::SpaceEvenly => {
1271 let spacing = (size_without_padding - pref_size) / (num_spacings + 2 as Coord);
1272 Some((data.padding.begin + spacing, spacing))
1273 }
1274 };
1275 if let Some((mut pos, spacing)) = align {
1276 for it in &mut layout_data {
1277 it.pos = pos;
1278 it.size = it.pref;
1279 pos += spacing + it.size;
1280 }
1281 }
1282
1283 let mut generator = LayoutCacheGenerator::new(&repeater_indices, &mut result);
1284 for layout in layout_data.iter() {
1285 generator.add(layout.pos, layout.size);
1286 }
1287 result
1288}
1289
1290pub fn solve_box_layout_ortho(
1292 data: &BoxLayoutOrthoData,
1293 repeater_indices: Slice<u32>,
1294) -> SharedVector<Coord> {
1295 let mut result = SharedVector::<Coord>::default();
1296 result.resize(data.cells.len() * 2 + repeater_indices.len(), 0 as _);
1297 if data.cells.is_empty() {
1298 return result;
1299 }
1300 let size_without_padding = data.size - data.padding.begin - data.padding.end;
1301 let mut generator = LayoutCacheGenerator::new(&repeater_indices, &mut result);
1302 for c in data.cells.iter() {
1303 let min =
1304 c.constraint.min.max(c.constraint.min_percent * size_without_padding / 100 as Coord);
1305 let max =
1306 c.constraint.max.min(c.constraint.max_percent * size_without_padding / 100 as Coord);
1307 let size = match data.cross_axis_alignment {
1308 CrossAxisAlignment::Stretch => size_without_padding,
1309 _ => c.constraint.preferred,
1310 }
1311 .min(max)
1312 .max(min);
1313 let pos = match data.cross_axis_alignment {
1314 CrossAxisAlignment::Stretch | CrossAxisAlignment::Start => data.padding.begin,
1315 CrossAxisAlignment::End => data.padding.begin + size_without_padding - size,
1316 CrossAxisAlignment::Center => {
1317 data.padding.begin + (size_without_padding - size) / 2 as Coord
1318 }
1319 };
1320 generator.add(pos, size);
1321 }
1322 result
1323}
1324
1325pub fn box_layout_info(
1327 cells: Slice<LayoutItemInfo>,
1328 spacing: Coord,
1329 padding: &Padding,
1330 alignment: LayoutAlignment,
1331) -> LayoutInfo {
1332 let count = cells.len();
1333 let is_stretch = alignment == LayoutAlignment::Stretch;
1334 if count < 1 {
1335 let mut info = LayoutInfo::default();
1336 info.min = padding.begin + padding.end;
1337 info.preferred = info.min;
1338 if is_stretch {
1339 info.max = info.min;
1340 }
1341 return info;
1342 };
1343 let extra_w = padding.begin + padding.end + spacing * (count - 1) as Coord;
1344 let min = cells.iter().map(|c| c.constraint.min).sum::<Coord>() + extra_w; let max = if is_stretch {
1346 (cells.iter().map(|c| c.constraint.max).fold(extra_w, Saturating::add)).max(min)
1347 } else {
1348 Coord::MAX
1349 }; let preferred = cells.iter().map(|c| c.constraint.preferred_bounded()).sum::<Coord>() + extra_w;
1351 let stretch = cells.iter().map(|c| c.constraint.stretch).sum::<f32>();
1352 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
1353}
1354
1355pub fn box_layout_info_ortho(cells: Slice<LayoutItemInfo>, padding: &Padding) -> LayoutInfo {
1356 let extra_w = padding.begin + padding.end;
1357 let mut fold =
1358 cells.iter().fold(LayoutInfo { stretch: f32::MAX, ..Default::default() }, |a, b| {
1359 a.merge(&b.constraint)
1360 });
1361 fold.max = fold.max.max(fold.min);
1362 fold.preferred = fold.preferred.clamp(fold.min, fold.max);
1363 fold.min += extra_w;
1364 fold.max = Saturating::add(fold.max, extra_w);
1365 fold.preferred += extra_w;
1366 fold.min_percent = 0 as _;
1369 fold.max_percent = 100 as _;
1370 fold
1371}
1372
1373mod flexbox_taffy {
1375 use super::{
1376 Coord, CrossAxisAlignment, FlexboxLayoutAlignContent, FlexboxLayoutAlignSelf,
1377 FlexboxLayoutItemInfo, FlexboxLayoutWrap as SlintFlexboxLayoutWrap, LayoutAlignment,
1378 Padding, Slice,
1379 };
1380 use alloc::vec::Vec;
1381 pub use taffy::prelude::FlexDirection as TaffyFlexDirection;
1382 use taffy::prelude::*;
1383
1384 pub struct FlexboxLayoutParams<'a> {
1386 pub cells_h: &'a Slice<'a, FlexboxLayoutItemInfo>,
1387 pub cells_v: &'a Slice<'a, FlexboxLayoutItemInfo>,
1388 pub spacing_h: Coord,
1389 pub spacing_v: Coord,
1390 pub padding_h: &'a Padding,
1391 pub padding_v: &'a Padding,
1392 pub alignment: LayoutAlignment,
1393 pub align_content: FlexboxLayoutAlignContent,
1394 pub cross_axis_alignment: CrossAxisAlignment,
1395 pub flex_wrap: SlintFlexboxLayoutWrap,
1396 pub flex_direction: TaffyFlexDirection,
1397 pub container_width: Option<Coord>,
1398 pub container_height: Option<Coord>,
1399 pub use_measure_for_cross_axis: bool,
1402 }
1403
1404 pub struct FlexboxTaffyBuilder {
1407 pub taffy: TaffyTree<usize>,
1408 pub children: Vec<NodeId>,
1409 pub container: NodeId,
1410 pub order_map: Vec<usize>,
1412 }
1413
1414 impl FlexboxTaffyBuilder {
1415 pub fn new(params: FlexboxLayoutParams) -> Self {
1417 let mut taffy = TaffyTree::<usize>::new();
1418
1419 let column_cross_cap = match params.flex_direction {
1423 TaffyFlexDirection::Column | TaffyFlexDirection::ColumnReverse => params
1424 .container_width
1425 .map(|cw| (cw - params.padding_h.begin - params.padding_h.end).max(0 as Coord)),
1426 _ => None,
1427 };
1428
1429 let mut children: Vec<NodeId> = params
1431 .cells_h
1432 .iter()
1433 .enumerate()
1434 .map(|(idx, cell_h)| {
1435 let cell_v = params.cells_v.get(idx);
1436 let h_constraint = &cell_h.constraint;
1437 let v_constraint = cell_v.map(|c| &c.constraint);
1438
1439 let preferred_width = h_constraint.preferred_bounded();
1441 let preferred_height =
1442 v_constraint.map(|vc| vc.preferred_bounded()).unwrap_or(0 as Coord);
1443
1444 let flex_basis = if cell_h.flex_basis >= 0 as Coord {
1446 Dimension::length(cell_h.flex_basis as _)
1447 } else {
1448 match params.flex_direction {
1449 TaffyFlexDirection::Row | TaffyFlexDirection::RowReverse => {
1450 Dimension::length(preferred_width as _)
1451 }
1452 TaffyFlexDirection::Column | TaffyFlexDirection::ColumnReverse => {
1453 Dimension::length(preferred_height as _)
1454 }
1455 }
1456 };
1457
1458 let max_width = h_constraint.max.min(column_cross_cap.unwrap_or(Coord::MAX));
1459 let max_width_dim = if max_width < Coord::MAX {
1460 Dimension::length(max_width as _)
1461 } else {
1462 Dimension::auto()
1463 };
1464
1465 taffy
1466 .new_leaf_with_context(
1467 Style {
1468 flex_basis,
1469 size: Size {
1470 width: match params.flex_direction {
1471 TaffyFlexDirection::Column
1472 | TaffyFlexDirection::ColumnReverse => {
1473 let stretches = match cell_h.flex_align_self {
1479 FlexboxLayoutAlignSelf::Auto => {
1480 params.cross_axis_alignment
1481 == CrossAxisAlignment::Stretch
1482 }
1483 FlexboxLayoutAlignSelf::Stretch => true,
1484 _ => false,
1485 };
1486 if stretches {
1487 Dimension::auto()
1488 } else if preferred_width > 0 as Coord {
1489 Dimension::length(preferred_width as _)
1490 } else {
1491 Dimension::auto()
1492 }
1493 }
1494 _ => Dimension::auto(),
1495 },
1496 height: match params.flex_direction {
1497 TaffyFlexDirection::Row
1498 | TaffyFlexDirection::RowReverse => {
1499 if params.use_measure_for_cross_axis {
1502 Dimension::auto()
1503 } else if preferred_height > 0 as Coord {
1504 Dimension::length(preferred_height as _)
1505 } else {
1506 Dimension::auto()
1507 }
1508 }
1509 _ => Dimension::auto(),
1510 },
1511 },
1512 min_size: Size {
1513 width: Dimension::length(h_constraint.min as _),
1514 height: Dimension::length(
1515 v_constraint.map(|vc| vc.min as f32).unwrap_or(0.0),
1516 ),
1517 },
1518 max_size: Size {
1519 width: max_width_dim,
1520 height: if let Some(vc) = v_constraint {
1521 if vc.max < Coord::MAX {
1522 Dimension::length(vc.max as _)
1523 } else {
1524 Dimension::auto()
1525 }
1526 } else {
1527 Dimension::auto()
1528 },
1529 },
1530 flex_grow: cell_h.flex_grow,
1531 flex_shrink: cell_h.flex_shrink,
1532 align_self: match cell_h.flex_align_self {
1533 FlexboxLayoutAlignSelf::Auto => None,
1534 FlexboxLayoutAlignSelf::Stretch => Some(AlignSelf::Stretch),
1535 FlexboxLayoutAlignSelf::Start => Some(AlignSelf::FlexStart),
1536 FlexboxLayoutAlignSelf::End => Some(AlignSelf::FlexEnd),
1537 FlexboxLayoutAlignSelf::Center => Some(AlignSelf::Center),
1538 },
1539 ..Default::default()
1540 },
1541 idx,
1542 )
1543 .unwrap() })
1545 .collect();
1546
1547 let has_order = params.cells_h.iter().any(|c| c.flex_order != 0);
1550 let order_map: Vec<usize> = if has_order {
1551 let mut indices: Vec<usize> = (0..children.len()).collect();
1552 indices.sort_by_key(|&i| params.cells_h.get(i).map_or(0, |c| c.flex_order));
1554 let sorted_children: Vec<NodeId> = indices.iter().map(|&i| children[i]).collect();
1555 children = sorted_children;
1556 indices
1557 } else {
1558 Vec::new()
1559 };
1560
1561 let container = taffy
1563 .new_with_children(
1564 Style {
1565 display: Display::Flex,
1566 flex_direction: params.flex_direction,
1567 flex_wrap: match params.flex_wrap {
1568 SlintFlexboxLayoutWrap::Wrap => FlexWrap::Wrap,
1569 SlintFlexboxLayoutWrap::NoWrap => FlexWrap::NoWrap,
1570 SlintFlexboxLayoutWrap::WrapReverse => FlexWrap::WrapReverse,
1571 },
1572 justify_content: Some(match params.alignment {
1573 LayoutAlignment::Start => AlignContent::FlexStart,
1576 LayoutAlignment::End => AlignContent::FlexEnd,
1577 LayoutAlignment::Center => AlignContent::Center,
1578 LayoutAlignment::Stretch => AlignContent::Stretch,
1579 LayoutAlignment::SpaceBetween => AlignContent::SpaceBetween,
1580 LayoutAlignment::SpaceAround => AlignContent::SpaceAround,
1581 LayoutAlignment::SpaceEvenly => AlignContent::SpaceEvenly,
1582 }),
1583 align_items: Some(match params.cross_axis_alignment {
1584 CrossAxisAlignment::Stretch => AlignItems::Stretch,
1585 CrossAxisAlignment::Start => AlignItems::FlexStart,
1586 CrossAxisAlignment::End => AlignItems::FlexEnd,
1587 CrossAxisAlignment::Center => AlignItems::Center,
1588 }),
1589 align_content: Some(match params.align_content {
1590 FlexboxLayoutAlignContent::Stretch => AlignContent::Stretch,
1591 FlexboxLayoutAlignContent::Start => AlignContent::FlexStart,
1592 FlexboxLayoutAlignContent::End => AlignContent::FlexEnd,
1593 FlexboxLayoutAlignContent::Center => AlignContent::Center,
1594 FlexboxLayoutAlignContent::SpaceBetween => AlignContent::SpaceBetween,
1595 FlexboxLayoutAlignContent::SpaceAround => AlignContent::SpaceAround,
1596 FlexboxLayoutAlignContent::SpaceEvenly => AlignContent::SpaceEvenly,
1597 }),
1598 gap: Size {
1599 width: LengthPercentage::length(params.spacing_h as _),
1600 height: LengthPercentage::length(params.spacing_v as _),
1601 },
1602 padding: Rect {
1603 left: LengthPercentage::length(params.padding_h.begin as _),
1604 right: LengthPercentage::length(params.padding_h.end as _),
1605 top: LengthPercentage::length(params.padding_v.begin as _),
1606 bottom: LengthPercentage::length(params.padding_v.end as _),
1607 },
1608 size: Size {
1609 width: params
1610 .container_width
1611 .map(|w| Dimension::length(w as _))
1612 .unwrap_or(Dimension::auto()),
1613 height: params
1614 .container_height
1615 .map(|h| Dimension::length(h as _))
1616 .unwrap_or(Dimension::auto()),
1617 },
1618 ..Default::default()
1619 },
1620 &children,
1621 )
1622 .unwrap(); Self { taffy, children, container, order_map }
1625 }
1626
1627 pub fn compute_layout(
1635 &mut self,
1636 available_width: Coord,
1637 available_height: Coord,
1638 mut measure: Option<
1639 &mut dyn FnMut(usize, Option<Coord>, Option<Coord>) -> (Coord, Coord),
1640 >,
1641 ) {
1642 let available_space = taffy::prelude::Size {
1643 width: if available_width < Coord::MAX {
1644 AvailableSpace::Definite(available_width as _)
1645 } else {
1646 AvailableSpace::MaxContent
1647 },
1648 height: if available_height < Coord::MAX {
1649 AvailableSpace::Definite(available_height as _)
1650 } else {
1651 AvailableSpace::MaxContent
1652 },
1653 };
1654 self.taffy
1655 .compute_layout_with_measure(
1656 self.container,
1657 available_space,
1658 |known_dimensions, _available_space, _node_id, node_context, _style| {
1659 if let (Some(measure), Some(&mut child_index)) =
1660 (measure.as_deref_mut(), node_context)
1661 {
1662 let known_w = known_dimensions.width.map(|w| w as Coord);
1663 let known_h = known_dimensions.height.map(|h| h as Coord);
1664 let (w, h) = measure(child_index, known_w, known_h);
1665 taffy::prelude::Size { width: w as f32, height: h as f32 }
1666 } else {
1667 taffy::prelude::Size::ZERO
1668 }
1669 },
1670 )
1671 .unwrap_or_else(|e| {
1672 crate::debug_log!("FlexboxLayout computation error: {}", e);
1673 });
1674 }
1675
1676 pub fn container_size(&self) -> (Coord, Coord) {
1678 let layout = self.taffy.layout(self.container).unwrap();
1679 (layout.size.width as Coord, layout.size.height as Coord)
1680 }
1681
1682 pub fn child_geometry(&self, idx: usize) -> (Coord, Coord, Coord, Coord) {
1684 let layout = self.taffy.layout(self.children[idx]).unwrap();
1685 (
1686 layout.location.x as Coord,
1687 layout.location.y as Coord,
1688 layout.size.width as Coord,
1689 layout.size.height as Coord,
1690 )
1691 }
1692
1693 pub fn original_index(&self, taffy_idx: usize) -> usize {
1695 if self.order_map.is_empty() { taffy_idx } else { self.order_map[taffy_idx] }
1696 }
1697 }
1698}
1699
1700struct FlexboxLayoutCacheGenerator<'a> {
1702 repeater_indices: &'a [u32],
1704 counter: usize,
1706 repeat_offset: usize,
1708 next_rep: usize,
1710 current_offset: usize,
1712 result: &'a mut SharedVector<Coord>,
1714}
1715
1716impl<'a> FlexboxLayoutCacheGenerator<'a> {
1717 fn new(repeater_indices: &'a [u32], result: &'a mut SharedVector<Coord>) -> Self {
1718 let total_repeated_cells: usize = repeater_indices
1720 .chunks(2)
1721 .map(|chunk| chunk.get(1).copied().unwrap_or(0) as usize)
1722 .sum();
1723 assert!(result.len() >= total_repeated_cells * 4);
1724 let repeat_offset = result.len() / 4 - total_repeated_cells;
1725 Self { repeater_indices, counter: 0, repeat_offset, next_rep: 0, current_offset: 0, result }
1726 }
1727
1728 fn add(&mut self, x: Coord, y: Coord, w: Coord, h: Coord) {
1729 let res = self.result.make_mut_slice();
1730 let o = loop {
1731 if let Some(nr) = self.repeater_indices.get(self.next_rep * 2) {
1732 let nr = *nr as usize;
1733 if nr == self.counter {
1734 res[self.current_offset * 4] = (self.repeat_offset * 4) as Coord;
1737 res[self.current_offset * 4 + 1] = (self.repeat_offset * 4 + 1) as Coord;
1738 res[self.current_offset * 4 + 2] = (self.repeat_offset * 4 + 2) as Coord;
1739 res[self.current_offset * 4 + 3] = (self.repeat_offset * 4 + 3) as Coord;
1740 self.current_offset += 1;
1741 }
1742 if self.counter >= nr {
1743 let rep_count = self.repeater_indices[self.next_rep * 2 + 1] as usize;
1744 if self.counter - nr == rep_count {
1745 self.repeat_offset += rep_count;
1747 self.next_rep += 1;
1748 continue;
1749 }
1750 let cell_in_rep = self.counter - nr;
1752 let offset = self.repeat_offset + cell_in_rep;
1753 break offset;
1754 }
1755 }
1756 self.current_offset += 1;
1757 break self.current_offset - 1;
1758 };
1759 res[o * 4] = x;
1760 res[o * 4 + 1] = y;
1761 res[o * 4 + 2] = w;
1762 res[o * 4 + 3] = h;
1763 self.counter += 1;
1764 }
1765}
1766
1767pub type FlexboxMeasureFn<'a> =
1774 Option<&'a mut dyn FnMut(usize, Option<Coord>, Option<Coord>) -> (Coord, Coord)>;
1775
1776pub fn solve_flexbox_layout(
1777 data: &FlexboxLayoutData,
1778 repeater_indices: Slice<u32>,
1779) -> SharedVector<Coord> {
1780 let mut measure = |child_index: usize,
1786 known_w: Option<Coord>,
1787 known_h: Option<Coord>|
1788 -> (Coord, Coord) {
1789 let w = known_w.unwrap_or_else(|| {
1790 data.cells_h.get(child_index).map_or(0 as Coord, |c| c.constraint.preferred_bounded())
1791 });
1792 let h = known_h.unwrap_or_else(|| {
1793 data.cells_v.get(child_index).map_or(0 as Coord, |c| c.constraint.preferred_bounded())
1794 });
1795 (w, h)
1796 };
1797 solve_flexbox_layout_with_measure(data, repeater_indices, Some(&mut measure))
1798}
1799
1800pub fn solve_flexbox_layout_with_measure(
1803 data: &FlexboxLayoutData,
1804 repeater_indices: Slice<u32>,
1805 measure: FlexboxMeasureFn<'_>,
1806) -> SharedVector<Coord> {
1807 let mut result = SharedVector::<Coord>::default();
1809 result.resize(data.cells_h.len() * 4 + repeater_indices.len() * 2, 0 as _);
1810
1811 if data.cells_h.is_empty() {
1812 return result;
1813 }
1814
1815 let taffy_direction = match data.direction {
1816 FlexboxLayoutDirection::Row => flexbox_taffy::TaffyFlexDirection::Row,
1817 FlexboxLayoutDirection::RowReverse => flexbox_taffy::TaffyFlexDirection::RowReverse,
1818 FlexboxLayoutDirection::Column => flexbox_taffy::TaffyFlexDirection::Column,
1819 FlexboxLayoutDirection::ColumnReverse => flexbox_taffy::TaffyFlexDirection::ColumnReverse,
1820 };
1821
1822 let (container_width, container_height) = (
1823 if data.width > 0 as Coord { Some(data.width) } else { None },
1824 if data.height > 0 as Coord { Some(data.height) } else { None },
1825 );
1826
1827 let use_measure = measure.is_some();
1828 let mut builder = flexbox_taffy::FlexboxTaffyBuilder::new(flexbox_taffy::FlexboxLayoutParams {
1829 cells_h: &data.cells_h,
1830 cells_v: &data.cells_v,
1831 spacing_h: data.spacing_h,
1832 spacing_v: data.spacing_v,
1833 padding_h: &data.padding_h,
1834 padding_v: &data.padding_v,
1835 alignment: data.alignment,
1836 align_content: data.align_content,
1837 cross_axis_alignment: data.cross_axis_alignment,
1838 flex_wrap: data.flex_wrap,
1839 flex_direction: taffy_direction,
1840 container_width,
1841 container_height,
1842 use_measure_for_cross_axis: use_measure,
1843 });
1844
1845 let (available_width, available_height) = match data.direction {
1846 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1847 (data.width, Coord::MAX)
1848 }
1849 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1850 (Coord::MAX, data.height)
1851 }
1852 };
1853
1854 builder.compute_layout(available_width, available_height, measure);
1855
1856 if builder.order_map.is_empty() {
1860 let mut generator = FlexboxLayoutCacheGenerator::new(&repeater_indices, &mut result);
1861 for idx in 0..data.cells_h.len() {
1862 let (x, y, w, h) = builder.child_geometry(idx);
1863 generator.add(x, y, w, h);
1864 }
1865 } else {
1866 let count = data.cells_h.len();
1867 let mut geom = alloc::vec![(0 as Coord, 0 as Coord, 0 as Coord, 0 as Coord); count];
1868 for taffy_idx in 0..count {
1869 let orig_idx = builder.original_index(taffy_idx);
1870 geom[orig_idx] = builder.child_geometry(taffy_idx);
1871 }
1872 let mut generator = FlexboxLayoutCacheGenerator::new(&repeater_indices, &mut result);
1873 for (x, y, w, h) in geom {
1874 generator.add(x, y, w, h);
1875 }
1876 }
1877
1878 result
1879}
1880
1881pub fn flexbox_layout_info_main_axis(
1884 cells: Slice<FlexboxLayoutItemInfo>,
1885 spacing: Coord,
1886 padding: &Padding,
1887 flex_wrap: FlexboxLayoutWrap,
1888) -> LayoutInfo {
1889 let extra_pad = padding.begin + padding.end;
1890 if cells.is_empty() {
1891 return LayoutInfo {
1892 min: extra_pad,
1893 preferred: extra_pad,
1894 max: extra_pad,
1895 ..Default::default()
1896 };
1897 }
1898 let num_spacings = cells.len().saturating_sub(1) as Coord;
1899 let min = if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) {
1900 cells.iter().map(|c| c.constraint.min).sum::<Coord>() + spacing * num_spacings + extra_pad
1901 } else {
1902 cells.iter().map(|c| c.constraint.min).fold(0.0 as Coord, |a, b| a.max(b)) + extra_pad
1904 };
1905 let preferred = if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) {
1906 cells.iter().map(|c| c.constraint.preferred_bounded()).sum::<Coord>()
1908 + spacing * num_spacings
1909 + extra_pad
1910 } else {
1911 let total_area: Coord = cells
1915 .iter()
1916 .map(|c| {
1917 let w = c.constraint.preferred_bounded();
1918 w * w
1919 })
1920 .sum();
1921 let count = cells.len();
1922 Float::sqrt(total_area as f32) as Coord + spacing * (count - 1) as Coord + extra_pad
1923 };
1924 let stretch = cells.iter().map(|c| c.constraint.stretch).sum::<f32>();
1925 LayoutInfo {
1926 min,
1927 max: Coord::MAX,
1928 min_percent: 0 as _,
1929 max_percent: 100 as _,
1930 preferred,
1931 stretch,
1932 }
1933}
1934
1935pub fn flexbox_layout_info_cross_axis(
1944 cells_h: Slice<FlexboxLayoutItemInfo>,
1945 cells_v: Slice<FlexboxLayoutItemInfo>,
1946 spacing_h: Coord,
1947 spacing_v: Coord,
1948 padding_h: &Padding,
1949 padding_v: &Padding,
1950 direction: FlexboxLayoutDirection,
1951 flex_wrap: FlexboxLayoutWrap,
1952 constraint_size: Coord,
1953) -> LayoutInfo {
1954 if cells_h.is_empty() {
1955 assert!(cells_v.is_empty());
1956 let orientation = match direction {
1957 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1958 Orientation::Vertical
1959 }
1960 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1961 Orientation::Horizontal
1962 }
1963 };
1964 let padding = match orientation {
1965 Orientation::Horizontal => padding_h,
1966 Orientation::Vertical => padding_v,
1967 };
1968 let pad = padding.begin + padding.end;
1969 return LayoutInfo { min: pad, preferred: pad, max: pad, ..Default::default() };
1970 }
1971
1972 let cross_cells = match direction {
1974 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => &cells_v,
1975 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => &cells_h,
1976 };
1977
1978 let (main_cells, main_spacing, main_padding) = match direction {
1981 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
1982 (&cells_h, spacing_h, padding_h)
1983 }
1984 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
1985 (&cells_v, spacing_v, padding_v)
1986 }
1987 };
1988 let main_extra_pad = main_padding.begin + main_padding.end;
1989 let main_axis_constraint = if constraint_size > 0 as Coord && constraint_size < Coord::MAX {
1990 constraint_size
1992 } else if matches!(flex_wrap, FlexboxLayoutWrap::NoWrap) || constraint_size >= Coord::MAX {
1993 Coord::MAX
2000 } else {
2001 let total_area: Coord = main_cells
2004 .iter()
2005 .zip(cross_cells.iter())
2006 .map(|(m, c)| m.constraint.preferred_bounded() * c.constraint.preferred_bounded())
2007 .sum();
2008 let count = main_cells.len();
2009 Float::sqrt(total_area as f32) as Coord
2010 + main_spacing * (count - 1) as Coord
2011 + main_extra_pad
2012 };
2013
2014 let taffy_direction = match direction {
2015 FlexboxLayoutDirection::Row => flexbox_taffy::TaffyFlexDirection::Row,
2016 FlexboxLayoutDirection::RowReverse => flexbox_taffy::TaffyFlexDirection::RowReverse,
2017 FlexboxLayoutDirection::Column => flexbox_taffy::TaffyFlexDirection::Column,
2018 FlexboxLayoutDirection::ColumnReverse => flexbox_taffy::TaffyFlexDirection::ColumnReverse,
2019 };
2020
2021 let (container_width, container_height) = match direction {
2022 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
2023 (Some(main_axis_constraint), None)
2024 }
2025 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
2026 (None, Some(main_axis_constraint))
2027 }
2028 };
2029
2030 let mut builder = flexbox_taffy::FlexboxTaffyBuilder::new(flexbox_taffy::FlexboxLayoutParams {
2031 cells_h: &cells_h,
2032 cells_v: &cells_v,
2033 spacing_h,
2034 spacing_v,
2035 padding_h,
2036 padding_v,
2037 alignment: LayoutAlignment::Start,
2038 align_content: FlexboxLayoutAlignContent::Stretch,
2039 cross_axis_alignment: CrossAxisAlignment::Stretch,
2040 flex_wrap,
2041 flex_direction: taffy_direction,
2042 container_width,
2043 container_height,
2044 use_measure_for_cross_axis: false,
2045 });
2046
2047 let (available_width, available_height) = match direction {
2048 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => {
2049 (main_axis_constraint, Coord::MAX)
2050 }
2051 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => {
2052 (Coord::MAX, main_axis_constraint)
2053 }
2054 };
2055
2056 builder.compute_layout(available_width, available_height, None);
2057
2058 let (total_width, total_height) = builder.container_size();
2059 let cross_size = match direction {
2060 FlexboxLayoutDirection::Row | FlexboxLayoutDirection::RowReverse => total_height,
2061 FlexboxLayoutDirection::Column | FlexboxLayoutDirection::ColumnReverse => total_width,
2062 };
2063
2064 LayoutInfo {
2065 min: cross_size,
2066 max: Coord::MAX,
2067 min_percent: 0 as _,
2068 max_percent: 100 as _,
2069 preferred: cross_size,
2070 stretch: 0.0,
2071 }
2072}
2073
2074#[cfg(feature = "ffi")]
2075pub(crate) mod ffi {
2076 #![allow(unsafe_code)]
2077
2078 use super::*;
2079
2080 #[unsafe(no_mangle)]
2081 pub extern "C" fn slint_organize_grid_layout(
2082 input_data: Slice<GridLayoutInputData>,
2083 repeater_indices: Slice<u32>,
2084 repeater_steps: Slice<u32>,
2085 result: &mut GridLayoutOrganizedData,
2086 ) {
2087 *result = super::organize_grid_layout(input_data, repeater_indices, repeater_steps);
2088 }
2089
2090 #[unsafe(no_mangle)]
2091 pub extern "C" fn slint_organize_dialog_button_layout(
2092 input_data: Slice<GridLayoutInputData>,
2093 dialog_button_roles: Slice<DialogButtonRole>,
2094 result: &mut GridLayoutOrganizedData,
2095 ) {
2096 *result = super::organize_dialog_button_layout(input_data, dialog_button_roles);
2097 }
2098
2099 #[unsafe(no_mangle)]
2100 pub extern "C" fn slint_solve_grid_layout(
2101 data: &GridLayoutData,
2102 constraints: Slice<LayoutItemInfo>,
2103 orientation: Orientation,
2104 repeater_indices: Slice<u32>,
2105 repeater_steps: Slice<u32>,
2106 result: &mut SharedVector<Coord>,
2107 ) {
2108 *result = super::solve_grid_layout(
2109 data,
2110 constraints,
2111 orientation,
2112 repeater_indices,
2113 repeater_steps,
2114 )
2115 }
2116
2117 #[unsafe(no_mangle)]
2118 pub extern "C" fn slint_grid_layout_info(
2119 organized_data: &GridLayoutOrganizedData,
2120 constraints: Slice<LayoutItemInfo>,
2121 repeater_indices: Slice<u32>,
2122 repeater_steps: Slice<u32>,
2123 spacing: Coord,
2124 padding: &Padding,
2125 orientation: Orientation,
2126 ) -> LayoutInfo {
2127 super::grid_layout_info(
2128 organized_data.clone(),
2129 constraints,
2130 repeater_indices,
2131 repeater_steps,
2132 spacing,
2133 padding,
2134 orientation,
2135 )
2136 }
2137
2138 #[unsafe(no_mangle)]
2139 pub extern "C" fn slint_solve_box_layout(
2140 data: &BoxLayoutData,
2141 repeater_indices: Slice<u32>,
2142 result: &mut SharedVector<Coord>,
2143 ) {
2144 *result = super::solve_box_layout(data, repeater_indices)
2145 }
2146
2147 #[unsafe(no_mangle)]
2148 pub extern "C" fn slint_solve_box_layout_ortho(
2149 data: &BoxLayoutOrthoData,
2150 repeater_indices: Slice<u32>,
2151 result: &mut SharedVector<Coord>,
2152 ) {
2153 *result = super::solve_box_layout_ortho(data, repeater_indices)
2154 }
2155
2156 #[unsafe(no_mangle)]
2157 pub extern "C" fn slint_box_layout_info(
2159 cells: Slice<LayoutItemInfo>,
2160 spacing: Coord,
2161 padding: &Padding,
2162 alignment: LayoutAlignment,
2163 ) -> LayoutInfo {
2164 super::box_layout_info(cells, spacing, padding, alignment)
2165 }
2166
2167 #[unsafe(no_mangle)]
2168 pub extern "C" fn slint_box_layout_info_ortho(
2170 cells: Slice<LayoutItemInfo>,
2171 padding: &Padding,
2172 ) -> LayoutInfo {
2173 super::box_layout_info_ortho(cells, padding)
2174 }
2175
2176 pub type FlexboxMeasureFnC = unsafe extern "C" fn(
2180 user_data: *mut core::ffi::c_void,
2181 child_index: usize,
2182 known_width: Coord,
2183 known_height: Coord,
2184 out_width: *mut Coord,
2185 out_height: *mut Coord,
2186 );
2187
2188 #[unsafe(no_mangle)]
2189 #[allow(unsafe_code)]
2190 pub extern "C" fn slint_solve_flexbox_layout(
2191 data: &FlexboxLayoutData,
2192 repeater_indices: Slice<u32>,
2193 result: &mut SharedVector<Coord>,
2194 measure_fn: *const core::ffi::c_void,
2195 measure_user_data: *mut core::ffi::c_void,
2196 ) {
2197 const {
2200 assert!(
2201 core::mem::size_of::<*const core::ffi::c_void>()
2202 == core::mem::size_of::<FlexboxMeasureFnC>()
2203 );
2204 }
2205 let measure_fn: Option<FlexboxMeasureFnC> = if measure_fn.is_null() {
2206 None
2207 } else {
2208 Some(unsafe {
2209 core::mem::transmute::<*const core::ffi::c_void, FlexboxMeasureFnC>(measure_fn)
2210 })
2211 };
2212 if let Some(c_measure) = measure_fn {
2213 let mut measure = |child_index: usize,
2214 known_w: Option<Coord>,
2215 known_h: Option<Coord>|
2216 -> (Coord, Coord) {
2217 let mut out_w: Coord = 0 as _;
2218 let mut out_h: Coord = 0 as _;
2219 unsafe {
2222 c_measure(
2223 measure_user_data,
2224 child_index,
2225 known_w.unwrap_or(-1 as _),
2226 known_h.unwrap_or(-1 as _),
2227 &mut out_w,
2228 &mut out_h,
2229 );
2230 }
2231 (out_w, out_h)
2232 };
2233 *result = super::solve_flexbox_layout_with_measure(
2234 data,
2235 repeater_indices,
2236 Some(&mut measure),
2237 );
2238 } else {
2239 *result = super::solve_flexbox_layout(data, repeater_indices);
2240 }
2241 }
2242
2243 #[unsafe(no_mangle)]
2244 pub extern "C" fn slint_flexbox_layout_info_main_axis(
2246 cells: Slice<FlexboxLayoutItemInfo>,
2247 spacing: Coord,
2248 padding: &Padding,
2249 flex_wrap: FlexboxLayoutWrap,
2250 ) -> LayoutInfo {
2251 super::flexbox_layout_info_main_axis(cells, spacing, padding, flex_wrap)
2252 }
2253
2254 #[unsafe(no_mangle)]
2255 pub extern "C" fn slint_flexbox_layout_info_cross_axis(
2257 cells_h: Slice<FlexboxLayoutItemInfo>,
2258 cells_v: Slice<FlexboxLayoutItemInfo>,
2259 spacing_h: Coord,
2260 spacing_v: Coord,
2261 padding_h: &Padding,
2262 padding_v: &Padding,
2263 direction: FlexboxLayoutDirection,
2264 flex_wrap: FlexboxLayoutWrap,
2265 constraint_size: Coord,
2266 ) -> LayoutInfo {
2267 super::flexbox_layout_info_cross_axis(
2268 cells_h,
2269 cells_v,
2270 spacing_h,
2271 spacing_v,
2272 padding_h,
2273 padding_v,
2274 direction,
2275 flex_wrap,
2276 constraint_size,
2277 )
2278 }
2279}
2280
2281#[cfg(test)]
2282mod tests {
2283 use super::*;
2284
2285 fn collect_from_organized_data(
2286 organized_data: &GridLayoutOrganizedData,
2287 num_cells: usize,
2288 repeater_indices: Slice<u32>,
2289 repeater_steps: Slice<u32>,
2290 ) -> Vec<(u16, u16, u16, u16)> {
2291 let mut result = Vec::new();
2292 for i in 0..num_cells {
2293 let col_and_span = organized_data.col_or_row_and_span(
2294 i,
2295 Orientation::Horizontal,
2296 &repeater_indices,
2297 &repeater_steps,
2298 );
2299 let row_and_span = organized_data.col_or_row_and_span(
2300 i,
2301 Orientation::Vertical,
2302 &repeater_indices,
2303 &repeater_steps,
2304 );
2305 result.push((col_and_span.0, col_and_span.1, row_and_span.0, row_and_span.1));
2306 }
2307 result
2308 }
2309
2310 #[test]
2311 fn test_organized_data_generator_2_fixed_cells() {
2312 let mut result = GridLayoutOrganizedData::default();
2314 let num_cells = 2;
2315 let mut generator = OrganizedDataGenerator::new(&[], &[], num_cells, 0, 0, &mut result);
2316 generator.add(0, 1, 0, 1);
2317 generator.add(1, 2, 0, 3);
2318 assert_eq!(result.as_slice(), &[0, 1, 0, 1, 1, 2, 0, 3]);
2319
2320 let repeater_indices = Slice::from_slice(&[]);
2321 let empty_steps = Slice::from_slice(&[]);
2322 let collected_data =
2323 collect_from_organized_data(&result, num_cells, repeater_indices, empty_steps);
2324 assert_eq!(collected_data.as_slice(), &[(0, 1, 0, 1), (1, 2, 0, 3)]);
2325
2326 assert_eq!(
2327 result.max_value(num_cells, Orientation::Horizontal, &repeater_indices, &empty_steps),
2328 3
2329 );
2330 assert_eq!(
2331 result.max_value(num_cells, Orientation::Vertical, &repeater_indices, &empty_steps),
2332 3
2333 );
2334 }
2335
2336 #[test]
2337 fn test_organized_data_generator_1_fixed_cell_1_repeater() {
2338 let mut result = GridLayoutOrganizedData::default();
2340 let num_cells = 4;
2341 let repeater_indices = &[1u32, 3u32];
2342 let mut generator =
2343 OrganizedDataGenerator::new(repeater_indices, &[], 1, 1, 3, &mut result);
2344 generator.add(0, 1, 0, 2); generator.add(1, 2, 1, 3); generator.add(1, 1, 2, 4);
2347 generator.add(2, 2, 3, 5);
2348 assert_eq!(
2349 result.as_slice(),
2350 &[
2351 0, 1, 0, 2, 8, 4, 0, 0, 1, 2, 1, 3, 1, 1, 2, 4, 2, 2, 3, 5, ]
2357 );
2358 let repeater_indices = Slice::from_slice(repeater_indices);
2359 let empty_steps = Slice::from_slice(&[]);
2360 let collected_data =
2361 collect_from_organized_data(&result, num_cells, repeater_indices, empty_steps);
2362 assert_eq!(
2363 collected_data.as_slice(),
2364 &[(0, 1, 0, 2), (1, 2, 1, 3), (1, 1, 2, 4), (2, 2, 3, 5)]
2365 );
2366
2367 assert_eq!(
2368 result.max_value(num_cells, Orientation::Horizontal, &repeater_indices, &empty_steps),
2369 4
2370 );
2371 assert_eq!(
2372 result.max_value(num_cells, Orientation::Vertical, &repeater_indices, &empty_steps),
2373 8
2374 );
2375 }
2376
2377 #[test]
2378
2379 fn test_organize_data_with_auto_and_spans() {
2380 let auto = i_slint_common::ROW_COL_AUTO;
2381 let input = std::vec![
2382 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 2., rowspan: -1. },
2383 GridLayoutInputData { new_row: false, col: auto, row: auto, colspan: 1., rowspan: 2. },
2384 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 2., rowspan: 1. },
2385 GridLayoutInputData { new_row: true, col: -2., row: 80000., colspan: 2., rowspan: 1. },
2386 ];
2387 let repeater_indices = Slice::from_slice(&[]);
2388 let (organized_data, errors) = organize_grid_layout_impl(
2389 Slice::from_slice(&input),
2390 repeater_indices,
2391 Slice::from_slice(&[]),
2392 );
2393 assert_eq!(
2394 organized_data.as_slice(),
2395 &[
2396 0, 2, 0, 0, 2, 1, 0, 2, 0, 2, 1, 1, 0, 2, 65535, 1, ]
2401 );
2402 assert_eq!(errors.len(), 3);
2403 assert_eq!(errors[0], "cell rowspan -1 is negative, clamping to 0");
2405 assert_eq!(errors[1], "cell row 80000 is too large, clamping to 65535");
2406 assert_eq!(errors[2], "cell col -2 is negative, clamping to 0");
2407 let empty_steps = Slice::from_slice(&[]);
2408 let collected_data = collect_from_organized_data(
2409 &organized_data,
2410 input.len(),
2411 repeater_indices,
2412 empty_steps,
2413 );
2414 assert_eq!(
2415 collected_data.as_slice(),
2416 &[(0, 2, 0, 0), (2, 1, 0, 2), (0, 2, 1, 1), (0, 2, 65535, 1)]
2417 );
2418 assert_eq!(
2419 organized_data.max_value(3, Orientation::Horizontal, &repeater_indices, &empty_steps),
2420 3
2421 );
2422 assert_eq!(
2423 organized_data.max_value(3, Orientation::Vertical, &repeater_indices, &empty_steps),
2424 2
2425 );
2426 }
2427
2428 #[test]
2429 fn test_organize_data_1_empty_repeater() {
2430 let auto = i_slint_common::ROW_COL_AUTO;
2432 let cell =
2433 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2434 let input = std::vec![cell];
2435 let repeater_indices = Slice::from_slice(&[1u32, 0u32]);
2436 let (organized_data, errors) = organize_grid_layout_impl(
2437 Slice::from_slice(&input),
2438 repeater_indices,
2439 Slice::from_slice(&[]),
2440 );
2441 assert_eq!(
2442 organized_data.as_slice(),
2443 &[
2444 0, 1, 0, 1, 0, 0, 0, 0
2446 ] );
2448 assert_eq!(errors.len(), 0);
2449 let empty_steps = Slice::from_slice(&[]);
2450 let collected_data = collect_from_organized_data(
2451 &organized_data,
2452 input.len(),
2453 repeater_indices,
2454 empty_steps,
2455 );
2456 assert_eq!(collected_data.as_slice(), &[(0, 1, 0, 1)]);
2457 assert_eq!(
2458 organized_data.max_value(1, Orientation::Horizontal, &repeater_indices, &empty_steps),
2459 1
2460 );
2461 }
2462
2463 #[test]
2464 fn test_organize_data_4_repeaters() {
2465 let auto = i_slint_common::ROW_COL_AUTO;
2466 let mut cell =
2467 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2468 let mut input = std::vec![cell.clone()];
2469 for _ in 0..8 {
2470 cell.new_row = false;
2471 input.push(cell.clone());
2472 }
2473 let repeater_indices = Slice::from_slice(&[0u32, 0u32, 1u32, 4u32, 6u32, 2u32, 8u32, 0u32]);
2474 let (organized_data, errors) = organize_grid_layout_impl(
2475 Slice::from_slice(&input),
2476 repeater_indices,
2477 Slice::from_slice(&[]),
2478 );
2479 assert_eq!(
2480 organized_data.as_slice(),
2481 &[
2482 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, ]
2496 );
2497 assert_eq!(errors.len(), 0);
2498 let empty_steps = Slice::from_slice(&[]);
2499 let collected_data = collect_from_organized_data(
2500 &organized_data,
2501 input.len(),
2502 repeater_indices,
2503 empty_steps,
2504 );
2505 assert_eq!(
2506 collected_data.as_slice(),
2507 &[
2508 (0, 1, 0, 1),
2509 (1, 1, 0, 1),
2510 (2, 1, 0, 1),
2511 (3, 1, 0, 1),
2512 (4, 1, 0, 1),
2513 (5, 1, 0, 1),
2514 (6, 1, 0, 1),
2515 (7, 1, 0, 1),
2516 (8, 1, 0, 1),
2517 ]
2518 );
2519 let empty_steps = Slice::from_slice(&[]);
2520 assert_eq!(
2521 organized_data.max_value(
2522 input.len(),
2523 Orientation::Horizontal,
2524 &repeater_indices,
2525 &empty_steps
2526 ),
2527 9
2528 );
2529 }
2530
2531 #[test]
2532 fn test_organize_data_repeated_rows() {
2533 let auto = i_slint_common::ROW_COL_AUTO;
2534 let mut input = Vec::new();
2535 let num_rows: u32 = 3;
2536 let num_columns: u32 = 2;
2537 for _ in 0..num_rows {
2539 let mut cell = GridLayoutInputData {
2540 new_row: true,
2541 col: auto,
2542 row: auto,
2543 colspan: 1.,
2544 rowspan: 1.,
2545 };
2546 input.push(cell.clone());
2547 cell.new_row = false;
2548 input.push(cell.clone());
2549 }
2550 let repeater_indices_arr = [0_u32, num_rows];
2552 let repeater_steps_arr = [num_columns];
2553 let repeater_steps = Slice::from_slice(&repeater_steps_arr);
2554 let repeater_indices = Slice::from_slice(&repeater_indices_arr);
2555 let (organized_data, errors) =
2556 organize_grid_layout_impl(Slice::from_slice(&input), repeater_indices, repeater_steps);
2557 assert_eq!(
2558 organized_data.as_slice(),
2559 &[
2560 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, ]
2565 );
2566 assert_eq!(errors.len(), 0);
2567 let collected_data = collect_from_organized_data(
2568 &organized_data,
2569 input.len(),
2570 repeater_indices,
2571 repeater_steps,
2572 );
2573 assert_eq!(
2574 collected_data.as_slice(),
2575 &[(0, 1, 0, 1), (1, 1, 0, 1), (0, 1, 1, 1), (1, 1, 1, 1), (0, 1, 2, 1), (1, 1, 2, 1),]
2577 );
2578 assert_eq!(
2579 organized_data.max_value(
2580 input.len(),
2581 Orientation::Horizontal,
2582 &repeater_indices,
2583 &repeater_steps
2584 ),
2585 2
2586 );
2587 assert_eq!(
2588 organized_data.max_value(
2589 input.len(),
2590 Orientation::Vertical,
2591 &repeater_indices,
2592 &repeater_steps
2593 ),
2594 3
2595 );
2596
2597 let mut layout_cache_v = SharedVector::<Coord>::default();
2599 let mut generator = GridLayoutCacheGenerator::new(
2600 repeater_indices.as_slice(),
2601 repeater_steps.as_slice(),
2602 0, 1, 6, &mut layout_cache_v,
2606 );
2607 generator.add(0., 50.);
2609 generator.add(0., 50.);
2610 generator.add(50., 50.);
2612 generator.add(50., 50.);
2613 generator.add(100., 50.);
2615 generator.add(100., 50.);
2616 assert_eq!(
2617 layout_cache_v.as_slice(),
2618 &[
2619 2., 4., 0., 50., 0., 50., 50., 50., 50., 50., 100., 50., 100., 50., ]
2624 );
2625
2626 let layout_cache_v_access = |jump_index: usize,
2628 repeater_index: usize,
2629 stride: usize,
2630 child_offset: usize|
2631 -> Coord {
2632 let base = layout_cache_v[jump_index] as usize;
2633 let data_idx = base + repeater_index * stride + child_offset;
2634 layout_cache_v[data_idx]
2635 };
2636 assert_eq!(layout_cache_v_access(0, 0, 4, 0), 0.);
2639 assert_eq!(layout_cache_v_access(0, 1, 4, 0), 50.);
2640 assert_eq!(layout_cache_v_access(0, 2, 4, 0), 100.);
2641 assert_eq!(layout_cache_v_access(0, 0, 4, 2), 0.);
2643 assert_eq!(layout_cache_v_access(0, 1, 4, 2), 50.);
2644 assert_eq!(layout_cache_v_access(0, 2, 4, 2), 100.);
2645 }
2646
2647 #[test]
2648 fn test_organize_data_repeated_rows_multiple_repeaters() {
2649 let auto = i_slint_common::ROW_COL_AUTO;
2650 let mut input = Vec::new();
2651 let num_rows: u32 = 5;
2652 let mut cell =
2653 GridLayoutInputData { new_row: true, col: auto, row: auto, colspan: 1., rowspan: 1. };
2654 for _ in 0..3 {
2656 cell.new_row = true;
2657 input.push(cell.clone());
2658 cell.new_row = false;
2659 input.push(cell.clone());
2660 }
2661 for _ in 0..2 {
2663 cell.new_row = true;
2664 input.push(cell.clone());
2665 cell.new_row = false;
2666 input.push(cell.clone());
2667 cell.new_row = false;
2668 input.push(cell.clone());
2669 }
2670 let repeater_indices_arr = [0_u32, 3, 6, 2];
2673 let repeater_steps_arr = [2, 3];
2674 let repeater_steps = Slice::from_slice(&repeater_steps_arr);
2675 let repeater_indices = Slice::from_slice(&repeater_indices_arr);
2676 let (organized_data, errors) =
2677 organize_grid_layout_impl(Slice::from_slice(&input), repeater_indices, repeater_steps);
2678 assert_eq!(
2679 organized_data.as_slice(),
2680 &[
2681 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, ]
2691 );
2692 assert_eq!(errors.len(), 0);
2693 let collected_data = collect_from_organized_data(
2694 &organized_data,
2695 input.len(),
2696 repeater_indices,
2697 repeater_steps,
2698 );
2699 assert_eq!(
2700 collected_data.as_slice(),
2701 &[
2703 (0, 1, 0, 1),
2704 (1, 1, 0, 1),
2705 (0, 1, 1, 1),
2706 (1, 1, 1, 1),
2707 (0, 1, 2, 1),
2708 (1, 1, 2, 1),
2709 (0, 1, 3, 1),
2710 (1, 1, 3, 1),
2711 (2, 1, 3, 1),
2712 (0, 1, 4, 1),
2713 (1, 1, 4, 1),
2714 (2, 1, 4, 1)
2715 ]
2716 );
2717 assert_eq!(
2718 organized_data.max_value(
2719 input.len(),
2720 Orientation::Horizontal,
2721 &repeater_indices,
2722 &repeater_steps
2723 ),
2724 3 );
2726 assert_eq!(
2727 organized_data.max_value(
2728 input.len(),
2729 Orientation::Vertical,
2730 &repeater_indices,
2731 &repeater_steps
2732 ),
2733 num_rows as usize );
2735
2736 let mut layout_cache_v = SharedVector::<Coord>::default();
2738 let mut generator = GridLayoutCacheGenerator::new(
2739 repeater_indices.as_slice(),
2740 repeater_steps.as_slice(),
2741 0, 2, 12, &mut layout_cache_v,
2745 );
2746 generator.add(0., 50.);
2748 generator.add(0., 50.);
2749 generator.add(50., 50.);
2751 generator.add(50., 50.);
2752 generator.add(100., 50.);
2754 generator.add(100., 50.);
2755 generator.add(150., 50.);
2757 generator.add(150., 50.);
2758 generator.add(150., 50.);
2759 generator.add(200., 50.);
2761 generator.add(200., 50.);
2762 generator.add(200., 50.);
2763 assert_eq!(
2764 layout_cache_v.as_slice(),
2765 &[
2766 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., ]
2774 );
2775
2776 let layout_cache_v_access = |jump_index: usize,
2778 repeater_index: usize,
2779 stride: usize,
2780 child_offset: usize|
2781 -> Coord {
2782 let base = layout_cache_v[jump_index] as usize;
2783 let data_idx = base + repeater_index * stride + child_offset;
2784 layout_cache_v[data_idx]
2785 };
2786 assert_eq!(layout_cache_v_access(0, 0, 4, 0), 0.);
2788 assert_eq!(layout_cache_v_access(0, 1, 4, 0), 50.);
2789 assert_eq!(layout_cache_v_access(0, 2, 4, 0), 100.);
2790 assert_eq!(layout_cache_v_access(0, 0, 4, 2), 0.);
2792 assert_eq!(layout_cache_v_access(0, 1, 4, 2), 50.);
2793 assert_eq!(layout_cache_v_access(0, 2, 4, 2), 100.);
2794 assert_eq!(layout_cache_v_access(2, 0, 6, 0), 150.);
2796 assert_eq!(layout_cache_v_access(2, 1, 6, 0), 200.);
2797 assert_eq!(layout_cache_v_access(2, 0, 6, 4), 150.);
2799 assert_eq!(layout_cache_v_access(2, 1, 6, 4), 200.);
2800 }
2801
2802 #[test]
2803 fn test_layout_cache_generator_2_fixed_cells() {
2804 let mut result = SharedVector::<Coord>::default();
2806 result.resize(2 * 2, 0 as _);
2807 let mut generator = LayoutCacheGenerator::new(&[], &mut result);
2808 generator.add(0., 50.); generator.add(80., 50.); assert_eq!(result.as_slice(), &[0., 50., 80., 50.]);
2811 }
2812
2813 #[test]
2814 fn test_layout_cache_generator_1_fixed_cell_1_repeater() {
2815 let mut result = SharedVector::<Coord>::default();
2817 let repeater_indices = &[1, 3];
2818 result.resize(4 * 2 + repeater_indices.len(), 0 as _);
2819 let mut generator = LayoutCacheGenerator::new(repeater_indices, &mut result);
2820 generator.add(0., 50.); generator.add(80., 50.); generator.add(160., 50.);
2823 generator.add(240., 50.);
2824 assert_eq!(
2825 result.as_slice(),
2826 &[
2827 0., 50., 4., 5., 80., 50., 160., 50., 240., 50. ]
2831 );
2832 }
2833
2834 #[test]
2835 fn test_layout_cache_generator_4_repeaters() {
2836 let mut result = SharedVector::<Coord>::default();
2838 let repeater_indices = &[1, 0, 1, 4, 6, 2, 8, 0];
2839 result.resize(8 * 2 + repeater_indices.len(), 0 as _);
2840 let mut generator = LayoutCacheGenerator::new(repeater_indices, &mut result);
2841 generator.add(0., 50.); generator.add(80., 10.); generator.add(160., 10.);
2844 generator.add(240., 10.);
2845 generator.add(320., 10.); generator.add(400., 80.); generator.add(500., 20.); generator.add(600., 20.); assert_eq!(
2850 result.as_slice(),
2851 &[
2852 0., 50., 12., 13., 12., 13., 400., 80., 20., 21., 0., 0., 80., 10., 160., 10., 240., 10., 320., 10., 500., 20., 600., 20. ]
2861 );
2862 }
2863}