Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(platforms/macos): Basic macOS platform adapter (#158)
The major missing feature at this point is text editing support. Co-authored-by: Mads Marquart <mads@marquart.dk>
- Loading branch information
1 parent
be88b64
commit a06725e
Showing
22 changed files
with
1,427 additions
and
2 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
members = [ | ||
"common", | ||
"consumer", | ||
"platforms/macos", | ||
"platforms/windows", | ||
"platforms/winit", | ||
] | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
[package] | ||
name = "accesskit_macos" | ||
version = "0.0.0" | ||
authors = ["Matt Campbell <mattcampbell@pobox.com>"] | ||
license = "MIT/Apache-2.0" | ||
description = "AccessKit UI accessibility infrastructure: macOS adapter" | ||
categories = ["gui"] | ||
keywords = ["gui", "ui", "accessibility"] | ||
repository = "https://github.com/AccessKit/accesskit" | ||
readme = "README.md" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
accesskit = { version = "0.8.0", path = "../../common" } | ||
accesskit_consumer = { version = "0.8.0", path = "../../consumer" } | ||
objc2 = "0.3.0-beta.3" | ||
once_cell = "1.13.0" | ||
parking_lot = "0.12.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# AccessKit macOS adapter | ||
|
||
This is the macOS adapter for [AccessKit](https://accesskit.dev/). It exposes an AccessKit accessibility tree through the Cocoa `NSAccessibility` protocol. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// Copyright 2022 The AccessKit Authors. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0 (found in | ||
// the LICENSE-APACHE file) or the MIT license (found in | ||
// the LICENSE-MIT file), at your option. | ||
|
||
use accesskit::{kurbo::Point, ActionHandler, TreeUpdate}; | ||
use accesskit_consumer::{FilterResult, Tree}; | ||
use objc2::{ | ||
foundation::{MainThreadMarker, NSArray, NSObject, NSPoint}, | ||
rc::{Id, Shared, WeakId}, | ||
}; | ||
use once_cell::unsync::Lazy; | ||
use std::{ffi::c_void, ptr::null_mut, rc::Rc}; | ||
|
||
use crate::{appkit::NSView, context::Context, event::QueuedEvents, node::filter}; | ||
|
||
pub struct Adapter { | ||
context: Lazy<Rc<Context>, Box<dyn FnOnce() -> Rc<Context>>>, | ||
} | ||
|
||
impl Adapter { | ||
/// Create a new macOS adapter. This function must be called on | ||
/// the main thread. | ||
/// | ||
/// # Safety | ||
/// | ||
/// `view` must be a valid, unreleased pointer to an `NSView`. | ||
pub unsafe fn new( | ||
view: *mut c_void, | ||
source: Box<dyn FnOnce() -> TreeUpdate>, | ||
action_handler: Box<dyn ActionHandler>, | ||
) -> Self { | ||
let view = unsafe { Id::retain(view as *mut NSView) }.unwrap(); | ||
let view = WeakId::new(&view); | ||
let mtm = MainThreadMarker::new().unwrap(); | ||
Self { | ||
context: Lazy::new(Box::new(move || { | ||
let tree = Tree::new(source(), action_handler); | ||
Context::new(view, tree, mtm) | ||
})), | ||
} | ||
} | ||
|
||
/// Initialize the tree if it hasn't been initialized already, then apply | ||
/// the provided update. | ||
/// | ||
/// The caller must call [`QueuedEvents::raise`] on the return value. | ||
/// | ||
/// This method may be safely called on any thread, but refer to | ||
/// [`QueuedEvents::raise`] for restrictions on the context in which | ||
/// it should be called. | ||
pub fn update(&self, update: TreeUpdate) -> QueuedEvents { | ||
let context = Lazy::force(&self.context); | ||
context.update(update) | ||
} | ||
|
||
/// If and only if the tree has been initialized, call the provided function | ||
/// and apply the resulting update. | ||
/// | ||
/// If a [`QueuedEvents`] instance is returned, the caller must call | ||
/// [`QueuedEvents::raise`] on it. | ||
/// | ||
/// This method may be safely called on any thread, but refer to | ||
/// [`QueuedEvents::raise`] for restrictions on the context in which | ||
/// it should be called. | ||
pub fn update_if_active(&self, updater: impl FnOnce() -> TreeUpdate) -> Option<QueuedEvents> { | ||
Lazy::get(&self.context).map(|context| context.update(updater())) | ||
} | ||
|
||
pub fn view_children(&self) -> *mut NSArray<NSObject> { | ||
let context = Lazy::force(&self.context); | ||
let state = context.tree.read(); | ||
let node = state.root(); | ||
let platform_nodes = if filter(&node) == FilterResult::Include { | ||
vec![Id::into_super(Id::into_super( | ||
context.get_or_create_platform_node(node.id()), | ||
))] | ||
} else { | ||
node.filtered_children(filter) | ||
.map(|node| { | ||
Id::into_super(Id::into_super( | ||
context.get_or_create_platform_node(node.id()), | ||
)) | ||
}) | ||
.collect::<Vec<Id<NSObject, Shared>>>() | ||
}; | ||
let array = NSArray::from_vec(platform_nodes); | ||
Id::autorelease_return(array) | ||
} | ||
|
||
pub fn focus(&self) -> *mut NSObject { | ||
let context = Lazy::force(&self.context); | ||
let state = context.tree.read(); | ||
if let Some(node) = state.focus() { | ||
if filter(&node) == FilterResult::Include { | ||
return Id::autorelease_return(context.get_or_create_platform_node(node.id())) | ||
as *mut _; | ||
} | ||
} | ||
null_mut() | ||
} | ||
|
||
pub fn hit_test(&self, point: NSPoint) -> *mut NSObject { | ||
let context = Lazy::force(&self.context); | ||
let view = match context.view.load() { | ||
Some(view) => view, | ||
None => { | ||
return null_mut(); | ||
} | ||
}; | ||
|
||
let window = view.window().unwrap(); | ||
let point = window.convert_point_from_screen(point); | ||
let point = view.convert_point_from_view(point, None); | ||
let view_bounds = view.bounds(); | ||
let point = Point::new(point.x, view_bounds.size.height - point.y); | ||
|
||
let state = context.tree.read(); | ||
let root = state.root(); | ||
let point = root.transform().inverse() * point; | ||
if let Some(node) = root.node_at_point(point, &filter) { | ||
return Id::autorelease_return(context.get_or_create_platform_node(node.id())) | ||
as *mut _; | ||
} | ||
null_mut() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
// Copyright 2022 The AccessKit Authors. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0 (found in | ||
// the LICENSE-APACHE file) or the MIT license (found in | ||
// the LICENSE-MIT file), at your option. | ||
|
||
use objc2::foundation::NSString; | ||
|
||
#[link(name = "AppKit", kind = "framework")] | ||
extern "C" { | ||
// Notifications | ||
pub(crate) static NSAccessibilityUIElementDestroyedNotification: &'static NSString; | ||
pub(crate) static NSAccessibilityFocusedUIElementChangedNotification: &'static NSString; | ||
pub(crate) static NSAccessibilityTitleChangedNotification: &'static NSString; | ||
pub(crate) static NSAccessibilityValueChangedNotification: &'static NSString; | ||
|
||
// Roles | ||
pub(crate) static NSAccessibilityButtonRole: &'static NSString; | ||
pub(crate) static NSAccessibilityCheckBoxRole: &'static NSString; | ||
pub(crate) static NSAccessibilityCellRole: &'static NSString; | ||
pub(crate) static NSAccessibilityColorWellRole: &'static NSString; | ||
pub(crate) static NSAccessibilityColumnRole: &'static NSString; | ||
pub(crate) static NSAccessibilityComboBoxRole: &'static NSString; | ||
pub(crate) static NSAccessibilityGroupRole: &'static NSString; | ||
pub(crate) static NSAccessibilityImageRole: &'static NSString; | ||
pub(crate) static NSAccessibilityIncrementorRole: &'static NSString; | ||
pub(crate) static NSAccessibilityLevelIndicatorRole: &'static NSString; | ||
pub(crate) static NSAccessibilityLinkRole: &'static NSString; | ||
pub(crate) static NSAccessibilityListRole: &'static NSString; | ||
pub(crate) static NSAccessibilityMenuRole: &'static NSString; | ||
pub(crate) static NSAccessibilityMenuBarRole: &'static NSString; | ||
pub(crate) static NSAccessibilityMenuItemRole: &'static NSString; | ||
pub(crate) static NSAccessibilityOutlineRole: &'static NSString; | ||
pub(crate) static NSAccessibilityPopUpButtonRole: &'static NSString; | ||
pub(crate) static NSAccessibilityProgressIndicatorRole: &'static NSString; | ||
pub(crate) static NSAccessibilityRadioButtonRole: &'static NSString; | ||
pub(crate) static NSAccessibilityRadioGroupRole: &'static NSString; | ||
pub(crate) static NSAccessibilityRowRole: &'static NSString; | ||
pub(crate) static NSAccessibilityScrollBarRole: &'static NSString; | ||
pub(crate) static NSAccessibilitySliderRole: &'static NSString; | ||
pub(crate) static NSAccessibilitySplitterRole: &'static NSString; | ||
pub(crate) static NSAccessibilityStaticTextRole: &'static NSString; | ||
pub(crate) static NSAccessibilityTabGroupRole: &'static NSString; | ||
pub(crate) static NSAccessibilityTableRole: &'static NSString; | ||
pub(crate) static NSAccessibilityTextFieldRole: &'static NSString; | ||
pub(crate) static NSAccessibilityToolbarRole: &'static NSString; | ||
pub(crate) static NSAccessibilityUnknownRole: &'static NSString; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Copyright 2022 The AccessKit Authors. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0 (found in | ||
// the LICENSE-APACHE file) or the MIT license (found in | ||
// the LICENSE-MIT file), at your option. | ||
|
||
use objc2::{extern_class, foundation::NSObject, ClassType}; | ||
|
||
extern_class!( | ||
#[derive(Debug)] | ||
pub struct NSAccessibilityElement; | ||
|
||
unsafe impl ClassType for NSAccessibilityElement { | ||
type Super = NSObject; | ||
} | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright 2022 The AccessKit Authors. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0 (found in | ||
// the LICENSE-APACHE file) or the MIT license (found in | ||
// the LICENSE-MIT file), at your option. | ||
|
||
use objc2::foundation::{NSObject, NSString}; | ||
|
||
#[link(name = "AppKit", kind = "framework")] | ||
extern "C" { | ||
pub(crate) fn NSAccessibilityPostNotification(element: &NSObject, notification: &NSString); | ||
} |
Oops, something went wrong.