1use crate::items::{DialogButtonRole, LayoutAlignment};
9use crate::{slice::Slice, Coord, SharedVector};
10use alloc::vec::Vec;
11
12pub use crate::items::Orientation;
13
14#[repr(C)]
17#[derive(Clone, Copy, Debug, PartialEq)]
18pub struct LayoutInfo {
19 pub max: Coord,
21 pub max_percent: Coord,
23 pub min: Coord,
25 pub min_percent: Coord,
27 pub preferred: Coord,
29 pub stretch: f32,
31}
32
33impl Default for LayoutInfo {
34 fn default() -> Self {
35 LayoutInfo {
36 min: 0 as _,
37 max: Coord::MAX,
38 min_percent: 0 as _,
39 max_percent: 100 as _,
40 preferred: 0 as _,
41 stretch: 0 as _,
42 }
43 }
44}
45
46impl LayoutInfo {
47 #[must_use]
49 pub fn merge(&self, other: &LayoutInfo) -> Self {
50 Self {
51 min: self.min.max(other.min),
52 max: self.max.min(other.max),
53 min_percent: self.min_percent.max(other.min_percent),
54 max_percent: self.max_percent.min(other.max_percent),
55 preferred: self.preferred.max(other.preferred),
56 stretch: self.stretch.min(other.stretch),
57 }
58 }
59
60 #[must_use]
62 pub fn preferred_bounded(&self) -> Coord {
63 self.preferred.min(self.max).max(self.min)
64 }
65}
66
67impl core::ops::Add for LayoutInfo {
68 type Output = Self;
69
70 fn add(self, rhs: Self) -> Self::Output {
71 self.merge(&rhs)
72 }
73}
74
75pub fn min_max_size_for_layout_constraints(
77 constraints_horizontal: LayoutInfo,
78 constraints_vertical: LayoutInfo,
79) -> (Option<crate::api::LogicalSize>, Option<crate::api::LogicalSize>) {
80 let min_width = constraints_horizontal.min.min(constraints_horizontal.max) as f32;
81 let min_height = constraints_vertical.min.min(constraints_vertical.max) as f32;
82 let max_width = constraints_horizontal.max.max(constraints_horizontal.min) as f32;
83 let max_height = constraints_vertical.max.max(constraints_vertical.min) as f32;
84
85 let min_size = if min_width > 0. || min_height > 0. || cfg!(target_arch = "wasm32") {
89 Some(crate::api::LogicalSize::new(min_width, min_height))
90 } else {
91 None
92 };
93
94 let max_size = if (max_width > 0.
95 && max_height > 0.
96 && (max_width < i32::MAX as f32 || max_height < i32::MAX as f32))
97 || cfg!(target_arch = "wasm32")
98 {
99 let window_size_max = 16_777_215.;
101 Some(crate::api::LogicalSize::new(
102 max_width.min(window_size_max),
103 max_height.min(window_size_max),
104 ))
105 } else {
106 None
107 };
108
109 (min_size, max_size)
110}
111
112trait Saturating {
115 fn add(_: Self, _: Self) -> Self;
116}
117impl Saturating for i32 {
118 #[inline]
119 fn add(a: Self, b: Self) -> Self {
120 a.saturating_add(b)
121 }
122}
123impl Saturating for f32 {
124 #[inline]
125 fn add(a: Self, b: Self) -> Self {
126 a + b
127 }
128}
129
130mod grid_internal {
131 use super::*;
132
133 fn order_coord<T: PartialOrd>(a: &T, b: &T) -> core::cmp::Ordering {
134 a.partial_cmp(b).unwrap_or(core::cmp::Ordering::Equal)
135 }
136
137 #[derive(Debug, Clone)]
138 pub struct LayoutData {
139 pub min: Coord,
141 pub max: Coord,
142 pub pref: Coord,
143 pub stretch: f32,
144
145 pub pos: Coord,
147 pub size: Coord,
148 }
149
150 impl Default for LayoutData {
151 fn default() -> Self {
152 LayoutData {
153 min: 0 as _,
154 max: Coord::MAX,
155 pref: 0 as _,
156 stretch: f32::MAX,
157 pos: 0 as _,
158 size: 0 as _,
159 }
160 }
161 }
162
163 trait Adjust {
164 fn can_grow(_: &LayoutData) -> Coord;
165 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord;
166 fn distribute(_: &mut LayoutData, val: Coord);
167 }
168
169 struct Grow;
170 impl Adjust for Grow {
171 fn can_grow(it: &LayoutData) -> Coord {
172 it.max - it.size
173 }
174
175 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord {
176 expected_size - current_size
177 }
178
179 fn distribute(it: &mut LayoutData, val: Coord) {
180 it.size += val;
181 }
182 }
183
184 struct Shrink;
185 impl Adjust for Shrink {
186 fn can_grow(it: &LayoutData) -> Coord {
187 it.size - it.min
188 }
189
190 fn to_distribute(expected_size: Coord, current_size: Coord) -> Coord {
191 current_size - expected_size
192 }
193
194 fn distribute(it: &mut LayoutData, val: Coord) {
195 it.size -= val;
196 }
197 }
198
199 #[allow(clippy::unnecessary_cast)] fn adjust_items<A: Adjust>(data: &mut [LayoutData], size_without_spacing: Coord) -> Option<()> {
201 loop {
202 let size_cannot_grow: Coord = data
203 .iter()
204 .filter(|it| A::can_grow(it) <= 0 as _)
205 .map(|it| it.size)
206 .fold(0 as Coord, Saturating::add);
207
208 let total_stretch: f32 =
209 data.iter().filter(|it| A::can_grow(it) > 0 as _).map(|it| it.stretch).sum();
210
211 let actual_stretch = |s: f32| if total_stretch <= 0. { 1. } else { s };
212
213 let max_grow = data
214 .iter()
215 .filter(|it| A::can_grow(it) > 0 as _)
216 .map(|it| A::can_grow(it) as f32 / actual_stretch(it.stretch))
217 .min_by(order_coord)?;
218
219 let current_size: Coord = data
220 .iter()
221 .filter(|it| A::can_grow(it) > 0 as _)
222 .map(|it| it.size)
223 .fold(0 as _, Saturating::add);
224
225 let to_distribute =
227 A::to_distribute(size_without_spacing, size_cannot_grow + current_size) as f32;
228 if to_distribute <= 0. || max_grow <= 0. {
229 return Some(());
230 }
231
232 let grow = if total_stretch <= 0. {
233 to_distribute
234 / (data.iter().filter(|it| A::can_grow(it) > 0 as _).count() as Coord) as f32
235 } else {
236 to_distribute / total_stretch
237 }
238 .min(max_grow);
239
240 let mut distributed = 0 as Coord;
241 for it in data.iter_mut().filter(|it| A::can_grow(it) > 0 as Coord) {
242 let val = (grow * actual_stretch(it.stretch)) as Coord;
243 A::distribute(it, val);
244 distributed += val;
245 }
246
247 if distributed <= 0 as Coord {
248 if let Some(it) = data
251 .iter_mut()
252 .filter(|it| A::can_grow(it) > 0 as _)
253 .max_by(|a, b| actual_stretch(a.stretch).total_cmp(&b.stretch))
254 {
255 A::distribute(it, to_distribute as Coord);
256 }
257 return Some(());
258 }
259 }
260 }
261
262 pub fn layout_items(data: &mut [LayoutData], start_pos: Coord, size: Coord, spacing: Coord) {
263 let size_without_spacing = size - spacing * (data.len() - 1) as Coord;
264
265 let mut pref = 0 as Coord;
266 for it in data.iter_mut() {
267 it.size = it.pref;
268 pref += it.pref;
269 }
270 if size_without_spacing >= pref {
271 adjust_items::<Grow>(data, size_without_spacing);
272 } else if size_without_spacing < pref {
273 adjust_items::<Shrink>(data, size_without_spacing);
274 }
275
276 let mut pos = start_pos;
277 for it in data.iter_mut() {
278 it.pos = pos;
279 pos = Saturating::add(pos, Saturating::add(it.size, spacing));
280 }
281 }
282
283 #[test]
284 #[allow(clippy::float_cmp)] fn test_layout_items() {
286 let my_items = &mut [
287 LayoutData { min: 100., max: 200., pref: 100., stretch: 1., ..Default::default() },
288 LayoutData { min: 50., max: 300., pref: 100., stretch: 1., ..Default::default() },
289 LayoutData { min: 50., max: 150., pref: 100., stretch: 1., ..Default::default() },
290 ];
291
292 layout_items(my_items, 100., 650., 0.);
293 assert_eq!(my_items[0].size, 200.);
294 assert_eq!(my_items[1].size, 300.);
295 assert_eq!(my_items[2].size, 150.);
296
297 layout_items(my_items, 100., 200., 0.);
298 assert_eq!(my_items[0].size, 100.);
299 assert_eq!(my_items[1].size, 50.);
300 assert_eq!(my_items[2].size, 50.);
301
302 layout_items(my_items, 100., 300., 0.);
303 assert_eq!(my_items[0].size, 100.);
304 assert_eq!(my_items[1].size, 100.);
305 assert_eq!(my_items[2].size, 100.);
306 }
307
308 pub fn to_layout_data(
310 data: &[GridLayoutCellData],
311 spacing: Coord,
312 size: Option<Coord>,
313 ) -> Vec<LayoutData> {
314 let mut num = 0usize;
315 for cell in data {
316 num = num.max(cell.col_or_row as usize + cell.span.max(1) as usize);
317 }
318 if num < 1 {
319 return Default::default();
320 }
321 let mut layout_data =
322 alloc::vec![grid_internal::LayoutData { stretch: 1., ..Default::default() }; num];
323 let mut has_spans = false;
324 for cell in data {
325 let constraint = &cell.constraint;
326 let mut max = constraint.max;
327 if let Some(size) = size {
328 max = max.min(size * constraint.max_percent / 100 as Coord);
329 }
330 for c in 0..(cell.span as usize) {
331 let cdata = &mut layout_data[cell.col_or_row as usize + c];
332 cdata.max = cdata.max.min(max);
333 }
334 if cell.span == 1 {
335 let mut min = constraint.min;
336 if let Some(size) = size {
337 min = min.max(size * constraint.min_percent / 100 as Coord);
338 }
339 let pref = constraint.preferred.min(max).max(min);
340 let cdata = &mut layout_data[cell.col_or_row as usize];
341 cdata.min = cdata.min.max(min);
342 cdata.pref = cdata.pref.max(pref);
343 cdata.stretch = cdata.stretch.min(constraint.stretch);
344 } else {
345 has_spans = true;
346 }
347 }
348 if has_spans {
349 for cell in data.iter().filter(|cell| cell.span > 1) {
351 let span_data = &mut layout_data
352 [(cell.col_or_row as usize)..(cell.col_or_row + cell.span) as usize];
353 let mut min = cell.constraint.min;
354 if let Some(size) = size {
355 min = min.max(size * cell.constraint.min_percent / 100 as Coord);
356 }
357 grid_internal::layout_items(span_data, 0 as _, min, spacing);
358 for cdata in span_data {
359 if cdata.min < cdata.size {
360 cdata.min = cdata.size;
361 }
362 }
363 }
364 for cell in data.iter().filter(|cell| cell.span > 1) {
366 let span_data = &mut layout_data
367 [(cell.col_or_row as usize)..(cell.col_or_row + cell.span) as usize];
368 let mut max = cell.constraint.max;
369 if let Some(size) = size {
370 max = max.min(size * cell.constraint.max_percent / 100 as Coord);
371 }
372 grid_internal::layout_items(span_data, 0 as _, max, spacing);
373 for cdata in span_data {
374 if cdata.max > cdata.size {
375 cdata.max = cdata.size;
376 }
377 }
378 }
379 for cell in data.iter().filter(|cell| cell.span > 1) {
381 let span_data = &mut layout_data
382 [(cell.col_or_row as usize)..(cell.col_or_row + cell.span) as usize];
383 grid_internal::layout_items(span_data, 0 as _, cell.constraint.preferred, spacing);
384 for cdata in span_data {
385 cdata.pref = cdata.pref.max(cdata.size).min(cdata.max).max(cdata.min);
386 }
387 }
388 for cell in data.iter().filter(|cell| cell.span > 1) {
390 let span_data = &mut layout_data
391 [(cell.col_or_row as usize)..(cell.col_or_row + cell.span) as usize];
392 let total_stretch: f32 = span_data.iter().map(|c| c.stretch).sum();
393 if total_stretch > cell.constraint.stretch {
394 for cdata in span_data {
395 cdata.stretch *= cell.constraint.stretch / total_stretch;
396 }
397 }
398 }
399 }
400 layout_data
401 }
402}
403
404#[repr(C)]
405pub struct Constraint {
406 pub min: Coord,
407 pub max: Coord,
408}
409
410impl Default for Constraint {
411 fn default() -> Self {
412 Constraint { min: 0 as Coord, max: Coord::MAX }
413 }
414}
415
416#[repr(C)]
417#[derive(Copy, Clone, Debug, Default)]
418pub struct Padding {
419 pub begin: Coord,
420 pub end: Coord,
421}
422
423#[repr(C)]
424#[derive(Debug)]
425pub struct GridLayoutData<'a> {
426 pub size: Coord,
427 pub spacing: Coord,
428 pub padding: Padding,
429 pub cells: Slice<'a, GridLayoutCellData>,
430}
431
432#[repr(C)]
433#[derive(Default, Debug)]
434pub struct GridLayoutCellData {
435 pub col_or_row: u16,
437 pub span: u16,
439 pub constraint: LayoutInfo,
440}
441
442pub fn solve_grid_layout(data: &GridLayoutData) -> SharedVector<Coord> {
444 let mut layout_data =
445 grid_internal::to_layout_data(data.cells.as_slice(), data.spacing, Some(data.size));
446
447 if layout_data.is_empty() {
448 return Default::default();
449 }
450
451 grid_internal::layout_items(
452 &mut layout_data,
453 data.padding.begin,
454 data.size - (data.padding.begin + data.padding.end),
455 data.spacing,
456 );
457
458 let mut result = SharedVector::with_capacity(4 * data.cells.len());
459 for cell in data.cells.iter() {
460 let cdata = &layout_data[cell.col_or_row as usize];
461 result.push(cdata.pos);
462 result.push(if cell.span > 0 {
463 let first_cell = &layout_data[cell.col_or_row as usize];
464 let last_cell = &layout_data[cell.col_or_row as usize + cell.span as usize - 1];
465 last_cell.pos + last_cell.size - first_cell.pos
466 } else {
467 0 as Coord
468 });
469 }
470 result
471}
472
473pub fn grid_layout_info(
474 cells: Slice<GridLayoutCellData>,
475 spacing: Coord,
476 padding: &Padding,
477) -> LayoutInfo {
478 let layout_data = grid_internal::to_layout_data(cells.as_slice(), spacing, None);
479 if layout_data.is_empty() {
480 return Default::default();
481 }
482 let spacing_w = spacing * (layout_data.len() - 1) as Coord + padding.begin + padding.end;
483 let min = layout_data.iter().map(|data| data.min).sum::<Coord>() + spacing_w;
484 let max = layout_data.iter().map(|data| data.max).fold(spacing_w, Saturating::add);
485 let preferred = layout_data.iter().map(|data| data.pref).sum::<Coord>() + spacing_w;
486 let stretch = layout_data.iter().map(|data| data.stretch).sum::<f32>();
487 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
488}
489
490#[repr(C)]
491#[derive(Debug)]
492pub struct BoxLayoutData<'a> {
496 pub size: Coord,
497 pub spacing: Coord,
498 pub padding: Padding,
499 pub alignment: LayoutAlignment,
500 pub cells: Slice<'a, BoxLayoutCellData>,
501}
502
503#[repr(C)]
504#[derive(Default, Debug, Clone)]
505pub struct BoxLayoutCellData {
506 pub constraint: LayoutInfo,
507}
508
509pub fn solve_box_layout(data: &BoxLayoutData, repeater_indexes: Slice<u32>) -> SharedVector<Coord> {
511 let mut result = SharedVector::<Coord>::default();
512 result.resize(data.cells.len() * 2 + repeater_indexes.len(), 0 as _);
513
514 if data.cells.is_empty() {
515 return result;
516 }
517
518 let mut layout_data: Vec<_> = data
519 .cells
520 .iter()
521 .map(|c| {
522 let min = c.constraint.min.max(c.constraint.min_percent * data.size / 100 as Coord);
523 let max = c.constraint.max.min(c.constraint.max_percent * data.size / 100 as Coord);
524 grid_internal::LayoutData {
525 min,
526 max,
527 pref: c.constraint.preferred.min(max).max(min),
528 stretch: c.constraint.stretch,
529 ..Default::default()
530 }
531 })
532 .collect();
533
534 let size_without_padding = data.size - data.padding.begin - data.padding.end;
535 let pref_size: Coord = layout_data.iter().map(|it| it.pref).sum();
536 let num_spacings = (layout_data.len() - 1) as Coord;
537 let spacings = data.spacing * num_spacings;
538
539 let align = match data.alignment {
540 LayoutAlignment::Stretch => {
541 grid_internal::layout_items(
542 &mut layout_data,
543 data.padding.begin,
544 size_without_padding,
545 data.spacing,
546 );
547 None
548 }
549 _ if size_without_padding <= pref_size + spacings => {
550 grid_internal::layout_items(
551 &mut layout_data,
552 data.padding.begin,
553 size_without_padding,
554 data.spacing,
555 );
556 None
557 }
558 LayoutAlignment::Center => Some((
559 data.padding.begin + (size_without_padding - pref_size - spacings) / 2 as Coord,
560 data.spacing,
561 )),
562 LayoutAlignment::Start => Some((data.padding.begin, data.spacing)),
563 LayoutAlignment::End => {
564 Some((data.padding.begin + (size_without_padding - pref_size - spacings), data.spacing))
565 }
566 LayoutAlignment::SpaceBetween => {
567 Some((data.padding.begin, (size_without_padding - pref_size) / num_spacings))
568 }
569 LayoutAlignment::SpaceAround => {
570 let spacing = (size_without_padding - pref_size) / (num_spacings + 1 as Coord);
571 Some((data.padding.begin + spacing / 2 as Coord, spacing))
572 }
573 LayoutAlignment::SpaceEvenly => {
574 let spacing = (size_without_padding - pref_size) / (num_spacings + 2 as Coord);
575 Some((data.padding.begin + spacing, spacing))
576 }
577 };
578 if let Some((mut pos, spacing)) = align {
579 for it in &mut layout_data {
580 it.pos = pos;
581 it.size = it.pref;
582 pos += spacing + it.size;
583 }
584 }
585
586 let res = result.make_mut_slice();
587
588 let mut repeat_offset =
590 res.len() / 2 - repeater_indexes.iter().skip(1).step_by(2).sum::<u32>() as usize;
591 let mut next_rep = 0;
593 let mut current_offset = 0;
595 for (idx, layout) in layout_data.iter().enumerate() {
596 let o = loop {
597 if let Some(nr) = repeater_indexes.get(next_rep * 2) {
598 let nr = *nr as usize;
599 if nr == idx {
600 for o in 0..2 {
601 res[current_offset * 2 + o] = (repeat_offset * 2 + o) as _;
602 }
603 current_offset += 1;
604 }
605 if idx >= nr {
606 if idx - nr == repeater_indexes[next_rep * 2 + 1] as usize {
607 next_rep += 1;
608 continue;
609 }
610 repeat_offset += 1;
611 break repeat_offset - 1;
612 }
613 }
614 current_offset += 1;
615 break current_offset - 1;
616 };
617 res[o * 2] = layout.pos;
618 res[o * 2 + 1] = layout.size;
619 }
620 result
621}
622
623pub fn box_layout_info(
625 cells: Slice<BoxLayoutCellData>,
626 spacing: Coord,
627 padding: &Padding,
628 alignment: LayoutAlignment,
629) -> LayoutInfo {
630 let count = cells.len();
631 let is_stretch = alignment == LayoutAlignment::Stretch;
632 if count < 1 {
633 let mut info = LayoutInfo::default();
634 info.min = padding.begin + padding.end;
635 info.preferred = info.min;
636 if is_stretch {
637 info.max = info.min;
638 }
639 return info;
640 };
641 let extra_w = padding.begin + padding.end + spacing * (count - 1) as Coord;
642 let min = cells.iter().map(|c| c.constraint.min).sum::<Coord>() + extra_w;
643 let max = if is_stretch {
644 (cells.iter().map(|c| c.constraint.max).fold(extra_w, Saturating::add)).max(min)
645 } else {
646 Coord::MAX
647 };
648 let preferred = cells.iter().map(|c| c.constraint.preferred_bounded()).sum::<Coord>() + extra_w;
649 let stretch = cells.iter().map(|c| c.constraint.stretch).sum::<f32>();
650 LayoutInfo { min, max, min_percent: 0 as _, max_percent: 100 as _, preferred, stretch }
651}
652
653pub fn box_layout_info_ortho(cells: Slice<BoxLayoutCellData>, padding: &Padding) -> LayoutInfo {
654 let extra_w = padding.begin + padding.end;
655 let mut fold =
656 cells.iter().fold(LayoutInfo { stretch: f32::MAX, ..Default::default() }, |a, b| {
657 a.merge(&b.constraint)
658 });
659 fold.max = fold.max.max(fold.min);
660 fold.preferred = fold.preferred.clamp(fold.min, fold.max);
661 fold.min += extra_w;
662 fold.max = Saturating::add(fold.max, extra_w);
663 fold.preferred += extra_w;
664 fold
665}
666
667pub fn reorder_dialog_button_layout(cells: &mut [GridLayoutCellData], roles: &[DialogButtonRole]) {
672 fn add_buttons(
673 cells: &mut [GridLayoutCellData],
674 roles: &[DialogButtonRole],
675 idx: &mut u16,
676 role: DialogButtonRole,
677 ) {
678 for (cell, r) in cells.iter_mut().zip(roles.iter()) {
679 if *r == role {
680 cell.col_or_row = *idx;
681 *idx += 1;
682 }
683 }
684 }
685
686 #[cfg(feature = "std")]
687 fn is_kde() -> bool {
688 std::env::var("XDG_CURRENT_DESKTOP")
690 .ok()
691 .and_then(|v| v.as_bytes().first().copied())
692 .is_some_and(|x| x.eq_ignore_ascii_case(&b'K'))
693 }
694 #[cfg(not(feature = "std"))]
695 let is_kde = || true;
696
697 let mut idx = 0;
698
699 if cfg!(windows) {
700 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
701 idx += 1;
702 add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
703 add_buttons(cells, roles, &mut idx, DialogButtonRole::Action);
704 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
705 add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
706 add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
707 } else if cfg!(target_os = "macos") {
708 add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
709 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
710 add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
711 add_buttons(cells, roles, &mut idx, DialogButtonRole::Action);
712 idx += 1;
713 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
714 add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
715 } else if is_kde() {
716 add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
718 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
719 idx += 1;
720 add_buttons(cells, roles, &mut idx, DialogButtonRole::Action);
721 add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
722 add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
723 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
724 } else {
725 add_buttons(cells, roles, &mut idx, DialogButtonRole::Help);
727 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reset);
728 idx += 1;
729 add_buttons(cells, roles, &mut idx, DialogButtonRole::Action);
730 add_buttons(cells, roles, &mut idx, DialogButtonRole::Apply);
731 add_buttons(cells, roles, &mut idx, DialogButtonRole::Reject);
732 add_buttons(cells, roles, &mut idx, DialogButtonRole::Accept);
733 }
734}
735
736#[cfg(feature = "ffi")]
737pub(crate) mod ffi {
738 #![allow(unsafe_code)]
739
740 use super::*;
741
742 #[unsafe(no_mangle)]
743 pub extern "C" fn slint_solve_grid_layout(
744 data: &GridLayoutData,
745 result: &mut SharedVector<Coord>,
746 ) {
747 *result = super::solve_grid_layout(data)
748 }
749
750 #[unsafe(no_mangle)]
751 pub extern "C" fn slint_grid_layout_info(
752 cells: Slice<GridLayoutCellData>,
753 spacing: Coord,
754 padding: &Padding,
755 ) -> LayoutInfo {
756 super::grid_layout_info(cells, spacing, padding)
757 }
758
759 #[unsafe(no_mangle)]
760 pub extern "C" fn slint_solve_box_layout(
761 data: &BoxLayoutData,
762 repeater_indexes: Slice<u32>,
763 result: &mut SharedVector<Coord>,
764 ) {
765 *result = super::solve_box_layout(data, repeater_indexes)
766 }
767
768 #[unsafe(no_mangle)]
769 pub extern "C" fn slint_box_layout_info(
771 cells: Slice<BoxLayoutCellData>,
772 spacing: Coord,
773 padding: &Padding,
774 alignment: LayoutAlignment,
775 ) -> LayoutInfo {
776 super::box_layout_info(cells, spacing, padding, alignment)
777 }
778
779 #[unsafe(no_mangle)]
780 pub extern "C" fn slint_box_layout_info_ortho(
782 cells: Slice<BoxLayoutCellData>,
783 padding: &Padding,
784 ) -> LayoutInfo {
785 super::box_layout_info_ortho(cells, padding)
786 }
787
788 #[unsafe(no_mangle)]
793 pub unsafe extern "C" fn slint_reorder_dialog_button_layout(
794 cells: *mut GridLayoutCellData,
795 roles: Slice<DialogButtonRole>,
796 ) {
797 reorder_dialog_button_layout(core::slice::from_raw_parts_mut(cells, roles.len()), &roles);
798 }
799}