use super::{
enum_bytes, modifier_bool, modifier_i64, modifier_indirect, static_getter,
static_getter_indirect, BoundState, EdgeSet, ViewHandle,
};
use crate::abi;
use crate::mangle::{self, swift_type};
use crate::resolve;
use core::ffi::c_void;
pub fn full_screen_cover(view: &ViewHandle, content: &ViewHandle, presented: bool) -> ViewHandle {
unsafe {
let binding = super::constant_binding(
&[u8::from(presented)],
resolve::bool_meta(),
mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
);
super::presenting_with_dismiss(
view,
content,
&binding,
[0, 0],
resolve::full_screen_cover_fn(),
resolve::full_screen_cover_oret(),
)
}
}
pub fn full_screen_cover_bound(
view: &ViewHandle,
content: &ViewHandle,
state: &BoundState<bool>,
) -> ViewHandle {
unsafe {
let binding = super::bool_binding_of(state);
super::presenting_with_dismiss(
view,
content,
&binding,
[0, 0],
resolve::full_screen_cover_fn(),
resolve::full_screen_cover_oret(),
)
}
}
pub fn full_screen_cover_bound_dismiss(
view: &ViewHandle,
content: &ViewHandle,
state: &BoundState<bool>,
on_dismiss: impl Fn() + 'static,
) -> ViewHandle {
unsafe {
let binding = super::bool_binding_of(state);
let words = super::action_words(on_dismiss);
super::presenting_with_dismiss(
view,
content,
&binding,
words,
resolve::full_screen_cover_fn(),
resolve::full_screen_cover_oret(),
)
}
}
pub fn status_bar_hidden(view: &ViewHandle, hidden: bool) -> ViewHandle {
unsafe {
modifier_bool(
resolve::status_bar_fn(),
resolve::status_bar_oret(),
view,
hidden,
)
}
}
pub fn navigation_bar_hidden(view: &ViewHandle, hidden: bool) -> ViewHandle {
unsafe {
modifier_bool(
resolve::navigation_bar_hidden_fn(),
resolve::navigation_bar_hidden_oret(),
view,
hidden,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TitleDisplayMode {
Automatic,
Inline,
Large,
}
impl TitleDisplayMode {
fn case(self) -> &'static str {
match self {
Self::Automatic => "automatic",
Self::Inline => "inline",
Self::Large => "large",
}
}
}
pub fn navigation_bar_title_display_mode(
view: &ViewHandle,
mode: TitleDisplayMode,
) -> ViewHandle {
unsafe {
let ma = resolve::title_display_mode_ma();
let meta = abi::metadata0(ma);
assert!(!meta.is_null(), "NavigationBarItem.TitleDisplayMode metadata");
let tag = abi::enum_case_index(meta, mode.case()).unwrap_or_else(|| {
panic!("NavigationBarItem.TitleDisplayMode has no `{}` case", mode.case())
});
let bytes = enum_bytes(ma, tag as u32);
modifier_indirect(
resolve::nav_bar_title_display_mode_fn(),
resolve::nav_bar_title_display_mode_oret(),
view,
&bytes,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i64)]
pub enum KeyboardType {
Default = 0,
AsciiCapable = 1,
NumbersAndPunctuation = 2,
Url = 3,
NumberPad = 4,
PhonePad = 5,
NamePhonePad = 6,
EmailAddress = 7,
DecimalPad = 8,
Twitter = 9,
WebSearch = 10,
AsciiCapableNumberPad = 11,
}
pub fn keyboard_type(view: &ViewHandle, kind: KeyboardType) -> ViewHandle {
unsafe {
modifier_i64(
resolve::keyboard_type_fn(),
resolve::keyboard_type_oret(),
view,
kind as i64,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(i64)]
pub enum Autocapitalization {
None = 0,
Words = 1,
Sentences = 2,
AllCharacters = 3,
}
pub fn autocapitalization(view: &ViewHandle, style: Autocapitalization) -> ViewHandle {
unsafe {
modifier_i64(
resolve::autocapitalization_fn(),
resolve::autocapitalization_oret(),
view,
style as i64,
)
}
}
swift_statics! {
pub enum TextInputCapitalization {
Never = c"$s7SwiftUI27TextInputAutocapitalizationV5neverACvgZ",
Words = c"$s7SwiftUI27TextInputAutocapitalizationV5wordsACvgZ",
Sentences = c"$s7SwiftUI27TextInputAutocapitalizationV9sentencesACvgZ",
Characters = c"$s7SwiftUI27TextInputAutocapitalizationV10charactersACvgZ",
}
}
pub fn text_input_autocapitalization(
view: &ViewHandle,
style: Option<TextInputCapitalization>,
) -> ViewHandle {
unsafe {
let payload = abi::metadata0(resolve::text_input_autocapitalization_ma());
assert!(!payload.is_null(), "TextInputAutocapitalization metadata");
let opt = abi::resolve_type_by_mangled_name(
swift_type!(opt SwiftUI.TextInputAutocapitalization).as_bytes(),
);
assert!(!opt.is_null(), "Optional<TextInputAutocapitalization> metadata");
let mut buf = vec![0u8; abi::type_bytes(opt)];
if let Some(s) = style {
let value = static_getter_indirect(s.resolved(), payload, 0);
buf[..value.len()].copy_from_slice(&value);
}
abi::store_optional_tag(&mut buf, payload, style.is_some());
modifier_indirect(
resolve::text_input_autocapitalization_fn(),
resolve::text_input_autocapitalization_oret(),
view,
&buf,
)
}
}
pub fn defers_system_gestures(view: &ViewHandle, edges: EdgeSet) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::edge_set_ma());
let (bits, _) = static_getter(edges.resolved(), meta);
modifier_i64(
resolve::defers_system_gestures_fn(),
resolve::defers_system_gestures_oret(),
view,
bits as i64,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct Day {
pub year: i64,
pub month: i64,
pub day: i64,
}
unsafe fn date_components(day: Day) -> Vec<u8> {
unsafe {
let cls = crate::objc::class(c"OBJC_CLASS_$_NSDateComponents");
assert!(!cls.is_null(), "NSDateComponents class");
let obj = crate::objc::send(cls, crate::objc::sel(c"alloc"));
let obj = crate::objc::send(obj, crate::objc::sel(c"init"));
assert!(!obj.is_null(), "NSDateComponents init");
crate::objc::send_i64(obj, crate::objc::sel(c"setYear:"), day.year);
crate::objc::send_i64(obj, crate::objc::sel(c"setMonth:"), day.month);
crate::objc::send_i64(obj, crate::objc::sel(c"setDay:"), day.day);
let meta = abi::metadata0(resolve::date_components_ma());
assert!(!meta.is_null(), "DateComponents metadata");
let mut out = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::date_components_from_ns(),
in("x0") obj,
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 multi_date_picker(title: &str, days: &[Day]) -> ViewHandle {
unsafe {
let named = mangle::bound(
&swift_type!(SwiftUI.MultiDatePicker),
&[&swift_type!(SwiftUI.Text)],
);
let meta = abi::resolve_type_by_mangled_name(named.as_bytes());
assert!(!meta.is_null(), "{}", mangle::demangle(&named));
let wt = abi::get_view_wt(meta);
assert!(!wt.is_null(), "{} !: View", crate::diag::type_name(meta));
let dc_meta = abi::metadata0(resolve::date_components_ma());
assert!(!dc_meta.is_null(), "DateComponents metadata");
let hashable = abi::get_view_wt_for_proto(dc_meta, resolve::hashable_proto());
assert!(!hashable.is_null(), "DateComponents !: Hashable");
let stride = abi::type_bytes(dc_meta);
let mut bytes = vec![0u8; stride * days.len()];
for (i, d) in days.iter().enumerate() {
let v = date_components(*d);
bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
}
let set = abi::swift_set(dc_meta, hashable, days.len(), &bytes);
let set_named = mangle::set(&swift_type!(Foundation.DateComponents));
let set_meta = abi::resolve_type_by_mangled_name(set_named.as_bytes());
assert!(!set_meta.is_null(), "{}", mangle::demangle(&set_named));
let binding = super::constant_binding(
&set.to_le_bytes(),
set_meta,
mangle::bound(&swift_type!(SwiftUI.Binding), &[&set_named]).as_bytes(),
);
let b0 = u64::from_le_bytes(binding[0..8].try_into().expect("binding lo"));
let b1 = u64::from_le_bytes(binding[8..16].try_into().expect("binding mid"));
let b2 = u64::from_le_bytes(binding[16..24].try_into().expect("binding hi"));
let (k0, k1, k2, k3) = super::lsk_words(title);
let mut result = vec![0u8; abi::type_bytes(meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::multi_date_picker_init(),
in("x0") k0, in("x1") k1, in("x2") k2, in("x3") k3,
in("x4") b0, in("x5") b1, in("x6") b2,
in("x8") result.as_mut_ptr(),
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"),
);
super::wrap_owned(result, meta, wt)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum AlertButtonKind {
Cancel,
Default,
Destructive,
}
pub struct AlertButton<'a> {
kind: AlertButtonKind,
title: &'a str,
action: [u64; 2],
}
impl<'a> AlertButton<'a> {
pub fn cancel() -> Self {
Self { kind: AlertButtonKind::Cancel, title: "", action: [0, 0] }
}
pub fn default(title: &'a str) -> Self {
Self { kind: AlertButtonKind::Default, title, action: [0, 0] }
}
pub fn destructive(title: &'a str) -> Self {
Self { kind: AlertButtonKind::Destructive, title, action: [0, 0] }
}
pub fn on_tap(mut self, action: impl Fn() + 'static) -> Self {
self.action = super::action_words(action);
self
}
unsafe fn value(&self) -> Vec<u8> {
unsafe {
let meta = abi::metadata0(resolve::alert_button_ma());
assert!(!meta.is_null(), "Alert.Button metadata");
let mut out = vec![0u8; abi::type_bytes(meta)];
match self.kind {
AlertButtonKind::Cancel => {
core::arch::asm!("blr {f}", f = in(reg) resolve::alert_button_cancel(),
in("x0") self.action[0], in("x1") self.action[1],
in("x8") out.as_mut_ptr(),
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"),
);
}
kind => {
let f = if kind == AlertButtonKind::Default {
resolve::alert_button_default()
} else {
resolve::alert_button_destructive()
};
let t = super::text_value(self.title);
core::arch::asm!("blr {f}", f = in(reg) f,
in("x0") t[0], in("x1") t[1], in("x2") t[2], in("x3") t[3],
in("x4") self.action[0], in("x5") self.action[1],
in("x8") out.as_mut_ptr(),
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
}
}
}
struct SheetPayload {
title: [u64; 4],
message: [u64; 4],
buttons: u64,
meta: *const c_void,
}
unsafe extern "C" fn emit_action_sheet(out: *mut u8, payload: *mut c_void) {
let p = unsafe { &*(payload as *const SheetPayload) };
unsafe {
core::arch::asm!(
"sub sp, sp, #16",
"str {b}, [sp]",
"blr {f}",
"add sp, sp, #16",
f = in(reg) resolve::action_sheet_init(),
b = in(reg) p.buttons,
in("x0") p.title[0], in("x1") p.title[1],
in("x2") p.title[2], in("x3") p.title[3],
in("x4") p.message[0], in("x5") p.message[1],
in("x6") p.message[2], in("x7") p.message[3],
in("x8") out,
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"),
);
}
let _ = p.meta;
}
pub fn action_sheet(
view: &ViewHandle,
title: &str,
message: Option<&str>,
buttons: Vec<AlertButton<'_>>,
) -> ViewHandle {
unsafe {
let binding = super::constant_binding(
&[1u8],
resolve::bool_meta(),
mangle::bound(&swift_type!(SwiftUI.Binding), &[mangle::BOOL]).as_bytes(),
);
sheet_of_actions(view, title, message, buttons, &binding)
}
}
pub fn action_sheet_bound(
view: &ViewHandle,
title: &str,
message: Option<&str>,
buttons: Vec<AlertButton<'_>>,
state: &BoundState<bool>,
) -> ViewHandle {
unsafe {
let binding = super::bool_binding_of(state);
sheet_of_actions(view, title, message, buttons, &binding)
}
}
unsafe fn sheet_of_actions(
view: &ViewHandle,
title: &str,
message: Option<&str>,
buttons: Vec<AlertButton<'_>>,
binding: &[u8],
) -> ViewHandle {
unsafe {
assert!(!buttons.is_empty(), "an action sheet needs at least one button");
let btn_meta = abi::metadata0(resolve::alert_button_ma());
assert!(!btn_meta.is_null(), "Alert.Button metadata");
let stride = abi::type_bytes(btn_meta);
let mut bytes = vec![0u8; stride * buttons.len()];
for (i, b) in buttons.iter().enumerate() {
let v = b.value();
bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
}
let array = abi::swift_array(btn_meta, buttons.len(), &bytes);
let sheet_meta = abi::metadata0(resolve::action_sheet_ma());
assert!(!sheet_meta.is_null(), "ActionSheet metadata");
let payload = Box::leak(Box::new(SheetPayload {
title: super::text_value(title),
message: match message {
Some(m) => super::text_value(m),
None => [0; 4],
},
buttons: array,
meta: sheet_meta,
})) as *mut SheetPayload as *mut c_void;
let ctx = Box::leak(Box::new(abi::ClosureCtx::new(emit_action_sheet, payload)))
as *mut abi::ClosureCtx;
let b0 = u64::from_le_bytes(binding[0..8].try_into().expect("binding lo"));
let b1 = u64::from_le_bytes(binding[8..16].try_into().expect("binding mid"));
let b2 = binding[16] as u64;
let (meta, wt, size) =
super::opaque_result_type(resolve::action_sheet_oret(), &[view.meta, view.wt]);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::action_sheet_fn(),
in("x0") b0, in("x1") b1, in("x2") b2,
in("x3") abi::swift_closure_thunk as *const c_void,
in("x4") ctx,
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"),
);
super::wrap_owned(result, meta, wt)
}
}
swift_statics! {
pub enum IndexBackground {
Always = c"$s7SwiftUI18PageIndexViewStyleV21BackgroundDisplayModeV6alwaysAEvgZ",
Automatic = c"$s7SwiftUI18PageIndexViewStyleV21BackgroundDisplayModeV9automaticAEvgZ",
Interactive = c"$s7SwiftUI18PageIndexViewStyleV21BackgroundDisplayModeV11interactiveAEvgZ",
Never = c"$s7SwiftUI18PageIndexViewStyleV21BackgroundDisplayModeV5neverAEvgZ",
}
}
pub fn index_view_style_page(view: &ViewHandle, background: IndexBackground) -> ViewHandle {
unsafe {
let bg_meta = abi::metadata0(resolve::page_index_background_ma());
assert!(!bg_meta.is_null(), "PageIndexViewStyle.BackgroundDisplayMode metadata");
let mut bg = static_getter_indirect(background.resolved(), bg_meta, 0);
let style_meta = abi::metadata0(resolve::page_index_style_ma());
assert!(!style_meta.is_null(), "PageIndexViewStyle metadata");
let style_wt =
abi::get_view_wt_for_proto(style_meta, resolve::index_view_style_proto());
assert!(!style_wt.is_null(), "PageIndexViewStyle !: IndexViewStyle");
let mut style = vec![0u8; abi::type_bytes(style_meta)];
core::arch::asm!("blr {f}", f = in(reg) resolve::page_index_style_init(),
in("x0") bg.as_mut_ptr(),
in("x8") style.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"),
);
let subst = [view.meta, style_meta, view.wt, style_wt];
let (meta, wt, size) =
super::opaque_result_type(resolve::index_view_style_oret(), &subst);
let mut result = vec![0u8; size];
let this = view.value_ptr();
core::arch::asm!("blr {f}", f = in(reg) resolve::index_view_style_fn(),
in("x0") style.as_mut_ptr(),
in("x1") view.meta,
in("x2") style_meta,
in("x3") view.wt,
in("x4") style_wt,
in("x8") result.as_mut_ptr(),
in("x20") this,
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("x21") _, clobber_abi("C"),
);
super::wrap_owned(result, meta, wt)
}
}