Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
557 changes: 199 additions & 358 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,17 @@ doc = false
extension-module = ["pyo3/extension-module"]

[dependencies]
accesskit = { version = "0.17.1", features = ["pyo3"] }
pyo3 = { version = "0.20", features = ["abi3-py38", "multiple-pymethods"] }
accesskit = { version = "0.21.0", features = ["pyo3"] }
pyo3 = { version = "0.24", features = ["abi3-py38", "multiple-pymethods"] }

[target.'cfg(target_os = "windows")'.dependencies]
accesskit_windows = { version = "0.24.1" }
accesskit_windows = { version = "0.29.0" }

[target.'cfg(target_os = "macos")'.dependencies]
accesskit_macos = { version = "0.18.1" }
accesskit_macos = { version = "0.22.0" }

[target.'cfg(any(target_os = "linux", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))'.dependencies]
accesskit_unix = { version = "0.13.1" }
accesskit_unix = { version = "0.17.0" }

[profile.release]
lto = true
Expand Down
1 change: 0 additions & 1 deletion examples/pygame/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ def build_initial_tree(self):
button_2 = build_button(BUTTON_2_ID, "Button 2")
result = accesskit.TreeUpdate(self.focus)
tree = accesskit.Tree(WINDOW_ID)
tree.app_name = "Hello world"
result.tree = tree
result.nodes.append((WINDOW_ID, root))
result.nodes.append((BUTTON_1_ID, button_1))
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ classifiers = [
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: User Interfaces"
]
dynamic = ["version"]

[project.urls]
Homepage = "https://github.com/AccessKit/accesskit-python"
144 changes: 87 additions & 57 deletions src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@
// the LICENSE-APACHE file) or the MIT license (found in
// the LICENSE-MIT file), at your option.

use crate::{Point, Rect};
use pyo3::{prelude::*, types::PyList};
use pyo3::{
prelude::*,
types::{PyList, PyTuple},
IntoPyObjectExt,
};

use crate::Point;

#[derive(Clone)]
#[pyclass(module = "accesskit")]
Expand Down Expand Up @@ -59,6 +64,22 @@ impl Node {
pub fn clear_actions(&mut self) {
self.inner_mut().clear_actions()
}

pub fn child_supports_action(&self, action: accesskit::Action) -> bool {
self.inner().child_supports_action(action)
}

pub fn add_child_action(&mut self, action: accesskit::Action) {
self.inner_mut().add_child_action(action)
}

pub fn remove_child_action(&mut self, action: accesskit::Action) {
self.inner_mut().remove_child_action(action)
}

pub fn clear_child_actions(&mut self) {
self.inner_mut().clear_child_actions();
}
}

pub type NodeId = u64;
Expand Down Expand Up @@ -151,7 +172,6 @@ impl From<accesskit::TextPosition> for TextPosition {
}
}

#[derive(Clone)]
#[pyclass(get_all, set_all, module = "accesskit")]
pub struct TextSelection {
pub anchor: Py<TextPosition>,
Expand All @@ -178,15 +198,20 @@ impl From<&accesskit::TextSelection> for TextSelection {
impl From<TextSelection> for accesskit::TextSelection {
fn from(selection: TextSelection) -> Self {
Python::with_gil(|py| accesskit::TextSelection {
anchor: selection.anchor.as_ref(py).borrow().0,
focus: selection.focus.as_ref(py).borrow().0,
anchor: selection.anchor.bind(py).borrow().0,
focus: selection.focus.bind(py).borrow().0,
})
}
}

impl From<TextSelection> for Box<accesskit::TextSelection> {
fn from(selection: TextSelection) -> Self {
Box::new(selection.into())
impl From<&TextSelection> for Box<accesskit::TextSelection> {
fn from(selection: &TextSelection) -> Self {
Python::with_gil(|py| {
Box::new(accesskit::TextSelection {
anchor: selection.anchor.borrow(py).0,
focus: selection.focus.borrow(py).0,
})
})
}
}

Expand Down Expand Up @@ -293,14 +318,14 @@ macro_rules! vec_property_methods {
$(#[pymethods]
impl Node {
#[getter]
pub fn $getter(&self, py: Python) -> Py<PyList> {
let values = self.inner().$getter().iter().cloned().map(<$py_item_type>::from).map(|i| i.into_py(py));
PyList::new(py, values).into()
pub fn $getter(&self, py: Python) -> PyResult<Py<PyList>> {
let values = self.inner().$getter().iter().cloned().map(<$py_item_type>::from);
Ok(PyList::new(py, values)?.unbind())
}
pub fn $setter(&mut self, values: &PyList) {
pub fn $setter(&mut self, values: &Bound<'_, PyList>) {
let values = values
.iter()
.map(PyAny::extract::<$py_item_type>)
.map(|item| item.extract::<$py_item_type>())
.filter_map(PyResult::ok)
.map(<$accesskit_item_type>::from)
.collect::<Vec<$accesskit_item_type>>();
Expand Down Expand Up @@ -404,7 +429,6 @@ macro_rules! unique_enum_property_methods {

flag_methods! {
(is_hidden, set_hidden, clear_hidden),
(is_linked, set_linked, clear_linked),
(is_multiselectable, set_multiselectable, clear_multiselectable),
(is_required, set_required, clear_required),
(is_visited, set_visited, clear_visited),
Expand Down Expand Up @@ -540,7 +564,7 @@ unique_enum_property_methods! {
property_methods! {
(transform, option_getter, Option<crate::Affine>, set_transform, simple_setter, crate::Affine, clear_transform),
(bounds, option_getter, Option<crate::Rect>, set_bounds, converting_setter, crate::Rect, clear_bounds),
(text_selection, option_getter, Option<TextSelection>, set_text_selection, simple_setter, TextSelection, clear_text_selection)
(text_selection, option_getter, Option<TextSelection>, set_text_selection, simple_setter, &TextSelection, clear_text_selection)
}

vec_property_methods! {
Expand All @@ -551,7 +575,6 @@ vec_property_methods! {
#[pyclass(module = "accesskit", get_all, set_all)]
pub struct Tree {
pub root: NodeId,
pub app_name: Option<String>,
pub toolkit_name: Option<String>,
pub toolkit_version: Option<String>,
}
Expand All @@ -562,7 +585,6 @@ impl Tree {
pub fn new(root: NodeId) -> Self {
Self {
root,
app_name: None,
toolkit_name: None,
toolkit_version: None,
}
Expand All @@ -573,14 +595,12 @@ impl From<Tree> for accesskit::Tree {
fn from(tree: Tree) -> Self {
Self {
root: tree.root.into(),
app_name: tree.app_name,
toolkit_name: tree.toolkit_name,
toolkit_version: tree.toolkit_version,
}
}
}

#[derive(Clone)]
#[pyclass(module = "accesskit", get_all, set_all)]
pub struct TreeUpdate {
pub nodes: Py<PyList>,
Expand All @@ -600,22 +620,21 @@ impl TreeUpdate {
}
}

impl From<TreeUpdate> for accesskit::TreeUpdate {
fn from(update: TreeUpdate) -> Self {
impl From<&TreeUpdate> for accesskit::TreeUpdate {
fn from(update: &TreeUpdate) -> Self {
Python::with_gil(|py| Self {
nodes: update
.nodes
.as_ref(py)
.bind(py)
.iter()
.map(PyAny::extract::<(NodeId, Node)>)
.map(|n| n.extract::<(NodeId, Node)>())
.filter_map(Result::ok)
.map(|(id, node)| (id.into(), node.into()))
.collect(),
tree: update.tree.map(|tree| {
let tree = tree.as_ref(py).borrow();
tree: update.tree.as_ref().map(|tree| {
let tree = tree.bind(py).borrow();
accesskit::Tree {
root: tree.root.into(),
app_name: tree.app_name.clone(),
toolkit_name: tree.toolkit_name.clone(),
toolkit_version: tree.toolkit_version.clone(),
}
Expand All @@ -625,13 +644,14 @@ impl From<TreeUpdate> for accesskit::TreeUpdate {
}
}

#[derive(Clone)]
#[pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE")]
#[derive(PartialEq)]
#[pyclass(module = "accesskit", rename_all = "SCREAMING_SNAKE_CASE", eq, eq_int)]
pub enum ActionDataKind {
CustomAction,
Value,
NumericValue,
ScrollTargetRect,
ScrollUnit,
ScrollHint,
ScrollToPoint,
SetScrollOffset,
SetTextSelection,
Expand All @@ -641,38 +661,48 @@ pub enum ActionDataKind {
pub struct ActionRequest {
pub action: accesskit::Action,
pub target: NodeId,
pub data: Option<(ActionDataKind, Py<PyAny>)>,
pub data: Option<Py<PyTuple>>,
}

impl From<accesskit::ActionRequest> for ActionRequest {
fn from(request: accesskit::ActionRequest) -> Self {
Python::with_gil(|py| Self {
action: request.action,
target: request.target.into(),
data: request.data.map(|data| match data {
accesskit::ActionData::CustomAction(action) => {
(ActionDataKind::CustomAction, action.into_py(py))
data: request.data.map(|data| {
match data {
accesskit::ActionData::CustomAction(action) => (
ActionDataKind::CustomAction,
action.into_py_any(py).unwrap(),
),
accesskit::ActionData::Value(value) => {
(ActionDataKind::Value, value.into_py_any(py).unwrap())
}
accesskit::ActionData::NumericValue(value) => {
(ActionDataKind::NumericValue, value.into_py_any(py).unwrap())
}
accesskit::ActionData::ScrollUnit(unit) => {
(ActionDataKind::ScrollUnit, unit.into_py_any(py).unwrap())
}
accesskit::ActionData::ScrollHint(hint) => {
(ActionDataKind::ScrollHint, hint.into_py_any(py).unwrap())
}
accesskit::ActionData::ScrollToPoint(point) => (
ActionDataKind::ScrollToPoint,
Point::from(point).into_py_any(py).unwrap(),
),
accesskit::ActionData::SetScrollOffset(point) => (
ActionDataKind::SetScrollOffset,
Point::from(point).into_py_any(py).unwrap(),
),
accesskit::ActionData::SetTextSelection(selection) => (
ActionDataKind::SetTextSelection,
TextSelection::from(&selection).into_py_any(py).unwrap(),
),
}
accesskit::ActionData::Value(value) => (ActionDataKind::Value, value.into_py(py)),
accesskit::ActionData::NumericValue(value) => {
(ActionDataKind::NumericValue, value.into_py(py))
}
accesskit::ActionData::ScrollTargetRect(rect) => (
ActionDataKind::ScrollTargetRect,
Rect::from(rect).into_py(py),
),
accesskit::ActionData::ScrollToPoint(point) => (
ActionDataKind::ScrollToPoint,
Point::from(point).into_py(py),
),
accesskit::ActionData::SetScrollOffset(point) => (
ActionDataKind::SetScrollOffset,
Point::from(point).into_py(py),
),
accesskit::ActionData::SetTextSelection(selection) => (
ActionDataKind::SetTextSelection,
TextSelection::from(&selection).into_py(py),
),
.into_pyobject(py)
.unwrap()
.unbind()
}),
})
}
Expand All @@ -690,9 +720,9 @@ impl accesskit::ActivationHandler for LocalPythonActivationHandler<'_> {
fn request_initial_tree(&mut self) -> Option<accesskit::TreeUpdate> {
let result = self.handler.call0(self.py).unwrap();
result
.extract::<Option<TreeUpdate>>(self.py)
.extract::<Option<PyRef<TreeUpdate>>>(self.py)
.unwrap()
.map(Into::into)
.map(|tree| (&*tree).into())
}
}

Expand All @@ -703,9 +733,9 @@ impl accesskit::ActivationHandler for PythonActivationHandler {
Python::with_gil(|py| {
let result = self.0.call0(py).unwrap();
result
.extract::<Option<TreeUpdate>>(py)
.extract::<Option<PyRef<TreeUpdate>>>(py)
.unwrap()
.map(Into::into)
.map(|tree| (&*tree).into())
})
}
}
Expand Down
Loading
Loading