cotis-accessibility 0.1.0-alpha

AccessKit accessibility bridge traits for Cotis
Documentation
//! Cotis-facing accessibility API backed by [AccessKit](https://github.com/AccessKit/accesskit).
//!
//! This crate defines the contract between Cotis elements and platform accessibility
//! adapters. Applications or renderers implement [`AccessKitProvider`] to register
//! nodes per [`ElementId`], receive [`ActionRequest`]s, and push
//! [`TreeUpdate`]s to the platform via a callback.
//!
//! This is a **trait-only** extension point: no platform adapter ships in this
//! workspace. Consumers also depend on `accesskit` directly for
//! [`Role`], [`Action`], and related types.
//!
//! # Suggested frame order
//!
//! 1. Run layout so element bounds are current.
//! 2. Call [`AccessKitProvider::new_accessibility_node`] for each accessible element.
//! 3. Poll [`AccessKitProvider::get_action_requests`] and handle user actions.
//! 4. The platform adapter invokes the routine installed by
//!    [`AccessKitProvider::set_tree_update_routine`] with tree updates.
//!
//! # Examples
//!
//! ```rust,ignore
//! use cotis_accessibility::{AccessibilityConfig, AccessKitProvider};
//! use accesskit::Role;
//! use cotis::utils::ElementId;
//!
//! // Implement AccessKitProvider on your renderer or app shell, then per frame:
//! // provider.new_accessibility_node(id, config);
//! // for request in provider.get_action_requests(id) { ... }
//! // Tree updates flow through set_tree_update_routine callback.
//! ```

use accesskit::{Action, ActionRequest, Role, TreeUpdate};
use cotis::utils::ElementId;
use cotis::utils::OwnedOrRef;
use cotis_utils::math::BoundingBox;

/// Metadata for registering a Cotis element as an AccessKit node.
///
/// Use [`OwnedOrRef`] fields to pass borrowed strings and bounds per frame or
/// owned values that outlive the registration call.
///
/// # Examples
///
/// ```rust,ignore
/// use accesskit::{Action, Role};
/// use cotis::utils::{ElementId, OwnedOrRef};
/// use cotis_accessibility::AccessibilityConfig;
/// use cotis_utils::math::BoundingBox;
///
/// let config = AccessibilityConfig {
///     parent: None,
///     role: Role::Button,
///     label: OwnedOrRef::Borrowed("Submit"),
///     description: OwnedOrRef::Borrowed(""),
///     actions: OwnedOrRef::Borrowed(&vec![Action::Click]),
///     bounding_box: OwnedOrRef::Borrowed(&BoundingBox {
///         x: 0.0,
///         y: 0.0,
///         width: 100.0,
///         height: 40.0,
///     }),
/// };
/// ```
pub struct AccessibilityConfig<'config> {
    /// Parent element in the accessibility tree, or `None` for a root node.
    pub parent: Option<ElementId>,
    /// Accessibility role (button, text field, etc.).
    pub role: Role,
    /// Primary accessible name for the element.
    pub label: OwnedOrRef<'config, str>,
    /// Secondary description or help text.
    pub description: OwnedOrRef<'config, str>,
    /// Actions the element supports (click, focus, etc.).
    pub actions: OwnedOrRef<'config, Vec<Action>>,
    /// Screen-space bounds in pixels.
    pub bounding_box: OwnedOrRef<'config, BoundingBox>,
}

/// Bridge trait implemented by renderers or app shells for AccessKit integration.
///
/// Platform adapters implement this trait to connect Cotis element IDs to
/// AccessKit's tree and action-request APIs.
pub trait AccessKitProvider {
    /// Registers or updates the accessibility node for `id`.
    ///
    /// Call when element metadata or bounds change (typically after layout).
    fn new_accessibility_node(&mut self, id: ElementId, config: AccessibilityConfig<'_>);

    /// Returns pending accessibility action requests for the given element.
    ///
    /// # Note
    ///
    /// The `id` parameter's scoping intent is unspecified. AccessKit adapters
    /// typically expose a global action-request queue; implementors should
    /// document how `id` filters or relates to that queue.
    fn get_action_requests(&mut self, id: ElementId) -> impl Iterator<Item = ActionRequest>;

    /// Installs a callback invoked when the accessibility tree should be updated.
    ///
    /// The platform adapter calls `fun` with [`TreeUpdate`] values to push
    /// changes to the native accessibility layer.
    fn set_tree_update_routine(&mut self, fun: Box<dyn Fn(TreeUpdate)>);
}