use super::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::{self, swift_type};
pub(crate) unsafe fn modifier_indirect(
f: *const c_void,
o: *const c_void,
v: &ViewHandle,
bytes: &[u8],
) -> ViewHandle {
unsafe { opaque_modifier(f, o, v, &[bytes.as_ptr() as u64], [0.0; 4]) }
}
pub(crate) unsafe fn enum_bytes(ma: *const c_void, tag: u32) -> Vec<u8> {
unsafe {
let meta = abi::metadata0(ma);
let mut buf = vec![0u8; abi::type_bytes(meta)];
abi::inject_enum_tag(&mut buf, meta, tag);
buf
}
}
pub(crate) unsafe fn opt_enum_bytes(ma: *const c_void, opt_mangled: &[u8], tag: Option<u32>) -> Vec<u8> {
unsafe {
let payload = abi::metadata0(ma);
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)];
if let Some(t) = tag {
abi::inject_enum_tag(&mut buf[..abi::type_bytes(payload)], payload, t);
}
abi::store_optional_tag(&mut buf, payload, tag.is_some());
buf
}
}
pub(crate) unsafe fn optional_words(
payload_meta: *const c_void,
opt_meta: *const c_void,
bytes: &[u8],
is_some: bool,
) -> Vec<u64> {
unsafe {
let stride = abi::type_bytes(opt_meta);
let mut buf = vec![0u8; stride.max(8)];
if is_some {
buf[..bytes.len()].copy_from_slice(bytes);
}
abi::store_optional_tag(&mut buf, payload_meta, is_some);
let words = abi::type_size(opt_meta).div_ceil(8);
(0..words)
.map(|i| u64::from_le_bytes(buf[i * 8..i * 8 + 8].try_into().expect("word")))
.collect()
}
}
pub(crate) unsafe fn opt_f64_words(v: Option<f64>) -> Vec<u64> {
unsafe {
let payload = resolve::double_meta();
let opt = abi::resolve_type_by_mangled_name(mangle::optional(mangle::DOUBLE).as_bytes());
optional_words(payload, opt, &v.unwrap_or(0.0).to_le_bytes(), v.is_some())
}
}
pub(crate) unsafe fn opt_unit_point_words(v: Option<(f64, f64)>) -> Vec<u64> {
unsafe {
let payload = crate::type_by_name!(swift_type!(SwiftUI.UnitPoint).as_bytes());
let opt = crate::type_by_name!(swift_type!(opt SwiftUI.UnitPoint).as_bytes());
let (x, y) = v.unwrap_or((0.0, 0.0));
let mut bytes = [0u8; 16];
bytes[..8].copy_from_slice(&x.to_le_bytes());
bytes[8..].copy_from_slice(&y.to_le_bytes());
optional_words(payload, opt, &bytes, v.is_some())
}
}
pub(crate) unsafe fn opt_string_words(v: Option<&str>) -> [u64; 2] {
unsafe {
let payload = resolve::string_meta();
let mut buf = [0u8; 16];
if let Some(s) = v {
buf.copy_from_slice(&abi::swift_string(s));
}
abi::store_optional_tag(&mut buf, payload, v.is_some());
[
u64::from_le_bytes(buf[..8].try_into().expect("string lo")),
u64::from_le_bytes(buf[8..].try_into().expect("string hi")),
]
}
}
swift_cases! {
pub enum ImageScale {
Small = 0,
Medium = 1,
Large = 2,
}
}
swift_cases! {
pub enum TruncationMode {
Head = 0,
Tail = 1,
Middle = 2,
}
}
swift_cases! {
pub enum TextCase {
Uppercase = 0,
Lowercase = 1,
}
}
swift_statics! {
pub enum HorizontalAlignment {
Leading = c"$s7SwiftUI19HorizontalAlignmentV7leadingACvgZ",
Center = c"$s7SwiftUI19HorizontalAlignmentV6centerACvgZ",
Trailing = c"$s7SwiftUI19HorizontalAlignmentV8trailingACvgZ",
ListRowSeparatorLeading = c"$s7SwiftUI19HorizontalAlignmentV23listRowSeparatorLeadingACvgZ",
ListRowSeparatorTrailing = c"$s7SwiftUI19HorizontalAlignmentV24listRowSeparatorTrailingACvgZ",
}
}
swift_statics! {
pub enum VerticalAlignment {
Top = c"$s7SwiftUI17VerticalAlignmentV3topACvgZ",
Center = c"$s7SwiftUI17VerticalAlignmentV6centerACvgZ",
Bottom = c"$s7SwiftUI17VerticalAlignmentV6bottomACvgZ",
FirstTextBaseline = c"$s7SwiftUI17VerticalAlignmentV17firstTextBaselineACvgZ",
LastTextBaseline = c"$s7SwiftUI17VerticalAlignmentV16lastTextBaselineACvgZ",
}
}
swift_statics! {
pub enum ScrollAnchorRole {
Alignment = c"$s7SwiftUI16ScrollAnchorRoleV9alignmentACvgZ",
SizeChanges = c"$s7SwiftUI16ScrollAnchorRoleV11sizeChangesACvgZ",
InitialOffset = c"$s7SwiftUI16ScrollAnchorRoleV13initialOffsetACvgZ",
}
}
pub fn accent_color(view: &ViewHandle, color: Option<crate::Color>) -> ViewHandle {
unsafe {
modifier_opt_color(resolve::accent_color_fn(), resolve::accent_color_oret(), view, color)
}
}
pub fn foreground_color_value(view: &ViewHandle, color: u64) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::foreground_color_fn(),
resolve::foreground_color_oret(),
view,
&[color],
[0.0; 4],
)
}
}
pub fn foreground_color(view: &ViewHandle, color: Option<crate::Color>) -> ViewHandle {
unsafe {
modifier_opt_color(
resolve::foreground_color_fn(),
resolve::foreground_color_oret(),
view,
color,
)
}
}
pub fn color_multiply(view: &ViewHandle, color: crate::Color) -> ViewHandle {
unsafe {
let word = color_value(color.r, color.g, color.b, color.a);
opaque_modifier(
resolve::color_multiply_fn(),
resolve::color_multiply_oret(),
view,
&[word],
[0.0; 4],
)
}
}
pub fn allows_window_activation_events(view: &ViewHandle, allows: Option<bool>) -> ViewHandle {
unsafe {
modifier_opt_bool(
resolve::allows_window_activation_fn(),
resolve::allows_window_activation_oret(),
view,
allows,
)
}
}
pub fn dialog_prevents_app_termination(view: &ViewHandle, prevents: Option<bool>) -> ViewHandle {
unsafe {
modifier_opt_bool(
resolve::dialog_prevents_termination_fn(),
resolve::dialog_prevents_termination_oret(),
view,
prevents,
)
}
}
pub fn disable_autocorrection(view: &ViewHandle, disable: Option<bool>) -> ViewHandle {
unsafe {
modifier_opt_bool(
resolve::disable_autocorrection_fn(),
resolve::disable_autocorrection_oret(),
view,
disable,
)
}
}
pub fn presentation_prevents_app_termination(
view: &ViewHandle,
prevents: Option<bool>,
) -> ViewHandle {
unsafe {
modifier_opt_bool(
resolve::presentation_prevents_termination_fn(),
resolve::presentation_prevents_termination_oret(),
view,
prevents,
)
}
}
pub fn preview_display_name(view: &ViewHandle, name: Option<&str>) -> ViewHandle {
unsafe {
let w = opt_string_words(name);
opaque_modifier(
resolve::preview_display_name_fn(),
resolve::preview_display_name_oret(),
view,
&w,
[0.0; 4],
)
}
}
pub fn hue_rotation(view: &ViewHandle, radians: f64) -> ViewHandle {
unsafe {
modifier_f64(resolve::hue_rotation_fn(), resolve::hue_rotation_oret(), view, radians)
}
}
pub fn grid_cell_anchor(view: &ViewHandle, x: f64, y: f64) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::grid_cell_anchor_fn(),
resolve::grid_cell_anchor_oret(),
view,
&[],
[x, y, 0.0, 0.0],
)
}
}
pub fn presentation_corner_radius(view: &ViewHandle, radius: Option<f64>) -> ViewHandle {
unsafe {
let w = opt_f64_words(radius);
opaque_modifier(
resolve::presentation_corner_radius_fn(),
resolve::presentation_corner_radius_oret(),
view,
&w,
[0.0; 4],
)
}
}
pub fn image_scale(view: &ViewHandle, scale: ImageScale) -> ViewHandle {
unsafe {
let b = enum_bytes(resolve::image_scale_ma(), scale.tag());
modifier_indirect(resolve::image_scale_fn(), resolve::image_scale_oret(), view, &b)
}
}
pub fn truncation_mode(view: &ViewHandle, mode: TruncationMode) -> ViewHandle {
unsafe {
let b = enum_bytes(resolve::truncation_mode_ma(), mode.tag());
modifier_indirect(resolve::truncation_mode_fn(), resolve::truncation_mode_oret(), view, &b)
}
}
pub fn text_case(view: &ViewHandle, case: Option<TextCase>) -> ViewHandle {
unsafe {
let b = opt_enum_bytes(
resolve::text_case_ma(),
swift_type!(opt enum SwiftUI.Text.Case).as_bytes(),
case.map(TextCase::tag),
);
modifier_indirect(resolve::text_case_fn(), resolve::text_case_oret(), view, &b)
}
}
pub fn grid_column_alignment(view: &ViewHandle, alignment: HorizontalAlignment) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::horizontal_alignment_ma());
let a = frozen_static(meta, alignment.getter());
opaque_modifier(
resolve::grid_column_alignment_fn(),
resolve::grid_column_alignment_oret(),
view,
&[a],
[0.0; 4],
)
}
}
pub fn transform_effect(
view: &ViewHandle,
a: f64,
b: f64,
c: f64,
d: f64,
tx: f64,
ty: f64,
) -> ViewHandle {
unsafe {
let mut bytes = [0u8; 48];
for (i, v) in [a, b, c, d, tx, ty].iter().enumerate() {
bytes[i * 8..(i + 1) * 8].copy_from_slice(&v.to_le_bytes());
}
modifier_indirect(
resolve::transform_effect_fn(),
resolve::transform_effect_oret(),
view,
&bytes,
)
}
}
pub fn projection_effect(view: &ViewHandle, m: [f64; 9]) -> ViewHandle {
unsafe {
let mut bytes = [0u8; 72];
for (i, v) in m.iter().enumerate() {
bytes[i * 8..(i + 1) * 8].copy_from_slice(&v.to_le_bytes());
}
modifier_indirect(
resolve::projection_effect_fn(),
resolve::projection_effect_oret(),
view,
&bytes,
)
}
}
pub fn list_row_insets(view: &ViewHandle, insets: Option<[f64; 4]>) -> ViewHandle {
unsafe {
let payload = abi::metadata0(resolve::edge_insets_ma());
let opt = crate::type_by_name!(swift_type!(opt SwiftUI.EdgeInsets).as_bytes());
let mut bytes = vec![0u8; abi::type_bytes(opt)];
if let Some(v) = insets {
for (i, x) in v.iter().enumerate() {
bytes[i * 8..(i + 1) * 8].copy_from_slice(&x.to_le_bytes());
}
}
abi::store_optional_tag(&mut bytes, payload, insets.is_some());
modifier_indirect(resolve::list_row_insets_fn(), resolve::list_row_insets_oret(), view, &bytes)
}
}
pub fn window_resize_anchor(view: &ViewHandle, anchor: Option<(f64, f64)>) -> ViewHandle {
unsafe {
let w = opt_unit_point_words(anchor);
opaque_modifier(
resolve::window_resize_anchor_fn(),
resolve::window_resize_anchor_oret(),
view,
&w,
[0.0; 4],
)
}
}
pub fn default_scroll_anchor(
view: &ViewHandle,
anchor: Option<(f64, f64)>,
role: ScrollAnchorRole,
) -> ViewHandle {
unsafe {
let mut w = opt_unit_point_words(anchor);
let meta = abi::metadata0(resolve::scroll_anchor_role_ma());
let r = resilient_static(meta, role.getter());
w.push(r.as_ptr() as u64);
opaque_modifier(
resolve::default_scroll_anchor_fn(),
resolve::default_scroll_anchor_oret(),
view,
&w,
[0.0; 4],
)
}
}
#[cfg(test)]
mod optional_abi_tests {
use super::*;
#[test]
fn optional_payloads_travel_as_raw_words() {
unsafe {
let w = opt_unit_point_words(Some((0.3, 0.6)));
assert_eq!(w.len(), 3, "Optional<UnitPoint> is three words");
assert_eq!(w[0], 0.3f64.to_bits(), "x is raw bits, not a float register");
assert_eq!(w[1], 0.6f64.to_bits(), "y is raw bits, not a float register");
assert_eq!(w[2] & 0xff, 0, "some");
let n = opt_unit_point_words(None);
assert_eq!(n.len(), 3);
assert_ne!(n[2] & 0xff, 0, "none");
let d = opt_f64_words(Some(12.0));
assert_eq!(d.len(), 2, "Optional<Double> is two words");
assert_eq!(d[0], 12.0f64.to_bits());
}
}
#[test]
fn optional_bool_is_one_byte() {
unsafe {
let m = abi::resolve_type_by_mangled_name(mangle::optional(mangle::BOOL).as_bytes());
assert_eq!(abi::type_bytes(m), 1);
let mut buf = [0u8; 1];
buf[0] = 1;
abi::store_optional_tag(&mut buf, resolve::bool_meta(), true);
assert_eq!(buf[0], 1, "Some(true)");
abi::store_optional_tag(&mut buf, resolve::bool_meta(), false);
assert!(buf[0] > 1, "nil is a value a Bool cannot hold");
}
}
#[test]
fn color_is_one_frozen_word() {
unsafe {
let m = crate::type_by_name!(swift_type!(SwiftUI.Color).as_bytes());
assert_eq!(abi::type_bytes(m), 8);
let o = crate::type_by_name!(swift_type!(opt SwiftUI.Color).as_bytes());
assert_eq!(abi::type_bytes(o), 8, "a null reference is already nil");
}
}
}