use std::rc::Rc;
use cranpose_core::NodeId;
use cranpose_foundation::{SemanticsCustomAction, SemanticsWidgetRole};
use super::{GeometryRect, LayoutState, Point, SemanticsDetails, Size};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SemanticsCallback {
pub(super) node_id: NodeId,
}
impl SemanticsCallback {
pub fn new(node_id: NodeId) -> Self {
Self { node_id }
}
pub fn node_id(&self) -> NodeId {
self.node_id
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SemanticsAction {
Click {
handler: SemanticsCallback,
},
}
#[derive(Clone, Debug)]
pub struct SemanticsText(pub(super) Rc<crate::text::AnnotatedString>);
impl SemanticsText {
pub fn as_str(&self) -> &str {
&self.0.text
}
}
impl PartialEq for SemanticsText {
fn eq(&self, other: &Self) -> bool {
self.as_str() == other.as_str()
}
}
impl Eq for SemanticsText {}
impl From<&str> for SemanticsText {
fn from(text: &str) -> Self {
Self(Rc::new(crate::text::AnnotatedString::from(text)))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SemanticsRole {
Layout,
Subcompose,
Text {
value: SemanticsText,
},
Spacer,
Button,
Unknown,
}
#[derive(Clone, Debug, PartialEq)]
pub struct SemanticsNode {
pub node_id: NodeId,
pub bounds: GeometryRect,
pub placement: SemanticsPlacement,
pub node_generation: u32,
pub role: SemanticsRole,
pub widget_role: Option<SemanticsWidgetRole>,
pub actions: Vec<SemanticsAction>,
pub children: Vec<Self>,
pub description: Option<String>,
pub on_click: Option<SemanticsCustomAction>,
pub selected: Option<bool>,
pub toggled: Option<cranpose_foundation::ToggleableState>,
pub enabled: bool,
pub hidden: bool,
pub merge_descendants: bool,
pub traversal_index: f32,
pub text: Option<String>,
pub focusable: bool,
pub focused: bool,
pub details: Option<Box<SemanticsDetails>>,
}
impl Default for SemanticsNode {
fn default() -> Self {
Self {
node_id: NodeId::default(),
bounds: GeometryRect::EMPTY,
placement: SemanticsPlacement::default(),
node_generation: 0,
role: SemanticsRole::Unknown,
widget_role: None,
actions: Vec::new(),
children: Vec::new(),
description: None,
on_click: None,
selected: None,
toggled: None,
enabled: true,
hidden: false,
merge_descendants: false,
traversal_index: 0.0,
text: None,
focusable: false,
focused: false,
details: None,
}
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct SemanticsPlacement {
pub(super) position: Point,
pub(super) content_offset: Point,
}
impl SemanticsPlacement {
pub(super) fn of(state: &LayoutState) -> Self {
Self {
position: state.position(),
content_offset: state.content_offset(),
}
}
pub(super) fn place(self, origin: Point) -> (Point, Point) {
let top_left = Point {
x: origin.x + self.position.x,
y: origin.y + self.position.y,
};
let content = Point {
x: top_left.x + self.content_offset.x,
y: top_left.y + self.content_offset.y,
};
(top_left, content)
}
}
#[derive(Clone, Debug)]
pub struct SemanticsTree {
pub(super) full: SemanticsNode,
pub(super) modal: Option<Vec<usize>>,
pub(super) tracking: super::semantics_update::SemanticsTracking,
}
impl PartialEq for SemanticsTree {
fn eq(&self, other: &Self) -> bool {
self.full == other.full && self.modal == other.modal
}
}
impl SemanticsTree {
pub(super) fn new(full: SemanticsNode) -> Self {
let modal = top_modal_path(&full);
Self {
full,
modal,
tracking: super::semantics_update::SemanticsTracking::default(),
}
}
pub fn root(&self) -> &SemanticsNode {
self.modal.as_deref().map_or(&self.full, |path| {
path.iter()
.try_fold(&self.full, |node, &index| node.children.get(index))
.unwrap_or(&self.full)
})
}
}
pub(super) fn modal_takes_space(size: Size) -> bool {
size.width > 0.0 && size.height > 0.0 && size.width.is_finite() && size.height.is_finite()
}
pub(super) fn top_modal_path(root: &SemanticsNode) -> Option<Vec<usize>> {
pub(super) fn search(node: &SemanticsNode, path: &mut Vec<usize>) -> bool {
if node.hidden {
return false;
}
for (index, child) in node.children.iter().enumerate().rev() {
path.push(index);
if search(child, path) {
return true;
}
path.pop();
}
node.details().is_modal
}
let mut path = Vec::new();
search(root, &mut path).then_some(path)
}