use super::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::swift_type;
pub(crate) unsafe fn opt_resilient_bytes(
payload_meta: *const c_void,
opt_mangled: &[u8],
value: Option<&[u8]>,
) -> Vec<u8> {
unsafe {
let opt = abi::resolve_type_by_mangled_name(opt_mangled);
assert!(!opt.is_null(), "{}", crate::diag::demangle_type(opt_mangled));
let mut buf = vec![0u8; abi::type_bytes(opt).max(abi::type_bytes(payload_meta))];
if let Some(v) = value {
buf[..v.len()].copy_from_slice(v);
}
abi::store_optional_tag(&mut buf, payload_meta, value.is_some());
buf
}
}
pub(crate) unsafe fn static_array(ma: *const c_void, getters: &[&'static core::ffi::CStr]) -> u64 {
unsafe {
let meta = abi::metadata0(ma);
let stride = abi::type_bytes(meta);
let mut bytes = vec![0u8; stride * getters.len()];
for (i, g) in getters.iter().enumerate() {
let v = resilient_static(meta, g);
bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
}
abi::swift_array(meta, getters.len(), &bytes)
}
}
pub(crate) unsafe fn option_set_word(ma: *const c_void, getters: &[&'static core::ffi::CStr]) -> u64 {
unsafe {
let meta = abi::metadata0(ma);
let mut acc = 0u64;
for g in getters {
let f = resolve::sym(g);
assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(g));
let (w, _) = static_getter(f, meta);
acc |= w;
}
acc
}
}
swift_statics! {
pub enum GestureMask {
None = c"$s7SwiftUI11GestureMaskV4noneACvgZ",
Gesture = c"$s7SwiftUI11GestureMaskV7gestureACvgZ",
Subviews = c"$s7SwiftUI11GestureMaskV8subviewsACvgZ",
All = c"$s7SwiftUI11GestureMaskV3allACvgZ",
}
}
swift_statics! {
pub enum ToolbarPlacement {
Automatic = c"$s7SwiftUI16ToolbarPlacementV9automaticACvgZ",
WindowToolbar = c"$s7SwiftUI16ToolbarPlacementV06windowC0ACvgZ",
BottomBar = c"$s7SwiftUI16ToolbarPlacementV9bottomBarACvgZ",
#[cfg(not(target_os = "macos"))]
NavigationBar = c"$s7SwiftUI16ToolbarPlacementV13navigationBarACvgZ",
#[cfg(not(target_os = "macos"))]
TabBar = c"$s7SwiftUI16ToolbarPlacementV6tabBarACvgZ",
}
}
swift_statics! {
pub enum LinePattern {
Solid = c"$s7SwiftUI4TextV9LineStyleV7PatternV5solidAGvgZ",
Dot = c"$s7SwiftUI4TextV9LineStyleV7PatternV3dotAGvgZ",
Dash = c"$s7SwiftUI4TextV9LineStyleV7PatternV4dashAGvgZ",
DashDot = c"$s7SwiftUI4TextV9LineStyleV7PatternV7dashDotAGvgZ",
DashDotDot = c"$s7SwiftUI4TextV9LineStyleV7PatternV07dashDotH0AGvgZ",
}
}
swift_statics! {
pub enum SymbolRenderingMode {
Monochrome = c"$s7SwiftUI19SymbolRenderingModeV10monochromeACvgZ",
Multicolor = c"$s7SwiftUI19SymbolRenderingModeV10multicolorACvgZ",
Hierarchical = c"$s7SwiftUI19SymbolRenderingModeV12hierarchicalACvgZ",
Palette = c"$s7SwiftUI19SymbolRenderingModeV7paletteACvgZ",
}
}
swift_statics! {
pub enum FontWidth {
Compressed = c"$s7SwiftUI4FontV5WidthV10compressedAEvgZ",
Condensed = c"$s7SwiftUI4FontV5WidthV9condensedAEvgZ",
Standard = c"$s7SwiftUI4FontV5WidthV8standardAEvgZ",
Expanded = c"$s7SwiftUI4FontV5WidthV8expandedAEvgZ",
}
}
swift_statics! {
pub enum TextScale {
Default = c"$s7SwiftUI4TextV5ScaleV7defaultAEvgZ",
Secondary = c"$s7SwiftUI4TextV5ScaleV9secondaryAEvgZ",
}
}
swift_statics! {
pub enum ScrollEdgeEffectStyle {
Automatic = c"$s7SwiftUI21ScrollEdgeEffectStyleV9automaticACvgZ",
Soft = c"$s7SwiftUI21ScrollEdgeEffectStyleV4softACvgZ",
Hard = c"$s7SwiftUI21ScrollEdgeEffectStyleV4hardACvgZ",
}
}
swift_statics! {
pub enum Alignment {
Center = c"$s7SwiftUI9AlignmentV6centerACvgZ",
Leading = c"$s7SwiftUI9AlignmentV7leadingACvgZ",
Trailing = c"$s7SwiftUI9AlignmentV8trailingACvgZ",
Top = c"$s7SwiftUI9AlignmentV3topACvgZ",
Bottom = c"$s7SwiftUI9AlignmentV6bottomACvgZ",
TopLeading = c"$s7SwiftUI9AlignmentV10topLeadingACvgZ",
TopTrailing = c"$s7SwiftUI9AlignmentV11topTrailingACvgZ",
BottomLeading = c"$s7SwiftUI9AlignmentV13bottomLeadingACvgZ",
BottomTrailing = c"$s7SwiftUI9AlignmentV14bottomTrailingACvgZ",
}
}
impl Alignment {
pub(crate) unsafe fn words(self) -> [u64; 2] {
unsafe {
let f = resolve::sym(self.getter());
assert!(!f.is_null(), "missing static: {}", crate::diag::symbol_name(self.getter()));
let meta = abi::metadata0(resolve::alignment_ma());
let r = abi::static_regs(f, meta);
[r[0], r[1]]
}
}
}
pub fn list_row_separator_tint(
view: &ViewHandle,
color: Option<crate::Color>,
edges: &[VerticalEdgeSet],
) -> ViewHandle {
unsafe {
let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
let e = option_set_word(resolve::vertical_edge_set_ma(), &g);
opaque_modifier(
resolve::list_row_separator_tint_fn(),
resolve::list_row_separator_tint_oret(),
view,
&[c, e],
[0.0; 4],
)
}
}
pub fn list_section_separator_tint(
view: &ViewHandle,
color: Option<crate::Color>,
edges: &[VerticalEdgeSet],
) -> ViewHandle {
unsafe {
let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
let e = option_set_word(resolve::vertical_edge_set_ma(), &g);
opaque_modifier(
resolve::list_section_separator_tint_fn(),
resolve::list_section_separator_tint_oret(),
view,
&[c, e],
[0.0; 4],
)
}
}
pub fn strikethrough(
view: &ViewHandle,
active: bool,
pattern: LinePattern,
color: Option<crate::Color>,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::line_pattern_ma());
let p = resilient_static(meta, pattern.getter());
let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
opaque_modifier(
resolve::strikethrough_fn(),
resolve::strikethrough_oret(),
view,
&[u64::from(active), p.as_ptr() as u64, c],
[0.0; 4],
)
}
}
pub fn underline(
view: &ViewHandle,
active: bool,
pattern: LinePattern,
color: Option<crate::Color>,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::line_pattern_ma());
let p = resilient_static(meta, pattern.getter());
let c = color.map_or(0, |c| color_value(c.r, c.g, c.b, c.a));
opaque_modifier(
resolve::underline_fn(),
resolve::underline_oret(),
view,
&[u64::from(active), p.as_ptr() as u64, c],
[0.0; 4],
)
}
}
pub(crate) unsafe fn toolbar_args(visibility: Visibility, placements: &[ToolbarPlacement]) -> (u64, u64) {
unsafe {
let meta = abi::metadata0(resolve::visibility_ma());
let v = abi::enum_argument(meta, visibility as u32);
let g: Vec<&core::ffi::CStr> = placements.iter().map(|p| p.getter()).collect();
(v, static_array(resolve::toolbar_placement_ma(), &g))
}
}
pub fn toolbar_visibility(
view: &ViewHandle,
visibility: Visibility,
placements: &[ToolbarPlacement],
) -> ViewHandle {
unsafe {
let (v, arr) = toolbar_args(visibility, placements);
opaque_modifier(
resolve::toolbar_visibility_fn(),
resolve::toolbar_visibility_oret(),
view,
&[v, arr],
[0.0; 4],
)
}
}
pub fn toolbar_background_visibility(
view: &ViewHandle,
visibility: Visibility,
placements: &[ToolbarPlacement],
) -> ViewHandle {
unsafe {
let (v, arr) = toolbar_args(visibility, placements);
opaque_modifier(
resolve::toolbar_background_visibility_fn(),
resolve::toolbar_background_visibility_oret(),
view,
&[v, arr],
[0.0; 4],
)
}
}
pub fn toolbar_background(
view: &ViewHandle,
visibility: Visibility,
placements: &[ToolbarPlacement],
) -> ViewHandle {
unsafe {
let (v, arr) = toolbar_args(visibility, placements);
opaque_modifier(
resolve::toolbar_background_fn(),
resolve::toolbar_background_oret(),
view,
&[v, arr],
[0.0; 4],
)
}
}
pub fn toolbar_color_scheme(
view: &ViewHandle,
scheme: Option<ColorScheme>,
placements: &[ToolbarPlacement],
) -> ViewHandle {
unsafe {
let s = opt_enum_bytes(
resolve::color_scheme_ma(),
swift_type!(opt enum SwiftUI.ColorScheme).as_bytes(),
scheme.map(|s| s as u32),
);
let g: Vec<&core::ffi::CStr> = placements.iter().map(|p| p.getter()).collect();
let arr = static_array(resolve::toolbar_placement_ma(), &g);
opaque_modifier(
resolve::toolbar_color_scheme_fn(),
resolve::toolbar_color_scheme_oret(),
view,
&[s.as_ptr() as u64, arr],
[0.0; 4],
)
}
}
pub fn container_relative_frame(
view: &ViewHandle,
axes: &[AxisSet],
alignment: Alignment,
) -> ViewHandle {
unsafe {
let g: Vec<&core::ffi::CStr> = axes.iter().map(|a| a.getter()).collect();
let a = option_set_word(resolve::axis_set_ma(), &g);
let al = alignment.words();
opaque_modifier(
resolve::container_relative_frame_fn(),
resolve::container_relative_frame_oret(),
view,
&[a, al[0], al[1]],
[0.0; 4],
)
}
}
pub fn symbol_rendering_mode(view: &ViewHandle, mode: Option<SymbolRenderingMode>) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::symbol_rendering_mode_ma());
let v = mode.map(|m| resilient_static(meta, m.getter()));
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.SymbolRenderingMode).as_bytes(), v.as_deref());
modifier_indirect(
resolve::symbol_rendering_mode_fn(),
resolve::symbol_rendering_mode_oret(),
view,
&b,
)
}
}
pub fn font_width(view: &ViewHandle, width: Option<FontWidth>) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::font_width_ma());
let v = width.map(|w| resilient_static(meta, w.getter()));
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.Font.Width).as_bytes(), v.as_deref());
modifier_indirect(resolve::font_width_fn(), resolve::font_width_oret(), view, &b)
}
}
pub fn list_item_tint_monochrome(view: &ViewHandle, tinted: bool) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::list_item_tint_ma());
let v = tinted.then(|| resilient_static(meta, c"$s7SwiftUI12ListItemTintV10monochromeACvgZ"));
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ListItemTint).as_bytes(), v.as_deref());
modifier_indirect(resolve::list_item_tint_fn(), resolve::list_item_tint_oret(), view, &b)
}
}
#[derive(Clone, Copy, Debug)]
pub enum ListItemTint {
Monochrome,
Fixed(crate::Color),
Preferred(crate::Color),
}
pub fn list_item_tint(view: &ViewHandle, tint: Option<ListItemTint>) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::list_item_tint_ma());
let v = tint.map(|t| match t {
ListItemTint::Monochrome => {
resilient_static(meta, c"$s7SwiftUI12ListItemTintV10monochromeACvgZ")
}
ListItemTint::Fixed(c) => list_item_tint_of(resolve::list_item_tint_fixed(), meta, c),
ListItemTint::Preferred(c) => {
list_item_tint_of(resolve::list_item_tint_preferred(), meta, c)
}
});
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ListItemTint).as_bytes(), v.as_deref());
modifier_indirect(resolve::list_item_tint_fn(), resolve::list_item_tint_oret(), view, &b)
}
}
unsafe fn list_item_tint_of(
f: *const c_void,
meta: *const c_void,
color: crate::Color,
) -> Vec<u8> {
unsafe {
let word = super::color_value(color.r, color.g, color.b, color.a);
let mut out = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) f,
in("x0") word,
in("x8") out.as_mut_ptr(),
lateout("x1") _, lateout("x2") _, lateout("x3") _, lateout("x4") _,
lateout("x5") _, lateout("x6") _, lateout("x7") _,
lateout("x9") _, lateout("x10") _, lateout("x11") _, lateout("x12") _,
lateout("x13") _, lateout("x14") _, lateout("x15") _, lateout("x16") _,
lateout("x17") _, lateout("lr") _, lateout("x20") _, lateout("x21") _,
clobber_abi("C"),
);
out
}
}
pub fn type_select_equivalent(view: &ViewHandle, text: Option<&str>) -> ViewHandle {
unsafe {
let payload = resolve::lsk_meta();
let key = text.map(|t| {
let (k0, k1, k2, k3) = lsk_words(t);
let mut b = [0u8; 32];
b[..8].copy_from_slice(&k0.to_le_bytes());
b[8..16].copy_from_slice(&k1.to_le_bytes());
b[16..24].copy_from_slice(&k2.to_le_bytes());
b[24..].copy_from_slice(&k3.to_le_bytes());
b
});
let opt = crate::type_by_name!(swift_type!(opt SwiftUI.LocalizedStringKey).as_bytes());
let w = optional_words(payload, opt, key.as_ref().map_or(&[][..], |k| &k[..]), key.is_some());
opaque_modifier(
resolve::type_select_equivalent_fn(),
resolve::type_select_equivalent_oret(),
view,
&w,
[0.0; 4],
)
}
}
swift_cases! {
pub enum PreviewLayout {
Device = 2,
SizeThatFits = 3,
}
}
pub fn preview_layout(view: &ViewHandle, layout: PreviewLayout) -> ViewHandle {
unsafe {
let b = enum_bytes(resolve::preview_layout_ma(), layout.tag());
modifier_indirect(resolve::preview_layout_fn(), resolve::preview_layout_oret(), view, &b)
}
}
pub fn text_scale(view: &ViewHandle, scale: TextScale, enabled: bool) -> ViewHandle {
unsafe {
let s = resilient_static(abi::metadata0(resolve::text_scale_ma()), scale.getter());
opaque_modifier(
resolve::text_scale_fn(),
resolve::text_scale_oret(),
view,
&[s.as_ptr() as u64, u64::from(enabled)],
[0.0; 4],
)
}
}
pub fn scroll_edge_effect_style(
view: &ViewHandle,
style: Option<ScrollEdgeEffectStyle>,
edges: &[EdgeSetMember],
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::scroll_edge_effect_style_ma());
let v = style.map(|s| resilient_static(meta, s.getter()));
let b = opt_resilient_bytes(meta, swift_type!(opt SwiftUI.ScrollEdgeEffectStyle).as_bytes(), v.as_deref());
let g: Vec<&core::ffi::CStr> = edges.iter().map(|e| e.getter()).collect();
let e = option_set_word(resolve::edge_set_ma(), &g);
opaque_modifier(
resolve::scroll_edge_effect_style_fn(),
resolve::scroll_edge_effect_style_oret(),
view,
&[b.as_ptr() as u64, e],
[0.0; 4],
)
}
}
swift_statics! {
pub enum EdgeSetMember {
Top = c"$s7SwiftUI4EdgeO3SetV3topAEvgZ",
Leading = c"$s7SwiftUI4EdgeO3SetV7leadingAEvgZ",
Bottom = c"$s7SwiftUI4EdgeO3SetV6bottomAEvgZ",
Trailing = c"$s7SwiftUI4EdgeO3SetV8trailingAEvgZ",
All = c"$s7SwiftUI4EdgeO3SetV3allAEvgZ",
Horizontal = c"$s7SwiftUI4EdgeO3SetV10horizontalAEvgZ",
Vertical = c"$s7SwiftUI4EdgeO3SetV8verticalAEvgZ",
}
}
crate::thunk::runtime_thunk! {
fn container_length_thunk = "rswift_container_length" via RSWIFT_CONTAINER_LENGTH {
prologue: ["mov x1, x20"],
branch: "br",
epilogue: [],
}
}
unsafe extern "C" fn container_length(axis: u64, _ctx: *mut c_void, length: f64) -> f64 {
if axis == 1 { length / 3.0 } else { length / 5.0 }
}
pub fn container_relative_frame_thirds(
view: &ViewHandle,
axes: &[AxisSet],
alignment: Alignment,
) -> ViewHandle {
unsafe {
RSWIFT_CONTAINER_LENGTH = container_length as *const c_void;
let g: Vec<&core::ffi::CStr> = axes.iter().map(|a| a.getter()).collect();
let set = option_set_word(resolve::axis_set_ma(), &g);
let [ah, av] = alignment.words();
let (meta, wt, size) = super::opaque_result_type(
resolve::container_relative_frame_closure_oret(),
&[view.meta, view.wt],
);
let mut result = vec![0u8; size];
let this = view.value_ptr();
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(
noop_closure_body,
core::ptr::null_mut(),
)));
core::arch::asm!("blr {f}", f = in(reg) resolve::container_relative_frame_closure_fn(),
in("x0") set,
in("x1") ah, in("x2") av,
in("x3") container_length_thunk as *const c_void,
in("x4") ctx as *mut abi::ClosureCtx,
in("x5") view.meta,
in("x6") view.wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
lateout("x7") _, lateout("x9") _, lateout("x10") _, lateout("x11") _,
lateout("x12") _, lateout("x13") _, lateout("x14") _, lateout("x15") _,
lateout("x16") _, lateout("x17") _, lateout("lr") _, lateout("x21") _,
clobber_abi("C"),
);
wrap_owned(result, meta, wt)
}
}
unsafe extern "C" fn noop_closure_body(_out: *mut u8, _payload: *mut c_void) {}