use super::*;
use super::closures::*;
use super::dragdrop::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
use crate::mangle::{self, swift_type};
pub fn search_selection(view: &ViewHandle, changed: impl Fn() + 'static) -> ViewHandle {
unsafe {
let value_meta = crate::type_by_name!(swift_type!(opt SwiftUI.TextSelection).as_bytes());
let payload = crate::type_by_name!(swift_type!(SwiftUI.TextSelection).as_bytes());
let width = abi::type_bytes(value_meta);
let mut none = vec![0u8; width];
abi::store_optional_tag(&mut none, payload, false);
let binding = binding_get_set(
value_meta,
mangle::ty("Binding")
.of(vec![mangle::opt(mangle::ty("TextSelection"))])
.mangle()
.as_bytes(),
Box::new(move |out| {
core::ptr::copy_nonoverlapping(none.as_ptr(), out, width);
}),
Box::new(move |_| changed()),
);
modifier_indirect(
resolve::search_selection_fn(),
resolve::search_selection_oret(),
view,
&binding,
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PresentationDetent {
Medium,
Large,
}
impl PresentationDetent {
fn getter(self) -> &'static core::ffi::CStr {
match self {
PresentationDetent::Medium => c"$s7SwiftUI18PresentationDetentV6mediumACvgZ",
PresentationDetent::Large => c"$s7SwiftUI18PresentationDetentV5largeACvgZ",
}
}
}
pub(crate) unsafe fn string_set(items: &[&str]) -> u64 {
unsafe {
let meta = resolve::string_meta();
let stride = abi::type_bytes(meta);
let mut bytes = vec![0u8; stride * items.len()];
for (i, s) in items.iter().enumerate() {
bytes[i * stride..i * stride + 16].copy_from_slice(&abi::swift_string(s));
}
abi::swift_set(meta, resolve::string_hashable_wt(), items.len(), &bytes)
}
}
pub fn handles_external_events(
view: &ViewHandle,
preferring: &[&str],
allowing: &[&str],
) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::handles_external_events_fn(),
resolve::handles_external_events_oret(),
view,
&[string_set(preferring), string_set(allowing)],
[0.0; 4],
)
}
}
pub fn presentation_detents(view: &ViewHandle, detents: &[PresentationDetent]) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::presentation_detent_ma());
let wt = abi::get_view_wt_for_proto(meta, resolve::hashable_proto());
assert!(!wt.is_null(), "PresentationDetent: Hashable");
let stride = abi::type_bytes(meta);
let mut bytes = vec![0u8; stride * detents.len()];
for (i, d) in detents.iter().enumerate() {
let v = resilient_static(meta, d.getter());
bytes[i * stride..i * stride + v.len()].copy_from_slice(&v);
}
let set = abi::swift_set(meta, wt, detents.len(), &bytes);
opaque_modifier(
resolve::presentation_detents_fn(),
resolve::presentation_detents_oret(),
view,
&[set],
[0.0; 4],
)
}
}
pub fn searchable(
view: &ViewHandle,
text: &BoundState<String>,
presented: &BoundState<bool>,
placement: SearchFieldPlacement,
prompt: &str,
) -> ViewHandle {
unsafe {
let t = text_binding(text);
let p = bool_binding(presented);
let meta = abi::metadata0(resolve::search_field_placement_ma());
let place = resilient_static(meta, placement.getter());
let (k0, k1, k2, k3) = lsk_words(prompt);
let mut ints: Vec<u64> = words(&t);
ints.extend(words(&p));
ints.push(place.as_ptr() as u64);
ints.extend_from_slice(&[k0, k1, k2, k3]);
let subst = [view.meta, view.wt];
let (m, wt, size) = opaque_result_type(resolve::searchable_oret(), &subst);
let r = abi::call_modifier(resolve::searchable_fn(), view.value_ptr(), view.meta, view.wt, &ints, [0.0; 4], size);
wrap_owned(r, m, wt)
}
}
pub fn search_suggestions(
view: &ViewHandle,
visibility: Visibility,
placements: SearchSuggestionsPlacementSet,
) -> ViewHandle {
unsafe {
let vm = abi::metadata0(resolve::visibility_ma());
let sm = abi::metadata0(resolve::search_suggestions_placement_set_ma());
let p = resilient_static(sm, placements.getter());
opaque_modifier(
resolve::search_suggestions_fn(),
resolve::search_suggestions_oret(),
view,
&[abi::enum_argument(vm, visibility as u32), p.as_ptr() as u64],
[0.0; 4],
)
}
}
pub fn text_input_formatting_control_visibility(
view: &ViewHandle,
visibility: Visibility,
placements: TextInputFormattingControlPlacementSet,
) -> ViewHandle {
unsafe {
let vm = abi::metadata0(resolve::visibility_ma());
let sm = abi::metadata0(resolve::text_input_formatting_set_ma());
let p = resilient_static(sm, placements.getter());
opaque_modifier(
resolve::text_input_formatting_fn(),
resolve::text_input_formatting_oret(),
view,
&[abi::enum_argument(vm, visibility as u32), p.as_ptr() as u64],
[0.0; 4],
)
}
}
pub fn dismissal_confirmation_dialog(
view: &ViewHandle,
title: &str,
should_present: bool,
actions: &ViewHandle,
message: &ViewHandle,
) -> ViewHandle {
unsafe {
let (k0, k1, k2, k3) = lsk_words(title);
two_view_modifier(
resolve::dismissal_dialog_fn(),
resolve::dismissal_dialog_oret(),
view,
&[k0, k1, k2, k3, u64::from(should_present)],
actions,
message,
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum FileOutcome {
Picked(String),
Failed,
}
pub(crate) unsafe extern "C" fn invoke_file_result(
a0: u64,
_a1: u64,
_a2: u64,
payload: *mut c_void,
) {
unsafe {
let f = boxed_ref::<dyn Fn(FileOutcome)>(payload);
let meta = abi::resolve_type_by_mangled_name(mangle::bound(&swift_type!(stdlib enum Result), &[&swift_type!(Foundation.URL), &mangle::existential("s5Error")]).as_bytes());
if meta.is_null() {
f(FileOutcome::Failed);
return;
}
if abi::read_enum_tag(a0 as *const u8, meta) != 0 {
f(FileOutcome::Failed);
return;
}
let (s0, s1): (u64, u64);
core::arch::asm!("blr {f}", f = in(reg) resolve::url_absolute_string(),
in("x20") a0,
lateout("x0") s0, lateout("x1") s1,
lateout("x2") _, lateout("x3") _, lateout("x4") _, lateout("x5") _,
lateout("x6") _, lateout("x7") _, lateout("x8") _, 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"),
);
let mut raw = [0u8; 16];
raw[..8].copy_from_slice(&s0.to_le_bytes());
raw[8..].copy_from_slice(&s1.to_le_bytes());
f(FileOutcome::Picked(abi::read_swift_string(raw)));
}
}
pub fn file_importer(
view: &ViewHandle,
state: &BoundState<bool>,
kinds: &[ContentType],
completion: impl Fn(FileOutcome) + 'static,
) -> ViewHandle {
unsafe {
let mut b = bool_binding(state);
let w = binding_words(&mut b);
let c = wide_words(invoke_file_result, boxed_ctx(Box::new(completion) as Box<dyn Fn(FileOutcome)>));
opaque_modifier(
resolve::file_importer_fn(),
resolve::file_importer_oret(),
view,
&[w[0], w[1], w[2], content_types(kinds), c[0], c[1]],
[0.0; 4],
)
}
}
pub fn search_scopes(
view: &ViewHandle,
scope: &BoundState<i64>,
scopes: &ViewHandle,
) -> ViewHandle {
unsafe {
let b = i64_binding(scope);
let av = scopes.meta;
let aw = scopes.wt;
let im = resolve::int_meta();
let ih = resolve::int_hashable_wt();
let subst = [view.meta, im, av, view.wt, ih, aw];
let (meta, wt, size) = opaque_result_type(resolve::search_scopes_oret(), &subst);
let mut ints = vec![b.as_ptr() as u64];
ints.extend_from_slice(&view_words(scopes));
ints.extend_from_slice(&[view.meta as u64, im as u64, av as u64]);
ints.extend_from_slice(&[view.wt as u64, ih as u64, aw as u64]);
let r = abi::call_modifier_generic(resolve::search_scopes_fn(), view.value_ptr(), &ints, [0.0; 4], size);
wrap_owned(r, meta, wt)
}
}
pub(crate) unsafe fn optional_url(spec: Option<&str>) -> Vec<u8> {
unsafe {
let payload = abi::metadata0(resolve::url_ma());
let opt = crate::type_by_name!(swift_type!(opt Foundation.URL).as_bytes());
assert!(!opt.is_null(), "Optional<URL>");
let mut buf = vec![0u8; abi::type_bytes(opt)];
match spec {
Some(s) => {
let ss = abi::swift_string(s);
let s0 = u64::from_le_bytes(ss[..8].try_into().expect("url lo"));
let s1 = u64::from_le_bytes(ss[8..].try_into().expect("url hi"));
core::arch::asm!("blr {f}", f = in(reg) resolve::url_string_init(),
in("x0") s0, in("x1") s1,
in("x8") buf.as_mut_ptr(), in("x20") payload,
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("x21") _, clobber_abi("C"),
);
}
None => abi::store_optional_tag(&mut buf, payload, false),
}
buf
}
}
pub fn file_mover(
view: &ViewHandle,
state: &BoundState<bool>,
file: Option<&str>,
completion: impl Fn(FileOutcome) + 'static,
) -> ViewHandle {
unsafe {
let mut b = bool_binding(state);
let w = binding_words(&mut b);
let url = optional_url(file);
let c = wide_words(invoke_file_result, boxed_ctx(Box::new(completion) as Box<dyn Fn(FileOutcome)>));
opaque_modifier(
resolve::file_mover_fn(),
resolve::file_mover_oret(),
view,
&[w[0], w[1], w[2], url.as_ptr() as u64, c[0], c[1]],
[0.0; 4],
)
}
}
pub fn file_exporter(
view: &ViewHandle,
state: &BoundState<bool>,
item: Option<&str>,
kinds: &[ContentType],
default_filename: Option<&str>,
completion: impl Fn(FileOutcome) + 'static,
cancellation: impl Fn() + 'static,
) -> ViewHandle {
unsafe {
let mut b = bool_binding(state);
let w = binding_words(&mut b);
let meta = resolve::string_meta();
let opt = abi::resolve_type_by_mangled_name(mangle::optional(mangle::STRING).as_bytes());
let mut boxed = vec![0u8; abi::type_bytes(opt)];
if let Some(s) = item {
boxed[..16].copy_from_slice(&abi::swift_string(s));
}
abi::store_optional_tag(&mut boxed, meta, item.is_some());
let name = optional_words(
meta,
opt,
&default_filename.map(|s| abi::swift_string(s)).unwrap_or([0u8; 16]),
default_filename.is_some(),
);
let done = wide_words(invoke_file_result, boxed_ctx(Box::new(completion) as Box<dyn Fn(FileOutcome)>));
let cancel = action_words(cancellation);
let mut ints = w.to_vec();
ints.push(boxed.as_ptr() as u64);
ints.push(content_types(kinds));
ints.extend_from_slice(&name);
ints.extend_from_slice(&done);
ints.extend_from_slice(&cancel);
generic1_modifier_ints(
resolve::file_exporter_fn(),
resolve::file_exporter_oret(),
view,
meta,
{
let wt = abi::get_view_wt_for_proto(meta, resolve::transferable_proto());
assert!(!wt.is_null(), "String: Transferable");
wt
},
&ints,
[0.0; 4],
)
}
}
pub fn navigation_document_url(view: &ViewHandle, spec: &str) -> ViewHandle {
unsafe {
let url = optional_url(Some(spec));
opaque_modifier(
resolve::navigation_document_url_fn(),
resolve::navigation_document_url_oret(),
view,
&[url.as_ptr() as u64],
[0.0; 4],
)
}
}
pub fn navigation_document(view: &ViewHandle, item: &str) -> ViewHandle {
unsafe {
let meta = resolve::string_meta();
let wt = abi::get_view_wt_for_proto(meta, resolve::transferable_proto());
assert!(!wt.is_null(), "String: Transferable");
let s = abi::swift_string(item);
generic1_modifier_ints(
resolve::navigation_document_item_fn(),
resolve::navigation_document_item_oret(),
view,
meta,
wt,
&[s.as_ptr() as u64],
[0.0; 4],
)
}
}
pub fn file_dialog_url_enabled(view: &ViewHandle, enabled: bool) -> ViewHandle {
unsafe {
let meta = abi::resolve_type_by_mangled_name(
mangle::pack(
mangle::Type::Nominal {
module: "Foundation",
path: vec!["Predicate"],
kind: mangle::Kind::Struct,
},
vec![mangle::Type::Nominal {
module: "Foundation",
path: vec!["URL"],
kind: mangle::Kind::Struct,
}],
)
.mangle()
.as_bytes(),
);
assert!(!meta.is_null(), "Predicate<Pack{{URL}}> metadata");
let getter = if enabled { resolve::predicate_true() } else { resolve::predicate_false() };
let value = abi::static_out(getter, meta, abi::type_bytes(meta));
opaque_modifier(
resolve::file_dialog_url_enabled_fn(),
resolve::file_dialog_url_enabled_oret(),
view,
&[value.as_ptr() as u64],
[0.0; 4],
)
}
}