use super::*;
use super::closures::*;
use crate::abi;
use crate::resolve;
use core::ffi::c_void;
pub fn on_modifier_keys_changed(
view: &ViewHandle,
mask: EventModifier,
initial: bool,
action: impl Fn(u64, u64) + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::event_modifiers_ma());
let m = frozen_static(meta, mask.getter());
let c = pair_words(invoke_word_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(u64, u64)>));
opaque_modifier(
resolve::on_modifier_keys_fn(),
resolve::on_modifier_keys_oret(),
view,
&[m, u64::from(initial), c[0], c[1]],
[0.0; 4],
)
}
}
pub fn on_pencil_double_tap(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_pencil_double_tap_fn(),
resolve::on_pencil_double_tap_oret(),
view,
&ignoring_value_words(action),
[0.0; 4],
)
}
}
pub fn on_pencil_squeeze(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
opaque_modifier(
resolve::on_pencil_squeeze_fn(),
resolve::on_pencil_squeeze_oret(),
view,
&ignoring_value_words(action),
[0.0; 4],
)
}
}
pub fn touch_bar(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
unsafe {
content_modifier(resolve::touch_bar_fn(), resolve::touch_bar_oret(), view, &[], content)
}
}
pub fn modifier_key_alternate(
view: &ViewHandle,
modifiers: EventModifier,
content: &ViewHandle,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::event_modifiers_ma());
let m = frozen_static(meta, modifiers.getter());
content_modifier(
resolve::modifier_key_alternate_fn(),
resolve::modifier_key_alternate_oret(),
view,
&[m],
content,
)
}
}
pub fn on_command(view: &ViewHandle, selector: &core::ffi::CStr, action: impl Fn() + 'static) -> ViewHandle {
unsafe {
let sel = crate::objc::sel(selector);
let c = action_words(action);
opaque_modifier(
resolve::on_command_fn(),
resolve::on_command_oret(),
view,
&[sel as u64, c[0], c[1]],
[0.0; 4],
)
}
}
pub(crate) unsafe extern "C" fn ask_condition(payload: *mut c_void, arg: u64) -> u64 {
unsafe {
let f = boxed_ref::<dyn Fn(i64, i64) -> bool>(payload);
u64::from(f(arg as i64, arg as i64))
}
}
pub fn sensory_feedback(
view: &ViewHandle,
feedback: SensoryFeedback,
trigger: i64,
condition: impl Fn(i64, i64) -> bool + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::sensory_feedback_ma());
let f = resilient_static(meta, feedback.getter());
let mut t = trigger;
let c = word_closure(ask_condition, boxed_ctx(Box::new(condition) as Box<dyn Fn(i64, i64) -> bool>));
generic1_modifier_ints(
resolve::sensory_feedback_fn(),
resolve::sensory_feedback_oret(),
view,
resolve::int_meta(),
resolve::int_equatable_wt(),
&[f.as_ptr() as u64, &mut t as *mut i64 as u64, c[0], c[1]],
[0.0; 4],
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CoordinateSpace {
Global,
Local,
}
impl CoordinateSpace {
fn tag(self) -> u32 {
match self {
CoordinateSpace::Global => 2,
CoordinateSpace::Local => 3,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum HoverPhase {
Active,
Ended,
}
unsafe extern "C" fn saw_hover(value: *const u8, payload: *mut c_void) {
unsafe {
let f = boxed_ref::<dyn Fn(HoverPhase)>(payload);
let meta = abi::metadata0(resolve::hover_phase_ma());
let phase = match abi::read_enum_tag(value, meta) {
0 => HoverPhase::Active,
_ => HoverPhase::Ended,
};
f(phase);
}
}
pub fn on_continuous_hover(
view: &ViewHandle,
space: CoordinateSpace,
perform: impl Fn(HoverPhase) + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::coordinate_space_ma());
let mut buf = vec![0u8; abi::type_bytes(meta)];
abi::inject_enum_tag(&mut buf, meta, space.tag());
let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
saw_hover,
boxed_ctx(Box::new(perform) as Box<dyn Fn(HoverPhase)>),
))) as *mut abi::ValueClosureCtx;
let subst = [view.meta, view.wt];
let (m, wt, size) = opaque_result_type(resolve::on_continuous_hover_oret(), &subst);
let r = abi::call_modifier(resolve::on_continuous_hover_fn(), view.value_ptr(), view.meta, view.wt,
&[
buf.as_ptr() as u64,
abi::value_closure_thunk as *const () as u64,
ctx as u64,
],
[0.0; 4],
size,
);
wrap_owned(r, m, wt)
}
}
#[cfg(target_os = "macos")]
pub fn touch_bar_item_presence(
view: &ViewHandle,
presence: TouchBarItemPresence,
identifier: &str,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::touch_bar_item_presence_ma());
let mut buf = vec![0u8; abi::type_bytes(meta)];
buf[..16].copy_from_slice(&abi::swift_string(identifier));
abi::inject_enum_tag(&mut buf, meta, presence as u32);
let subst = [view.meta, view.wt];
let (m, wt, size) = opaque_result_type(resolve::touch_bar_item_presence_oret(), &subst);
let r = abi::call_modifier(resolve::touch_bar_item_presence_fn(), view.value_ptr(), view.meta, view.wt,
&[buf.as_ptr() as u64],
[0.0; 4],
size,
);
wrap_owned(r, m, wt)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum KeyPressResult {
Handled = 0,
Ignored = 1,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct KeyPress {
pub characters: String,
pub modifiers: u64,
pub phase: u64,
}
impl KeyPress {
pub fn holding(&self, modifier: EventModifier) -> bool {
let bit = unsafe {
let meta = abi::metadata0(resolve::event_modifiers_ma());
frozen_static(meta, modifier.getter())
};
bit != 0 && self.modifiers & bit == bit
}
}
fn key_press_offsets() -> Option<&'static [usize; 4]> {
static CACHE: std::sync::OnceLock<Option<[usize; 4]>> = std::sync::OnceLock::new();
CACHE
.get_or_init(|| unsafe {
let offs = abi::struct_field_offsets(abi::metadata0(resolve::key_press_ma()))?;
let &[a, b, c, d] = offs.as_slice() else { return None };
Some([a, b, c, d])
})
.as_ref()
}
pub(crate) unsafe fn read_key_press(p: *const u8) -> KeyPress {
let Some(&[phase, _key, characters, modifiers]) = key_press_offsets() else {
return KeyPress::default();
};
unsafe {
let mut s = [0u8; 16];
s.copy_from_slice(core::slice::from_raw_parts(p.add(characters), 16));
KeyPress {
characters: abi::read_swift_string(s),
modifiers: *(p.add(modifiers) as *const u64),
phase: *(p.add(phase) as *const u64),
}
}
}
pub(crate) unsafe extern "C" fn invoke_key_press(payload: *mut c_void, arg: u64) -> u64 {
unsafe {
let f = boxed_ref::<dyn Fn(KeyPress) -> KeyPressResult>(payload);
let meta = abi::metadata0(resolve::key_press_result_ma());
abi::enum_argument(meta, f(read_key_press(arg as *const u8)) as u32)
}
}
pub fn on_key_press(
view: &ViewHandle,
characters: CharacterSetKind,
phases: KeyPressPhases,
action: impl Fn(KeyPress) -> KeyPressResult + 'static,
) -> ViewHandle {
unsafe {
let cm = abi::metadata0(resolve::character_set_ma());
let pm = abi::metadata0(resolve::key_press_phases_ma());
let c = resilient_static(cm, characters.getter());
let p = resilient_static(pm, phases.getter());
let k = word_closure(invoke_key_press, boxed_ctx(Box::new(action) as Box<dyn Fn(KeyPress) -> KeyPressResult>));
opaque_modifier(
resolve::on_key_press_fn(),
resolve::on_key_press_oret(),
view,
&[c.as_ptr() as u64, p.as_ptr() as u64, k[0], k[1]],
[0.0; 4],
)
}
}
swift_statics! {
pub enum Key {
Escape = c"$s7SwiftUI13KeyEquivalentV6escapeACvgZ",
Return = c"$s7SwiftUI13KeyEquivalentV6returnACvgZ",
Tab = c"$s7SwiftUI13KeyEquivalentV3tabACvgZ",
Space = c"$s7SwiftUI13KeyEquivalentV5spaceACvgZ",
Delete = c"$s7SwiftUI13KeyEquivalentV6deleteACvgZ",
DeleteForward = c"$s7SwiftUI13KeyEquivalentV13deleteForwardACvgZ",
Clear = c"$s7SwiftUI13KeyEquivalentV5clearACvgZ",
UpArrow = c"$s7SwiftUI13KeyEquivalentV7upArrowACvgZ",
DownArrow = c"$s7SwiftUI13KeyEquivalentV9downArrowACvgZ",
LeftArrow = c"$s7SwiftUI13KeyEquivalentV9leftArrowACvgZ",
RightArrow = c"$s7SwiftUI13KeyEquivalentV10rightArrowACvgZ",
Home = c"$s7SwiftUI13KeyEquivalentV4homeACvgZ",
End = c"$s7SwiftUI13KeyEquivalentV3endACvgZ",
PageUp = c"$s7SwiftUI13KeyEquivalentV6pageUpACvgZ",
PageDown = c"$s7SwiftUI13KeyEquivalentV8pageDownACvgZ",
}
}
pub fn on_key(
view: &ViewHandle,
key: Key,
action: impl Fn() -> KeyPressResult + 'static,
) -> ViewHandle {
unsafe {
let meta = abi::metadata0(resolve::key_equivalent_ma());
let bytes = resilient_static(meta, key.getter());
let c = word_closure(
invoke_key_result,
boxed_ctx(Box::new(action) as Box<dyn Fn() -> KeyPressResult>),
);
opaque_modifier(
resolve::on_key_single_fn(),
resolve::on_key_single_oret(),
view,
&[bytes.as_ptr() as u64, c[0], c[1]],
[0.0; 4],
)
}
}
pub(crate) unsafe extern "C" fn invoke_key_result(payload: *mut c_void, _arg: u64) -> u64 {
unsafe {
let f = boxed_ref::<dyn Fn() -> KeyPressResult>(payload);
let meta = abi::metadata0(resolve::key_press_result_ma());
abi::enum_argument(meta, f() as u32)
}
}