#[cfg(feature = "geo")]
use geo_types::{Coord, Rect as GeoRect};
use crate::{Size, Viewport};
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct BoundingBox {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64,
}
impl BoundingBox {
#[tracing::instrument(level = "trace")]
pub fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
Self {
x,
y,
width,
height,
}
}
#[tracing::instrument(level = "trace", skip(node))]
pub fn from_node(node: &accesskit::Node) -> Option<Self> {
let bounds = node.bounds()?;
Some(Self {
x: bounds.x0,
y: bounds.y0,
width: bounds.x1 - bounds.x0,
height: bounds.y1 - bounds.y0,
})
}
pub fn right(&self) -> f64 {
self.x + self.width
}
pub fn bottom(&self) -> f64 {
self.y + self.height
}
#[tracing::instrument(level = "trace")]
pub fn within_viewport(&self, viewport: &Viewport) -> bool {
self.x >= 0.0
&& self.y >= 0.0
&& self.right() <= f64::from(viewport.width)
&& self.bottom() <= f64::from(viewport.height)
}
pub fn meets_touch_target(&self) -> bool {
self.width >= 44.0 && self.height >= 44.0
}
pub fn to_size(&self) -> Size {
Size::new(self.width as u32, self.height as u32)
}
#[cfg(feature = "geo")]
#[tracing::instrument(level = "trace")]
pub fn to_geo_rect(&self) -> GeoRect {
GeoRect::new(
Coord {
x: self.x,
y: self.y,
},
Coord {
x: self.right(),
y: self.bottom(),
},
)
}
}
#[derive(Debug, Clone)]
pub struct LayoutContext {
pub viewport: Viewport,
pub bounds: std::collections::BTreeMap<accesskit::NodeId, BoundingBox>,
}
impl LayoutContext {
#[tracing::instrument(level = "debug", skip(bounds), fields(num_bounds = bounds.len()))]
pub fn new(
viewport: Viewport,
bounds: std::collections::BTreeMap<accesskit::NodeId, BoundingBox>,
) -> Self {
Self { viewport, bounds }
}
pub fn get_bounds(&self, node_id: &accesskit::NodeId) -> Option<&BoundingBox> {
self.bounds.get(node_id)
}
#[tracing::instrument(level = "trace", skip(self))]
pub fn is_within_viewport(&self, node_id: &accesskit::NodeId) -> Option<bool> {
self.bounds
.get(node_id)
.map(|bb| bb.within_viewport(&self.viewport))
}
#[cfg(feature = "geo")]
#[tracing::instrument(level = "trace", skip(self))]
pub fn contains(&self, outer: &accesskit::NodeId, inner: &accesskit::NodeId) -> Option<bool> {
use geo::Contains;
let outer_rect = self.bounds.get(outer)?.to_geo_rect();
let inner_rect = self.bounds.get(inner)?.to_geo_rect();
Some(outer_rect.contains(&inner_rect))
}
}
#[cfg(feature = "geo")]
impl BoundingBox {
pub fn to_geo_polygon(&self) -> geo_types::Polygon {
use geo_types::{LineString, Polygon};
let coords = vec![
Coord {
x: self.x,
y: self.y,
},
Coord {
x: self.right(),
y: self.y,
},
Coord {
x: self.right(),
y: self.bottom(),
},
Coord {
x: self.x,
y: self.bottom(),
},
Coord {
x: self.x,
y: self.y,
},
];
Polygon::new(LineString::from(coords), vec![])
}
}