1use super::*;
9use super::closures::*;
10use crate::abi;
11use crate::resolve;
12use core::ffi::c_void;
13
14pub fn on_modifier_keys_changed(
18 view: &ViewHandle,
19 mask: EventModifier,
20 initial: bool,
21 action: impl Fn(u64, u64) + 'static,
22) -> ViewHandle {
23 unsafe {
24 let meta = abi::metadata0(resolve::event_modifiers_ma());
25 let m = frozen_static(meta, mask.getter());
27 let c = pair_words(invoke_word_pair, boxed_ctx(Box::new(action) as Box<dyn Fn(u64, u64)>));
28 opaque_modifier(
29 resolve::on_modifier_keys_fn(),
30 resolve::on_modifier_keys_oret(),
31 view,
32 &[m, u64::from(initial), c[0], c[1]],
33 [0.0; 4],
34 )
35 }
36}
37
38pub fn on_pencil_double_tap(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
40 unsafe {
41 opaque_modifier(
42 resolve::on_pencil_double_tap_fn(),
43 resolve::on_pencil_double_tap_oret(),
44 view,
45 &ignoring_value_words(action),
46 [0.0; 4],
47 )
48 }
49}
50
51pub fn on_pencil_squeeze(view: &ViewHandle, action: impl Fn() + 'static) -> ViewHandle {
53 unsafe {
54 opaque_modifier(
55 resolve::on_pencil_squeeze_fn(),
56 resolve::on_pencil_squeeze_oret(),
57 view,
58 &ignoring_value_words(action),
59 [0.0; 4],
60 )
61 }
62}
63
64pub fn touch_bar(view: &ViewHandle, content: &ViewHandle) -> ViewHandle {
66 unsafe {
67 content_modifier(resolve::touch_bar_fn(), resolve::touch_bar_oret(), view, &[], content)
68 }
69}
70
71pub fn modifier_key_alternate(
73 view: &ViewHandle,
74 modifiers: EventModifier,
75 content: &ViewHandle,
76) -> ViewHandle {
77 unsafe {
78 let meta = abi::metadata0(resolve::event_modifiers_ma());
79 let m = frozen_static(meta, modifiers.getter());
80 content_modifier(
81 resolve::modifier_key_alternate_fn(),
82 resolve::modifier_key_alternate_oret(),
83 view,
84 &[m],
85 content,
86 )
87 }
88}
89
90pub fn on_command(view: &ViewHandle, selector: &core::ffi::CStr, action: impl Fn() + 'static) -> ViewHandle {
94 unsafe {
95 let sel = crate::objc::sel(selector);
96 let c = action_words(action);
97 opaque_modifier(
98 resolve::on_command_fn(),
99 resolve::on_command_oret(),
100 view,
101 &[sel as u64, c[0], c[1]],
102 [0.0; 4],
103 )
104 }
105}
106
107pub(crate) unsafe extern "C" fn ask_condition(payload: *mut c_void, arg: u64) -> u64 {
108 unsafe {
109 let f = boxed_ref::<dyn Fn(i64, i64) -> bool>(payload);
110 u64::from(f(arg as i64, arg as i64))
114 }
115}
116
117pub fn sensory_feedback(
122 view: &ViewHandle,
123 feedback: SensoryFeedback,
124 trigger: i64,
125 condition: impl Fn(i64, i64) -> bool + 'static,
126) -> ViewHandle {
127 unsafe {
128 let meta = abi::metadata0(resolve::sensory_feedback_ma());
129 let f = resilient_static(meta, feedback.getter());
130 let mut t = trigger;
131 let c = word_closure(ask_condition, boxed_ctx(Box::new(condition) as Box<dyn Fn(i64, i64) -> bool>));
132 generic1_modifier_ints(
133 resolve::sensory_feedback_fn(),
134 resolve::sensory_feedback_oret(),
135 view,
136 resolve::int_meta(),
137 resolve::int_equatable_wt(),
138 &[f.as_ptr() as u64, &mut t as *mut i64 as u64, c[0], c[1]],
139 [0.0; 4],
140 )
141 }
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
154pub enum CoordinateSpace {
155 Global,
156 Local,
157}
158
159impl CoordinateSpace {
160 fn tag(self) -> u32 {
161 match self {
162 CoordinateSpace::Global => 2,
163 CoordinateSpace::Local => 3,
164 }
165 }
166}
167
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
170pub enum HoverPhase {
171 Active,
173 Ended,
175}
176
177unsafe extern "C" fn saw_hover(value: *const u8, payload: *mut c_void) {
184 unsafe {
185 let f = boxed_ref::<dyn Fn(HoverPhase)>(payload);
186 let meta = abi::metadata0(resolve::hover_phase_ma());
187 let phase = match abi::read_enum_tag(value, meta) {
188 0 => HoverPhase::Active,
189 _ => HoverPhase::Ended,
190 };
191 f(phase);
192 }
193}
194
195pub fn on_continuous_hover(
197 view: &ViewHandle,
198 space: CoordinateSpace,
199 perform: impl Fn(HoverPhase) + 'static,
200) -> ViewHandle {
201 unsafe {
202 let meta = abi::metadata0(resolve::coordinate_space_ma());
203 let mut buf = vec![0u8; abi::type_bytes(meta)];
204 abi::inject_enum_tag(&mut buf, meta, space.tag());
205 let ctx = Box::leak(Box::new(abi::ValueClosureCtx::new(
206 saw_hover,
207 boxed_ctx(Box::new(perform) as Box<dyn Fn(HoverPhase)>),
208 ))) as *mut abi::ValueClosureCtx;
209 let subst = [view.meta, view.wt];
210 let (m, wt, size) = opaque_result_type(resolve::on_continuous_hover_oret(), &subst);
211 let r = abi::call_modifier(resolve::on_continuous_hover_fn(), view.value_ptr(), view.meta, view.wt,
212 &[
213 buf.as_ptr() as u64,
214 abi::value_closure_thunk as *const () as u64,
215 ctx as u64,
216 ],
217 [0.0; 4],
218 size,
219 );
220 wrap_owned(r, m, wt)
221 }
222}
223
224#[cfg(target_os = "macos")]
231pub fn touch_bar_item_presence(
232 view: &ViewHandle,
233 presence: TouchBarItemPresence,
234 identifier: &str,
235) -> ViewHandle {
236 unsafe {
237 let meta = abi::metadata0(resolve::touch_bar_item_presence_ma());
238 let mut buf = vec![0u8; abi::type_bytes(meta)];
239 buf[..16].copy_from_slice(&abi::swift_string(identifier));
240 abi::inject_enum_tag(&mut buf, meta, presence as u32);
241 let subst = [view.meta, view.wt];
242 let (m, wt, size) = opaque_result_type(resolve::touch_bar_item_presence_oret(), &subst);
243 let r = abi::call_modifier(resolve::touch_bar_item_presence_fn(), view.value_ptr(), view.meta, view.wt,
244 &[buf.as_ptr() as u64],
245 [0.0; 4],
246 size,
247 );
248 wrap_owned(r, m, wt)
249 }
250}
251
252#[derive(Clone, Copy, Debug, PartialEq, Eq)]
257pub enum KeyPressResult {
258 Handled = 0,
259 Ignored = 1,
260}
261
262#[derive(Clone, Debug, Default, PartialEq, Eq)]
275pub struct KeyPress {
276 pub characters: String,
279 pub modifiers: u64,
282 pub phase: u64,
285}
286
287impl KeyPress {
288 pub fn holding(&self, modifier: EventModifier) -> bool {
295 let bit = unsafe {
296 let meta = abi::metadata0(resolve::event_modifiers_ma());
297 frozen_static(meta, modifier.getter())
298 };
299 bit != 0 && self.modifiers & bit == bit
300 }
301}
302
303fn key_press_offsets() -> Option<&'static [usize; 4]> {
305 static CACHE: std::sync::OnceLock<Option<[usize; 4]>> = std::sync::OnceLock::new();
306 CACHE
307 .get_or_init(|| unsafe {
308 let offs = abi::struct_field_offsets(abi::metadata0(resolve::key_press_ma()))?;
309 let &[a, b, c, d] = offs.as_slice() else { return None };
311 Some([a, b, c, d])
312 })
313 .as_ref()
314}
315
316pub(crate) unsafe fn read_key_press(p: *const u8) -> KeyPress {
321 let Some(&[phase, _key, characters, modifiers]) = key_press_offsets() else {
322 return KeyPress::default();
323 };
324 unsafe {
325 let mut s = [0u8; 16];
326 s.copy_from_slice(core::slice::from_raw_parts(p.add(characters), 16));
327 KeyPress {
328 characters: abi::read_swift_string(s),
332 modifiers: *(p.add(modifiers) as *const u64),
333 phase: *(p.add(phase) as *const u64),
334 }
335 }
336}
337
338pub(crate) unsafe extern "C" fn invoke_key_press(payload: *mut c_void, arg: u64) -> u64 {
343 unsafe {
344 let f = boxed_ref::<dyn Fn(KeyPress) -> KeyPressResult>(payload);
345 let meta = abi::metadata0(resolve::key_press_result_ma());
346 abi::enum_argument(meta, f(read_key_press(arg as *const u8)) as u32)
347 }
348}
349
350pub fn on_key_press(
356 view: &ViewHandle,
357 characters: CharacterSetKind,
358 phases: KeyPressPhases,
359 action: impl Fn(KeyPress) -> KeyPressResult + 'static,
360) -> ViewHandle {
361 unsafe {
362 let cm = abi::metadata0(resolve::character_set_ma());
363 let pm = abi::metadata0(resolve::key_press_phases_ma());
364 let c = resilient_static(cm, characters.getter());
365 let p = resilient_static(pm, phases.getter());
366 let k = word_closure(invoke_key_press, boxed_ctx(Box::new(action) as Box<dyn Fn(KeyPress) -> KeyPressResult>));
367 opaque_modifier(
368 resolve::on_key_press_fn(),
369 resolve::on_key_press_oret(),
370 view,
371 &[c.as_ptr() as u64, p.as_ptr() as u64, k[0], k[1]],
372 [0.0; 4],
373 )
374 }
375}
376
377
378swift_statics! {
379 pub enum Key {
385 Escape = c"$s7SwiftUI13KeyEquivalentV6escapeACvgZ",
386 Return = c"$s7SwiftUI13KeyEquivalentV6returnACvgZ",
387 Tab = c"$s7SwiftUI13KeyEquivalentV3tabACvgZ",
388 Space = c"$s7SwiftUI13KeyEquivalentV5spaceACvgZ",
389 Delete = c"$s7SwiftUI13KeyEquivalentV6deleteACvgZ",
390 DeleteForward = c"$s7SwiftUI13KeyEquivalentV13deleteForwardACvgZ",
391 Clear = c"$s7SwiftUI13KeyEquivalentV5clearACvgZ",
392 UpArrow = c"$s7SwiftUI13KeyEquivalentV7upArrowACvgZ",
393 DownArrow = c"$s7SwiftUI13KeyEquivalentV9downArrowACvgZ",
394 LeftArrow = c"$s7SwiftUI13KeyEquivalentV9leftArrowACvgZ",
395 RightArrow = c"$s7SwiftUI13KeyEquivalentV10rightArrowACvgZ",
396 Home = c"$s7SwiftUI13KeyEquivalentV4homeACvgZ",
397 End = c"$s7SwiftUI13KeyEquivalentV3endACvgZ",
398 PageUp = c"$s7SwiftUI13KeyEquivalentV6pageUpACvgZ",
399 PageDown = c"$s7SwiftUI13KeyEquivalentV8pageDownACvgZ",
400 }
401}
402
403pub fn on_key(
410 view: &ViewHandle,
411 key: Key,
412 action: impl Fn() -> KeyPressResult + 'static,
413) -> ViewHandle {
414 unsafe {
415 let meta = abi::metadata0(resolve::key_equivalent_ma());
421 let bytes = resilient_static(meta, key.getter());
422
423 let c = word_closure(
424 invoke_key_result,
425 boxed_ctx(Box::new(action) as Box<dyn Fn() -> KeyPressResult>),
426 );
427 opaque_modifier(
428 resolve::on_key_single_fn(),
429 resolve::on_key_single_oret(),
430 view,
431 &[bytes.as_ptr() as u64, c[0], c[1]],
432 [0.0; 4],
433 )
434 }
435}
436
437pub(crate) unsafe extern "C" fn invoke_key_result(payload: *mut c_void, _arg: u64) -> u64 {
439 unsafe {
440 let f = boxed_ref::<dyn Fn() -> KeyPressResult>(payload);
441 let meta = abi::metadata0(resolve::key_press_result_ma());
442 abi::enum_argument(meta, f() as u32)
443 }
444}