Skip to main content

swiftui_native/views/
optionsets.rs

1//! Option sets, variadic placements, resilient statics.
2
3use super::*;
4use crate::abi;
5use crate::resolve;
6use core::ffi::c_void;
7use crate::mangle::swift_type;
8
9// ── Option sets, variadic placements, resilient statics ──
10
11/// `Optional<T>` for a resilient `T`, in a buffer ready to pass by address.
12pub(crate) unsafe fn opt_resilient_bytes(
13    payload_meta: *const c_void,
14    opt_mangled: &[u8],
15    value: Option<&[u8]>,
16) -> Vec<u8> {
17    unsafe {
18        let opt = abi::resolve_type_by_mangled_name(opt_mangled);
19        assert!(!opt.is_null(), "{}", crate::diag::demangle_type(opt_mangled));
20        let mut buf = vec![0u8; abi::type_bytes(opt).max(abi::type_bytes(payload_meta))];
21        if let Some(v) = value {
22            buf[..v.len()].copy_from_slice(v);
23        }
24        abi::store_optional_tag(&mut buf, payload_meta, value.is_some());
25        buf
26    }
27}
28
29/// A Swift `Array` of a resilient type, built from its static members.
30///
31/// A Swift variadic parameter is an `Array`, so `ToolbarPlacement...` is one
32/// word at the call site no matter how many placements it holds.
33pub(crate) unsafe fn static_array(ma: *const c_void, getters: &[&'static core::ffi::CStr]) -> u64 {
34    unsafe {
35        let meta = abi::metadata0(ma);
36        let stride = abi::type_bytes(meta);
37        let mut bytes = vec![0u8; stride * getters.len()];
38        for (i, g) in getters.iter().enumerate() {
39            let v = resilient_static(meta, g);
40            bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
41        }
42        abi::swift_array(meta, getters.len(), &bytes)
43    }
44}
45
46/// The union of an `OptionSet`'s members, as the one word it travels in.
47///
48/// SwiftUI's nested `.Set` types return from their static getters in a
49/// register, unlike its other resilient structs, which return through x8. The
50/// interface files do not say which is which — neither is marked `@frozen` —
51/// so this follows the convention the existing, parity-checked bindings
52/// established for `VerticalEdge.Set` and `Axis.Set`.
53pub(crate) unsafe fn option_set_word(ma: *const c_void, getters: &[&'static core::ffi::CStr]) -> u64 {
54    unsafe {
55        let meta = abi::metadata0(ma);
56        let mut acc = 0u64;
57        for g in getters {
58            let f = resolve::sym(g);
59            assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(g));
60            let (w, _) = static_getter(f, meta);
61            acc |= w;
62        }
63        acc
64    }
65}
66
67swift_statics! {
68    /// `SwiftUI.GestureMask` — which gestures still get a look in.
69    ///
70    /// The `including:` argument of `.gesture(_:including:)`. `All` is
71    /// SwiftUI's default and what this crate passed as a bare `3` before the
72    /// raw values were read from the framework.
73    pub enum GestureMask {
74        /// Neither this gesture nor the subviews' run.
75        None = c"$s7SwiftUI11GestureMaskV4noneACvgZ",
76        /// Only this gesture; the subviews' are disabled.
77        Gesture = c"$s7SwiftUI11GestureMaskV7gestureACvgZ",
78        /// Only the subviews'; this one is disabled.
79        Subviews = c"$s7SwiftUI11GestureMaskV8subviewsACvgZ",
80        /// Both, which is the default.
81        All = c"$s7SwiftUI11GestureMaskV3allACvgZ",
82    }
83}
84
85swift_statics! {
86    /// A member of `SwiftUI.ToolbarPlacement`.
87    pub enum ToolbarPlacement {
88        Automatic = c"$s7SwiftUI16ToolbarPlacementV9automaticACvgZ",
89        WindowToolbar = c"$s7SwiftUI16ToolbarPlacementV06windowC0ACvgZ",
90        BottomBar = c"$s7SwiftUI16ToolbarPlacementV9bottomBarACvgZ",
91        /// Unavailable on macOS: SwiftUI ships a trap stub there, so
92        /// reaching this aborts with "Unavailable code reached".
93        #[cfg(not(target_os = "macos"))]
94        NavigationBar = c"$s7SwiftUI16ToolbarPlacementV13navigationBarACvgZ",
95        /// Unavailable on macOS: SwiftUI ships a trap stub there, so
96        /// reaching this aborts with "Unavailable code reached".
97        #[cfg(not(target_os = "macos"))]
98        TabBar = c"$s7SwiftUI16ToolbarPlacementV6tabBarACvgZ",
99    }
100}
101
102swift_statics! {
103    /// A member of `SwiftUI.Text.LineStyle.Pattern`.
104    pub enum LinePattern {
105        Solid = c"$s7SwiftUI4TextV9LineStyleV7PatternV5solidAGvgZ",
106        Dot = c"$s7SwiftUI4TextV9LineStyleV7PatternV3dotAGvgZ",
107        Dash = c"$s7SwiftUI4TextV9LineStyleV7PatternV4dashAGvgZ",
108        DashDot = c"$s7SwiftUI4TextV9LineStyleV7PatternV7dashDotAGvgZ",
109        DashDotDot = c"$s7SwiftUI4TextV9LineStyleV7PatternV07dashDotH0AGvgZ",
110    }
111}
112
113swift_statics! {
114    /// A member of `SwiftUI.SymbolRenderingMode`.
115    pub enum SymbolRenderingMode {
116        Monochrome = c"$s7SwiftUI19SymbolRenderingModeV10monochromeACvgZ",
117        Multicolor = c"$s7SwiftUI19SymbolRenderingModeV10multicolorACvgZ",
118        Hierarchical = c"$s7SwiftUI19SymbolRenderingModeV12hierarchicalACvgZ",
119        Palette = c"$s7SwiftUI19SymbolRenderingModeV7paletteACvgZ",
120    }
121}
122
123swift_statics! {
124    /// A member of `SwiftUI.Font.Width`.
125    pub enum FontWidth {
126        Compressed = c"$s7SwiftUI4FontV5WidthV10compressedAEvgZ",
127        Condensed = c"$s7SwiftUI4FontV5WidthV9condensedAEvgZ",
128        Standard = c"$s7SwiftUI4FontV5WidthV8standardAEvgZ",
129        Expanded = c"$s7SwiftUI4FontV5WidthV8expandedAEvgZ",
130    }
131}
132
133swift_statics! {
134    /// `SwiftUI.Text.Scale`.
135    pub enum TextScale {
136        Default = c"$s7SwiftUI4TextV5ScaleV7defaultAEvgZ",
137        Secondary = c"$s7SwiftUI4TextV5ScaleV9secondaryAEvgZ",
138    }
139}
140
141swift_statics! {
142    /// `SwiftUI.ScrollEdgeEffectStyle`.
143    pub enum ScrollEdgeEffectStyle {
144        Automatic = c"$s7SwiftUI21ScrollEdgeEffectStyleV9automaticACvgZ",
145        Soft = c"$s7SwiftUI21ScrollEdgeEffectStyleV4softACvgZ",
146        Hard = c"$s7SwiftUI21ScrollEdgeEffectStyleV4hardACvgZ",
147    }
148}
149
150swift_statics! {
151    /// `SwiftUI.Alignment`, a frozen pair of alignment guides.
152    pub enum Alignment {
153        Center = c"$s7SwiftUI9AlignmentV6centerACvgZ",
154        Leading = c"$s7SwiftUI9AlignmentV7leadingACvgZ",
155        Trailing = c"$s7SwiftUI9AlignmentV8trailingACvgZ",
156        Top = c"$s7SwiftUI9AlignmentV3topACvgZ",
157        Bottom = c"$s7SwiftUI9AlignmentV6bottomACvgZ",
158        TopLeading = c"$s7SwiftUI9AlignmentV10topLeadingACvgZ",
159        TopTrailing = c"$s7SwiftUI9AlignmentV11topTrailingACvgZ",
160        BottomLeading = c"$s7SwiftUI9AlignmentV13bottomLeadingACvgZ",
161        BottomTrailing = c"$s7SwiftUI9AlignmentV14bottomTrailingACvgZ",
162    }
163}
164
165impl Alignment {
166    /// The two words an `Alignment` occupies: its horizontal and vertical guides.
167    ///
168    /// Returned in x0 and x1 rather than through x8. Reading it the other way
169    /// leaves both alignment keys zero — a value SwiftUI accepts, stores, and
170    /// then traps on at render time rather than at construction.
171    pub(crate) unsafe fn words(self) -> [u64; 2] {
172        unsafe {
173            let f = resolve::sym(self.getter());
174            assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(self.getter()));
175            let meta = abi::metadata0(resolve::alignment_ma());
176            let r = abi::static_regs(f, meta);
177            [r[0], r[1]]
178        }
179    }
180}
181
182/// Apply `.listRowSeparatorTint(_:edges:)`.
183pub fn list_row_separator_tint(
184    view: &ViewHandle,
185    color: Option<crate::Color>,
186    edges: &[VerticalEdgeSet],
187) -> ViewHandle {
188    unsafe {
189        let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
190        let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
191        let e = option_set_word(resolve::vertical_edge_set_ma(), &g);
192        opaque_modifier(
193            resolve::list_row_separator_tint_fn(),
194            resolve::list_row_separator_tint_oret(),
195            view,
196            &[c, e],
197            [0.0; 4],
198        )
199    }
200}
201
202/// Apply `.listSectionSeparatorTint(_:edges:)`.
203pub fn list_section_separator_tint(
204    view: &ViewHandle,
205    color: Option<crate::Color>,
206    edges: &[VerticalEdgeSet],
207) -> ViewHandle {
208    unsafe {
209        let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
210        let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
211        let e = option_set_word(resolve::vertical_edge_set_ma(), &g);
212        opaque_modifier(
213            resolve::list_section_separator_tint_fn(),
214            resolve::list_section_separator_tint_oret(),
215            view,
216            &[c, e],
217            [0.0; 4],
218        )
219    }
220}
221
222/// Apply `.strikethrough(_:pattern:color:)`.
223pub fn strikethrough(
224    view: &ViewHandle,
225    active: bool,
226    pattern: LinePattern,
227    color: Option<crate::Color>,
228) -> ViewHandle {
229    unsafe {
230        let meta = abi::metadata0(resolve::line_pattern_ma());
231        let p = resilient_static(meta, pattern.getter());
232        let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
233        opaque_modifier(
234            resolve::strikethrough_fn(),
235            resolve::strikethrough_oret(),
236            view,
237            &[u64::from(active), p.as_ptr() as u64, c],
238            [0.0; 4],
239        )
240    }
241}
242
243/// Apply `.underline(_:pattern:color:)`.
244pub fn underline(
245    view: &ViewHandle,
246    active: bool,
247    pattern: LinePattern,
248    color: Option<crate::Color>,
249) -> ViewHandle {
250    unsafe {
251        let meta = abi::metadata0(resolve::line_pattern_ma());
252        let p = resilient_static(meta, pattern.getter());
253        let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
254        opaque_modifier(
255            resolve::underline_fn(),
256            resolve::underline_oret(),
257            view,
258            &[u64::from(active), p.as_ptr() as u64, c],
259            [0.0; 4],
260        )
261    }
262}
263
264/// The `Visibility` case and the placement array these four share.
265pub(crate) unsafe fn toolbar_args(visibility: Visibility, placements: &[ToolbarPlacement]) -> (u64, u64) {
266    unsafe {
267        // `Visibility` travels *in* a register here. Handing over its address
268        // instead is not a crash: SwiftUI stores the pointer as if it were a
269        // case number and `dump` reports `Visibility.()`, a case that does not
270        // exist.
271        let meta = abi::metadata0(resolve::visibility_ma());
272        let v = abi::enum_argument(meta, visibility as u32);
273        let g: Vec<&core::ffi::CStr> = placements.iter().map(|p| p.getter()).collect();
274        (v, static_array(resolve::toolbar_placement_ma(), &g))
275    }
276}
277
278/// Apply `.toolbarVisibility(_:for:)`.
279pub fn toolbar_visibility(
280    view: &ViewHandle,
281    visibility: Visibility,
282    placements: &[ToolbarPlacement],
283) -> ViewHandle {
284    unsafe {
285        let (v, arr) = toolbar_args(visibility, placements);
286        opaque_modifier(
287            resolve::toolbar_visibility_fn(),
288            resolve::toolbar_visibility_oret(),
289            view,
290            &[v, arr],
291            [0.0; 4],
292        )
293    }
294}
295
296/// Apply `.toolbarBackgroundVisibility(_:for:)`.
297pub fn toolbar_background_visibility(
298    view: &ViewHandle,
299    visibility: Visibility,
300    placements: &[ToolbarPlacement],
301) -> ViewHandle {
302    unsafe {
303        let (v, arr) = toolbar_args(visibility, placements);
304        opaque_modifier(
305            resolve::toolbar_background_visibility_fn(),
306            resolve::toolbar_background_visibility_oret(),
307            view,
308            &[v, arr],
309            [0.0; 4],
310        )
311    }
312}
313
314/// Apply `.toolbarBackground(_:in:)`.
315pub fn toolbar_background(
316    view: &ViewHandle,
317    visibility: Visibility,
318    placements: &[ToolbarPlacement],
319) -> ViewHandle {
320    unsafe {
321        let (v, arr) = toolbar_args(visibility, placements);
322        opaque_modifier(
323            resolve::toolbar_background_fn(),
324            resolve::toolbar_background_oret(),
325            view,
326            &[v, arr],
327            [0.0; 4],
328        )
329    }
330}
331
332/// Apply `.toolbarColorScheme(_:in:)`.
333pub fn toolbar_color_scheme(
334    view: &ViewHandle,
335    scheme: Option<ColorScheme>,
336    placements: &[ToolbarPlacement],
337) -> ViewHandle {
338    unsafe {
339        let s = opt_enum_bytes(
340            resolve::color_scheme_ma(),
341            swift_type!(opt enum SwiftUI.ColorScheme).as_bytes(),
342            scheme.map(|s| s as u32),
343        );
344        let g: Vec<&core::ffi::CStr> = placements.iter().map(|p| p.getter()).collect();
345        let arr = static_array(resolve::toolbar_placement_ma(), &g);
346        opaque_modifier(
347            resolve::toolbar_color_scheme_fn(),
348            resolve::toolbar_color_scheme_oret(),
349            view,
350            &[s.as_ptr() as u64, arr],
351            [0.0; 4],
352        )
353    }
354}
355
356/// Apply `.containerRelativeFrame(_:alignment:)`.
357pub fn container_relative_frame(
358    view: &ViewHandle,
359    axes: &[AxisSet],
360    alignment: Alignment,
361) -> ViewHandle {
362    unsafe {
363        let g: Vec<&core::ffi::CStr> = axes.iter().map(|a| a.getter()).collect();
364        let a = option_set_word(resolve::axis_set_ma(), &g);
365        let al = alignment.words();
366        opaque_modifier(
367            resolve::container_relative_frame_fn(),
368            resolve::container_relative_frame_oret(),
369            view,
370            &[a, al[0], al[1]],
371            [0.0; 4],
372        )
373    }
374}
375
376/// Apply `.symbolRenderingMode(_:)`.
377pub fn symbol_rendering_mode(view: &ViewHandle, mode: Option<SymbolRenderingMode>) -> ViewHandle {
378    unsafe {
379        let meta = abi::metadata0(resolve::symbol_rendering_mode_ma());
380        let v = mode.map(|m| resilient_static(meta, m.getter()));
381        let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.SymbolRenderingMode).as_bytes(), v.as_deref());
382        modifier_indirect(
383            resolve::symbol_rendering_mode_fn(),
384            resolve::symbol_rendering_mode_oret(),
385            view,
386            &b,
387        )
388    }
389}
390
391/// Apply `.fontWidth(_:)`.
392pub fn font_width(view: &ViewHandle, width: Option<FontWidth>) -> ViewHandle {
393    unsafe {
394        let meta = abi::metadata0(resolve::font_width_ma());
395        let v = width.map(|w| resilient_static(meta, w.getter()));
396        let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.Font.Width).as_bytes(), v.as_deref());
397        modifier_indirect(resolve::font_width_fn(), resolve::font_width_oret(), view, &b)
398    }
399}
400
401/// Apply `.listItemTint(_:)` with the monochrome tint or none.
402pub fn list_item_tint_monochrome(view: &ViewHandle, tinted: bool) -> ViewHandle {
403    unsafe {
404        let meta = abi::metadata0(resolve::list_item_tint_ma());
405        let v = tinted.then(|| resilient_static(meta, c"$s7SwiftUI12ListItemTintV10monochromeACvgZ"));
406        let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ListItemTint).as_bytes(), v.as_deref());
407        modifier_indirect(resolve::list_item_tint_fn(), resolve::list_item_tint_oret(), view, &b)
408    }
409}
410
411/// Which `ListItemTint` a row uses.
412///
413/// The two colour cases are static *functions*, not `static var`s, which is
414/// why only `.monochrome` appears in the statics report — there is nothing
415/// for a plain getter to return when the case takes an argument.
416// `Color` is not `PartialEq` here, so neither is this — the colour cases
417// carry one and comparing tints is not something the crate needs.
418#[derive(Clone, Copy, Debug)]
419pub enum ListItemTint {
420    /// Tint with the list's own monochrome treatment.
421    Monochrome,
422    /// This colour, always.
423    Fixed(crate::Color),
424    /// This colour unless the user's accent colour overrides it.
425    Preferred(crate::Color),
426}
427
428/// Apply `.listItemTint(_:)`.
429///
430/// `None` clears the tint, which is the `nil` the modifier's `ListItemTint?`
431/// parameter takes.
432pub fn list_item_tint(view: &ViewHandle, tint: Option<ListItemTint>) -> ViewHandle {
433    unsafe {
434        let meta = abi::metadata0(resolve::list_item_tint_ma());
435        let v = tint.map(|t| match t {
436            ListItemTint::Monochrome => {
437                resilient_static(meta, c"$s7SwiftUI12ListItemTintV10monochromeACvgZ")
438            }
439            ListItemTint::Fixed(c) => list_item_tint_of(resolve::list_item_tint_fixed(), meta, c),
440            ListItemTint::Preferred(c) => {
441                list_item_tint_of(resolve::list_item_tint_preferred(), meta, c)
442            }
443        });
444        let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ListItemTint).as_bytes(), v.as_deref());
445        modifier_indirect(resolve::list_item_tint_fn(), resolve::list_item_tint_oret(), view, &b)
446    }
447}
448
449/// Call `ListItemTint.fixed(_:)` or `.preferred(_:)`.
450///
451/// `(sret, ptr)` — the `Color` is a single reference, so it travels in x0 by
452/// value rather than by address; the tint is resilient and comes back through
453/// x8.
454///
455/// # Safety
456/// `f` must be one of those two statics and `meta` `ListItemTint`'s metadata.
457unsafe fn list_item_tint_of(
458    f: *const c_void,
459    meta: *const c_void,
460    color: crate::Color,
461) -> Vec<u8> {
462    unsafe {
463        let word = super::color_value(color.r, color.g, color.b, color.a);
464        let mut out = vec![0u8; abi::type_bytes(meta)];
465        core::arch::asm!("blr {f}", f = in(reg) f,
466            in("x0") word,
467            in("x8") out.as_mut_ptr(),
468            lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
469            lateout("x5") _, lateout("x6") _, lateout("x7") _,
470            lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
471            lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
472            lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
473            clobber_abi("C"),
474        );
475        out
476    }
477}
478
479/// Apply `.typeSelectEquivalent(_:)`.
480pub fn type_select_equivalent(view: &ViewHandle, text: Option<&str>) -> ViewHandle {
481    unsafe {
482        let payload = resolve::lsk_meta();
483        let key = text.map(|t| {
484            let (k0, k1, k2, k3) = lsk_words(t);
485            let mut b = [0u8; 32];
486            b[..8].copy_from_slice(&k0.to_le_bytes());
487            b[8..16].copy_from_slice(&k1.to_le_bytes());
488            b[16..24].copy_from_slice(&k2.to_le_bytes());
489            b[24..].copy_from_slice(&k3.to_le_bytes());
490            b
491        });
492        // Four words in four registers, not by address. A
493        // `LocalizedStringKey` already travels that way everywhere else in this
494        // file, and thirty-two bytes is exactly the four-register limit, so its
495        // optional travels the same way.
496        let opt = crate::type_by_name!(swift_type!(opt SwiftUI.LocalizedStringKey).as_bytes());
497        let w = optional_words(payload, opt, key.as_ref().map_or(&[][..], |k| &k[..]), key.is_some());
498        opaque_modifier(
499            resolve::type_select_equivalent_fn(),
500            resolve::type_select_equivalent_oret(),
501            view,
502            &w,
503            [0.0; 4],
504        )
505    }
506}
507
508swift_cases! {
509    /// `SwiftUI.PreviewLayout`'s cases without payloads.
510    ///
511    /// The tags are not the declaration order. Swift numbers an enum's *payload*
512    /// cases first, and `PreviewLayout` has two of them — `fixed` and `fixed3D` —
513    /// so the empty cases start at two. Guessing zero and one lands on `fixed3D`
514    /// with a zeroed payload, which dumps as a real case and renders as nothing.
515    pub enum PreviewLayout {
516        Device = 2,
517        SizeThatFits = 3,
518    }
519}
520
521/// Apply `.previewLayout(_:)`.
522pub fn preview_layout(view: &ViewHandle, layout: PreviewLayout) -> ViewHandle {
523    unsafe {
524        let b = enum_bytes(resolve::preview_layout_ma(), layout.tag());
525        modifier_indirect(resolve::preview_layout_fn(), resolve::preview_layout_oret(), view, &b)
526    }
527}
528
529/// Apply `.textScale(_:isEnabled:)`.
530pub fn text_scale(view: &ViewHandle, scale: TextScale, enabled: bool) -> ViewHandle {
531    unsafe {
532        let s = resilient_static(abi::metadata0(resolve::text_scale_ma()), scale.getter());
533        opaque_modifier(
534            resolve::text_scale_fn(),
535            resolve::text_scale_oret(),
536            view,
537            &[s.as_ptr() as u64, u64::from(enabled)],
538            [0.0; 4],
539        )
540    }
541}
542
543/// Apply `.scrollEdgeEffectStyle(_:for:)`.
544pub fn scroll_edge_effect_style(
545    view: &ViewHandle,
546    style: Option<ScrollEdgeEffectStyle>,
547    edges: &[EdgeSetMember],
548) -> ViewHandle {
549    unsafe {
550        let meta = abi::metadata0(resolve::scroll_edge_effect_style_ma());
551        let v = style.map(|s| resilient_static(meta, s.getter()));
552        let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ScrollEdgeEffectStyle).as_bytes(), v.as_deref());
553        let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
554        let e = option_set_word(resolve::edge_set_ma(), &g);
555        opaque_modifier(
556            resolve::scroll_edge_effect_style_fn(),
557            resolve::scroll_edge_effect_style_oret(),
558            view,
559            &[b.as_ptr() as u64, e],
560            [0.0; 4],
561        )
562    }
563}
564
565swift_statics! {
566    /// A member of `SwiftUI.Edge.Set`.
567    pub enum EdgeSetMember {
568        Top = c"$s7SwiftUI4EdgeO3SetV3topAEvgZ",
569        Leading = c"$s7SwiftUI4EdgeO3SetV7leadingAEvgZ",
570        Bottom = c"$s7SwiftUI4EdgeO3SetV6bottomAEvgZ",
571        Trailing = c"$s7SwiftUI4EdgeO3SetV8trailingAEvgZ",
572        All = c"$s7SwiftUI4EdgeO3SetV3allAEvgZ",
573        Horizontal = c"$s7SwiftUI4EdgeO3SetV10horizontalAEvgZ",
574        Vertical = c"$s7SwiftUI4EdgeO3SetV8verticalAEvgZ",
575    }
576}
577
578crate::thunk::runtime_thunk! {
579    /// A `(CGFloat, Axis) -> CGFloat` closure, as `containerRelativeFrame` wants.
580    ///
581    /// Swift hands a closure its context in x20 and its arguments in the
582    /// ordinary registers — here the length in d0 and the axis in x0. Moving
583    /// x20 into x1 makes the whole thing addressable from a plain
584    /// `extern "C" fn(u64, *mut c_void, f64) -> f64`: integers in x0 and x1,
585    /// the float in d0, the result in d0. It is the only closure shape in this
586    /// crate that returns a value rather than writing one through x8.
587    fn container_length_thunk = "rswift_container_length" via RSWIFT_CONTAINER_LENGTH {
588        prologue: ["mov x1, x20"],
589        branch: "br",
590        epilogue: [],
591    }
592}
593
594/// The body behind [`container_length_thunk`]: a third of the length down the
595/// vertical axis, a fifth across.
596///
597/// `Axis` is frozen — `.horizontal` is 0 and `.vertical` is 1.
598unsafe extern "C" fn container_length(axis: u64, _ctx: *mut c_void, length: f64) -> f64 {
599    if axis == 1 { length / 3.0 } else { length / 5.0 }
600}
601
602/// Apply `.containerRelativeFrame(_:alignment:_:)` with the documentation's
603/// length function.
604pub fn container_relative_frame_thirds(
605    view: &ViewHandle,
606    axes: &[AxisSet],
607    alignment: Alignment,
608) -> ViewHandle {
609    unsafe {
610        RSWIFT_CONTAINER_LENGTH = container_length as *const c_void;
611        let g: Vec<&core::ffi::CStr> = axes.iter().map(|a| a.getter()).collect();
612        let set = option_set_word(resolve::axis_set_ma(), &g);
613        let [ah, av] = alignment.words();
614        let (meta, wt, size) = super::opaque_result_type(
615            resolve::container_relative_frame_closure_oret(),
616            &[view.meta, view.wt],
617        );
618        let mut result = vec![0u8; size];
619        let this = view.value_ptr();
620        // Leaked, not a stack local. The content closures elsewhere in this
621        // crate run *during* the call that takes them, so a local outlives
622        // them; this one is stored in the modifier and called at layout time,
623        // by which point the frame that held it is long gone. The body never
624        // reads it — the closure captures nothing — but SwiftUI retains and
625        // releases it, and `ClosureCtx`'s immortal refcount makes both no-ops.
626        let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
627            noop_closure_body,
628            core::ptr::null_mut(),
629        )));
630        core::arch::asm!("blr {f}", f = in(reg) resolve::container_relative_frame_closure_fn(),
631            in("x0") set,
632            in("x1") ah, in("x2") av,
633            in("x3") container_length_thunk as *const c_void,
634            in("x4") ctx as *mut abi::ClosureCtx,
635            in("x5") view.meta,
636            in("x6") view.wt,
637            in("x8") result.as_mut_ptr(),
638            in("x20") this,
639            lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
640            lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
641            lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
642            clobber_abi("C"),
643        );
644        wrap_owned(result, meta, wt)
645    }
646}
647
648unsafe extern "C" fn noop_closure_body(_out: *mut u8, _payload: *mut c_void) {}