accesskit_atspi_common/
util.rsuse accesskit::{Point, Rect};
use accesskit_consumer::{Node, TextPosition, TextRange};
use atspi_common::{CoordType, Granularity};
use crate::Error;
#[derive(Clone, Copy, Default)]
pub struct WindowBounds {
pub outer: Rect,
pub inner: Rect,
}
impl WindowBounds {
pub fn new(outer: Rect, inner: Rect) -> Self {
Self { outer, inner }
}
pub(crate) fn accesskit_point_to_atspi_point(
&self,
point: Point,
parent: Option<Node>,
coord_type: CoordType,
) -> Point {
let origin = self.origin(parent, coord_type);
Point::new(origin.x + point.x, origin.y + point.y)
}
pub(crate) fn atspi_point_to_accesskit_point(
&self,
point: Point,
parent: Option<Node>,
coord_type: CoordType,
) -> Point {
let origin = self.origin(parent, coord_type);
Point::new(point.x - origin.x, point.y - origin.y)
}
fn origin(&self, parent: Option<Node>, coord_type: CoordType) -> Point {
match coord_type {
CoordType::Screen => self.inner.origin(),
CoordType::Window => Point::ZERO,
CoordType::Parent => {
if let Some(parent) = parent {
let parent_origin = parent.bounding_box().unwrap_or_default().origin();
Point::new(-parent_origin.x, -parent_origin.y)
} else {
self.inner.origin()
}
}
}
}
}
pub(crate) fn text_position_from_offset<'a>(
node: &'a Node,
offset: i32,
) -> Option<TextPosition<'a>> {
let index = offset.try_into().ok()?;
node.text_position_from_global_usv_index(index)
}
pub(crate) fn text_range_from_offset<'a>(
node: &'a Node,
offset: i32,
granularity: Granularity,
) -> Result<TextRange<'a>, Error> {
let start_offset = text_position_from_offset(node, offset).ok_or(Error::IndexOutOfRange)?;
let start = match granularity {
Granularity::Char => start_offset,
Granularity::Line if start_offset.is_line_start() => start_offset,
Granularity::Line => start_offset.backward_to_line_start(),
Granularity::Paragraph if start_offset.is_paragraph_start() => start_offset,
Granularity::Paragraph => start_offset.backward_to_paragraph_start(),
Granularity::Sentence => return Err(Error::UnsupportedTextGranularity),
Granularity::Word if start_offset.is_word_start() => start_offset,
Granularity::Word => start_offset.backward_to_word_start(),
};
let end = match granularity {
Granularity::Char if start_offset.is_document_end() => start_offset,
Granularity::Char => start.forward_to_character_end(),
Granularity::Line => start.forward_to_line_end(),
Granularity::Paragraph => start.forward_to_paragraph_end(),
Granularity::Sentence => return Err(Error::UnsupportedTextGranularity),
Granularity::Word => start.forward_to_word_end(),
};
let mut range = start.to_degenerate_range();
range.set_end(end);
Ok(range)
}
pub(crate) fn text_range_from_offsets<'a>(
node: &'a Node,
start_offset: i32,
end_offset: i32,
) -> Option<TextRange<'a>> {
let start = text_position_from_offset(node, start_offset)?;
let end = if end_offset == -1 {
node.document_range().end()
} else {
text_position_from_offset(node, end_offset)?
};
let mut range = start.to_degenerate_range();
range.set_end(end);
Some(range)
}
pub(crate) fn text_range_bounds_from_offsets(
node: &Node,
start_offset: i32,
end_offset: i32,
) -> Option<Rect> {
text_range_from_offsets(node, start_offset, end_offset)?
.bounding_boxes()
.into_iter()
.reduce(|rect1, rect2| rect1.union(rect2))
}