Skip to content

Commit c668d18

Browse files
mwcampbelllunixbochs
authored andcommitted
feat: Add window-based constructor to macOS subclassing adapter (AccessKit#253)
1 parent 9bd131a commit c668d18

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

bindings/c/src/macos.rs

+18
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,24 @@ impl macos_subclassing_adapter {
128128
BoxCastPtr::to_mut_ptr(adapter)
129129
}
130130

131+
/// This function takes ownership of `handler`.
132+
#[no_mangle]
133+
pub unsafe extern "C" fn accesskit_macos_subclassing_adapter_for_window(
134+
window: *mut c_void,
135+
source: tree_update_factory,
136+
source_userdata: *mut c_void,
137+
handler: *mut action_handler,
138+
) -> *mut macos_subclassing_adapter {
139+
let source = source.unwrap();
140+
let handler = box_from_ptr(handler);
141+
let adapter = SubclassingAdapter::for_window(
142+
window,
143+
move || box_from_ptr(source(source_userdata)).into(),
144+
handler,
145+
);
146+
BoxCastPtr::to_mut_ptr(adapter)
147+
}
148+
131149
#[no_mangle]
132150
pub extern "C" fn accesskit_macos_subclassing_adapter_free(
133151
adapter: *mut macos_subclassing_adapter,

platforms/macos/src/appkit/window.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
use objc2::{
77
extern_class, extern_methods,
88
foundation::{NSObject, NSPoint, NSRect},
9+
msg_send_id,
10+
rc::{Id, Shared},
911
ClassType,
1012
};
1113

12-
use super::NSResponder;
14+
use super::{NSResponder, NSView};
1315

1416
extern_class!(
1517
#[derive(Debug)]
@@ -28,5 +30,9 @@ extern_methods!(
2830

2931
#[sel(convertPointFromScreen:)]
3032
pub(crate) fn convert_point_from_screen(&self, point: NSPoint) -> NSPoint;
33+
34+
pub(crate) fn content_view(&self) -> Option<Id<NSView, Shared>> {
35+
unsafe { msg_send_id![self, contentView] }
36+
}
3137
}
3238
);

platforms/macos/src/subclass.rs

+37-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,11 @@ use objc2::{
2020
use once_cell::{sync::Lazy as SyncLazy, unsync::Lazy};
2121
use std::{collections::HashMap, ffi::c_void, sync::Mutex};
2222

23-
use crate::{appkit::NSView, event::QueuedEvents, Adapter};
23+
use crate::{
24+
appkit::{NSView, NSWindow},
25+
event::QueuedEvents,
26+
Adapter,
27+
};
2428

2529
static SUBCLASSES: SyncLazy<Mutex<HashMap<&'static Class, &'static Class>>> =
2630
SyncLazy::new(|| Mutex::new(HashMap::new()));
@@ -116,13 +120,22 @@ impl SubclassingAdapter {
116120
) -> Self {
117121
let view = view as *mut NSView;
118122
let retained_view = unsafe { Id::retain(view) }.unwrap();
123+
Self::new_internal(retained_view, source, action_handler)
124+
}
125+
126+
fn new_internal(
127+
retained_view: Id<NSView, Shared>,
128+
source: impl 'static + FnOnce() -> TreeUpdate,
129+
action_handler: Box<dyn ActionHandler>,
130+
) -> Self {
119131
let adapter: LazyAdapter = {
120132
let retained_view = retained_view.clone();
121133
Lazy::new(Box::new(move || {
122134
let view = Id::as_ptr(&retained_view) as *mut c_void;
123135
unsafe { Adapter::new(view, source(), action_handler) }
124136
}))
125137
};
138+
let view = Id::as_ptr(&retained_view) as *mut NSView;
126139
// Cast to a pointer and back to force the lifetime to 'static
127140
// SAFETY: We know the class will live as long as the instance,
128141
// and we only use this reference while the instance is alive.
@@ -171,6 +184,29 @@ impl SubclassingAdapter {
171184
}
172185
}
173186

187+
/// Create an adapter that dynamically subclasses the content view
188+
/// of the specified window.
189+
///
190+
/// The action handler will always be called on the main thread.
191+
///
192+
/// # Safety
193+
///
194+
/// `window` must be a valid, unreleased pointer to an `NSWindow`.
195+
///
196+
/// # Panics
197+
///
198+
/// This function panics if the specified window doesn't currently have
199+
/// a content view.
200+
pub unsafe fn for_window(
201+
window: *mut c_void,
202+
source: impl 'static + FnOnce() -> TreeUpdate,
203+
action_handler: Box<dyn ActionHandler>,
204+
) -> Self {
205+
let window = unsafe { &*(window as *const NSWindow) };
206+
let retained_view = window.content_view().unwrap();
207+
Self::new_internal(retained_view, source, action_handler)
208+
}
209+
174210
/// Initialize the tree if it hasn't been initialized already, then apply
175211
/// the provided update.
176212
///

0 commit comments

Comments
 (0)