pub mod base;
pub mod domain_write;
pub mod fixed;
pub mod flex;
pub mod grid;
pub mod leaf;
pub mod list;
pub mod root;
pub mod text;
use dyn_clone::DynClone;
use guillotiere::euclid::{Point2D, Vector2D};
use wide::f32x4;
use crate::color::sRGB32;
use crate::render::Renderable;
use crate::render::compositor::CompositorView;
use crate::{
Error, PxDim, PxLimits, PxPoint, PxRect, RelLimits, SourceID, UNSIZED_AXIS, URect, rtree,
};
use derive_where::derive_where;
use std::marker::PhantomData;
use std::rc::{Rc, Weak};
use std::sync::Arc;
pub trait Layout<Props: ?Sized>: DynClone {
fn get_props(&self) -> &Props;
fn stage<'a>(
&self,
area: PxRect,
limits: PxLimits,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a>;
}
dyn_clone::clone_trait_object!(<Imposed> Layout<Imposed> where Imposed:?Sized);
impl<U: ?Sized, T> Layout<U> for Box<dyn Layout<T>>
where
for<'a> &'a T: Into<&'a U>,
{
fn get_props(&self) -> &U {
use std::ops::Deref;
Box::deref(self).get_props().into()
}
fn stage<'a>(
&self,
area: PxRect,
limits: PxLimits,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a> {
use std::ops::Deref;
Box::deref(self).stage(area, limits, window)
}
}
impl<U: ?Sized, T> Layout<U> for &dyn Layout<T>
where
for<'a> &'a T: Into<&'a U>,
{
fn get_props(&self) -> &U {
(*self).get_props().into()
}
fn stage<'a>(
&self,
area: PxRect,
limits: PxLimits,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a> {
(*self).stage(area, limits, window)
}
}
pub trait Desc {
type Props: ?Sized;
type Child: ?Sized;
type Children: Clone;
fn stage<'a>(
props: &Self::Props,
outer_area: PxRect,
limits: PxLimits,
children: &Self::Children,
id: std::sync::Weak<SourceID>,
renderable: Option<Rc<dyn Renderable>>,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a>;
}
#[derive_where(Clone)]
pub struct Node<T, D: Desc + ?Sized> {
pub props: Rc<T>,
pub id: std::sync::Weak<SourceID>,
pub children: D::Children,
pub renderable: Option<Rc<dyn Renderable>>,
pub layer: Option<(sRGB32, f32)>,
}
impl<T, D: Desc + ?Sized> Layout<T> for Node<T, D>
where
for<'a> &'a T: Into<&'a D::Props>,
{
fn get_props(&self) -> &T {
self.props.as_ref()
}
fn stage<'a>(
&self,
area: PxRect,
limits: PxLimits,
window: &mut crate::component::window::WindowState,
) -> Box<dyn Staged + 'a> {
let mut staged = D::stage(
self.props.as_ref().into(),
area,
limits,
&self.children,
self.id.clone(),
self.renderable.as_ref().map(|x| x.clone()),
window,
);
if let Some((color, rotation)) = self.layer {
window.driver.shared.create_layer(
&window.driver.device,
self.id.upgrade().unwrap(),
staged.get_area().to_untyped(),
None,
color,
rotation,
false,
);
staged.set_layer(self.id.clone());
}
staged
}
}
pub trait Staged: DynClone {
fn render(
&self,
parent_pos: PxPoint,
driver: &crate::graphics::Driver,
compositor: &mut CompositorView<'_>,
dependents: &mut Vec<std::sync::Weak<SourceID>>,
) -> Result<(), Error>;
fn get_rtree(&self) -> Weak<rtree::Node>;
fn get_area(&self) -> PxRect;
fn set_layer(&mut self, _id: std::sync::Weak<SourceID>) {
panic!("This staged object doesn't support layers!");
}
}
dyn_clone::clone_trait_object!(Staged);
#[derive(Clone)]
pub(crate) struct Concrete {
renderable: Option<Rc<dyn Renderable>>,
area: PxRect,
rtree: Rc<rtree::Node>,
children: im::Vector<Option<Box<dyn Staged>>>,
layer: Option<std::sync::Weak<SourceID>>,
}
impl Concrete {
pub fn new(
renderable: Option<Rc<dyn Renderable>>,
area: PxRect,
rtree: Rc<rtree::Node>,
children: im::Vector<Option<Box<dyn Staged>>>,
) -> Self {
debug_assert!(area.v.is_finite().all());
let (unsized_x, unsized_y) = check_unsized_abs(area.bottomright());
assert!(
!unsized_x && !unsized_y,
"concrete area must always be sized!: {area:?}",
);
Self {
renderable,
area,
rtree,
children,
layer: None,
}
}
fn render_self(
&self,
parent_pos: PxPoint,
driver: &crate::graphics::Driver,
compositor: &mut CompositorView<'_>,
) -> Result<(), Error> {
debug_assert!(self.area.v.is_finite().all());
if let Some(r) = &self.renderable {
r.render((self.area + parent_pos).to_untyped(), driver, compositor)?;
}
Ok(())
}
fn render_children(
&self,
parent_pos: PxPoint,
driver: &crate::graphics::Driver,
compositor: &mut CompositorView<'_>,
dependents: &mut Vec<std::sync::Weak<SourceID>>,
) -> Result<(), Error> {
for child in (&self.children).into_iter().flatten() {
child.render(
parent_pos + self.area.topleft().to_vector(),
driver,
compositor,
dependents,
)?;
}
Ok(())
}
}
impl Staged for Concrete {
fn render(
&self,
parent_pos: PxPoint,
driver: &crate::graphics::Driver,
compositor: &mut CompositorView<'_>,
dependents: &mut Vec<std::sync::Weak<SourceID>>,
) -> Result<(), Error> {
if let Some(id) = self.layer.as_ref().and_then(|x| x.upgrade()) {
let layers = driver.shared.access_layers();
let layer = layers.get(&id).expect("Missing layer in render call!");
let mut deps = Vec::new();
let mut region_uv = None;
let (mut view, depview) = if layer.target.is_some() {
dependents.push(Arc::downgrade(&id));
let index = match compositor.index {
0 => 1,
1 => 2,
2 => 1,
_ => panic!("Invalid index!"),
};
let mut atlas = driver.layer_atlas[index - 1].write();
let region = atlas.cache_region(
&driver.device,
&id,
layer.area.dim().ceil().to_i32(),
None,
None,
)?;
region_uv = Some(region.uv);
driver.layer_atlas[index % 2].write().remove_cache(&id);
assert!(compositor.pass < 0b111111);
let mut v = CompositorView {
index: index as u8,
window: compositor.window,
layer0: compositor.layer0,
layer1: compositor.layer1,
clipstack: compositor.clipstack,
offset: region.uv.min.to_f32() - layer.area.topleft() - parent_pos.to_vector(),
surface_dim: compositor.surface_dim,
pass: compositor.pass + 1,
slice: region.index,
};
v.reserve(driver);
(v, &mut deps)
} else {
(
CompositorView {
index: compositor.index,
window: compositor.window,
layer0: compositor.layer0,
layer1: compositor.layer1,
clipstack: compositor.clipstack,
offset: compositor.offset,
surface_dim: compositor.surface_dim,
pass: compositor.pass,
slice: compositor.slice,
},
dependents,
)
};
view.with_clip(layer.area + parent_pos, |refview| {
self.render_self(parent_pos, driver, refview)?;
self.render_children(parent_pos, driver, refview, depview)
})?;
if let Some(target) = layer.target.as_ref() {
target.write().dependents = deps;
compositor.append_layer(layer, parent_pos, region_uv.unwrap());
}
} else {
self.render_self(parent_pos, driver, compositor)?;
self.render_children(parent_pos, driver, compositor, dependents)?;
};
Ok(())
}
fn get_rtree(&self) -> Weak<rtree::Node> {
Rc::downgrade(&self.rtree)
}
fn get_area(&self) -> PxRect {
self.area
}
fn set_layer(&mut self, id: std::sync::Weak<SourceID>) {
self.layer = Some(id)
}
}
#[must_use]
#[inline]
pub(crate) fn map_unsized_area(mut area: URect, adjust: PxDim) -> URect {
let (unsized_x, unsized_y) = check_unsized(area);
let abs = area.abs.v.as_array_mut();
let rel = area.rel.v.as_array_mut();
if unsized_x {
rel[2] = rel[0];
abs[2] += abs[0] + adjust.width;
}
if unsized_y {
rel[3] = rel[1];
abs[3] += abs[1] + adjust.height;
}
area
}
#[must_use]
#[inline]
pub(crate) fn nuetralize_unsized(v: PxRect) -> PxRect {
let (unsized_x, unsized_y) = check_unsized_abs(v.bottomright());
let ltrb = v.v.to_array();
PxRect {
v: f32x4::new([
ltrb[0],
ltrb[1],
if unsized_x { ltrb[0] } else { ltrb[2] },
if unsized_y { ltrb[1] } else { ltrb[3] },
]),
_unit: PhantomData,
}
}
#[must_use]
#[inline]
pub(crate) fn limit_area(mut v: PxRect, limits: PxLimits) -> PxRect {
v.set_bottomright(
v.bottomright()
.max(v.topleft() + limits.min())
.min(v.topleft() + limits.max()),
);
v
}
#[must_use]
#[inline]
pub(crate) fn limit_dim(v: PxDim, limits: PxLimits) -> PxDim {
let (unsized_x, unsized_y) = check_unsized_dim(v);
PxDim::new(
if unsized_x {
v.width
} else {
v.width.max(limits.min().width).min(limits.max().width)
},
if unsized_y {
v.height
} else {
v.height.max(limits.min().height).min(limits.max().height)
},
)
}
#[must_use]
#[inline]
pub(crate) fn eval_dim(area: URect, dim: PxDim) -> PxDim {
let (unsized_x, unsized_y) = check_unsized(area);
PxDim::new(
if unsized_x {
area.bottomright().rel().x
} else {
let top = area.topleft().abs().x + (area.topleft().rel().x * dim.width);
let bottom = area.bottomright().abs().x + (area.bottomright().rel().x * dim.width);
bottom - top
},
if unsized_y {
area.bottomright().rel().y
} else {
let top = area.topleft().abs().y + (area.topleft().rel().y * dim.height);
let bottom = area.bottomright().abs().y + (area.bottomright().rel().y * dim.height);
bottom - top
},
)
}
#[must_use]
#[inline]
pub(crate) fn apply_limit(dim: PxDim, limits: PxLimits, rlimits: RelLimits) -> PxLimits {
let (unsized_x, unsized_y) = check_unsized_dim(dim);
let sign = limits.v.sign_bit() | rlimits.v.sign_bit();
let px = f32x4::new([
if unsized_x {
limits.min().width
} else {
limits.min().width.max(dim.width)
},
if unsized_y {
limits.min().height
} else {
limits.min().height.max(dim.height)
},
if unsized_x {
limits.max().width
} else {
limits.max().width.min(dim.width)
},
if unsized_y {
limits.max().height
} else {
limits.max().height.min(dim.height)
},
]);
PxLimits {
v: (rlimits.v.is_finite().blend(px, f32x4::ONE) * rlimits.v).copysign(sign),
_unit: PhantomData,
}
}
#[must_use]
#[inline]
pub(crate) fn check_unsized(area: URect) -> (bool, bool) {
(
area.bottomright().rel().x == UNSIZED_AXIS,
area.bottomright().rel().y == UNSIZED_AXIS,
)
}
#[must_use]
#[inline]
pub(crate) fn check_unsized_abs<U>(bottomright: Point2D<f32, U>) -> (bool, bool) {
(bottomright.x == UNSIZED_AXIS, bottomright.y == UNSIZED_AXIS)
}
#[must_use]
#[inline]
pub(crate) fn check_unsized_dim(dim: PxDim) -> (bool, bool) {
check_unsized_abs(dim.to_vector().to_point())
}
pub(crate) fn assert_sized(area: PxRect) {
let ltrb = area.v.as_array_ref();
for v in ltrb {
assert_ne!(*v, UNSIZED_AXIS);
assert!(v.is_finite());
}
}
#[must_use]
#[inline]
pub(crate) fn cap_unsized(area: PxRect) -> PxRect {
let ltrb = area.v.to_array();
PxRect {
v: f32x4::new(ltrb.map(|x| {
if x.is_finite() {
x
} else {
crate::UNSIZED_AXIS
}
})),
_unit: PhantomData,
}
}
#[must_use]
#[inline]
pub(crate) fn apply_anchor(area: PxRect, outer_area: PxRect, mut anchor: PxPoint) -> PxRect {
let (unsized_outer_x, unsized_outer_y) = check_unsized_abs(outer_area.bottomright());
if unsized_outer_x {
anchor.x = 0.0;
}
if unsized_outer_y {
anchor.y = 0.0;
}
area - anchor
}
#[must_use]
#[inline]
fn swap_pair<T>(xaxis: bool, v: (T, T)) -> (T, T) {
if xaxis { (v.0, v.1) } else { (v.1, v.0) }
}
trait Swappable<T> {
fn swap_axis(self, xaxis: bool) -> (T, T);
}
impl<T, U> Swappable<T> for Point2D<T, U> {
#[inline]
fn swap_axis(self, xaxis: bool) -> (T, T) {
swap_pair(xaxis, (self.x, self.y))
}
}
impl<T, U> Swappable<T> for guillotiere::euclid::Size2D<T, U> {
#[inline]
fn swap_axis(self, xaxis: bool) -> (T, T) {
swap_pair(xaxis, (self.width, self.height))
}
}
impl<T, U> Swappable<T> for Vector2D<T, U> {
#[inline]
fn swap_axis(self, xaxis: bool) -> (T, T) {
swap_pair(xaxis, (self.x, self.y))
}
}
#[must_use]
#[inline]
fn merge_margin(prev: f32, margin: f32) -> f32 {
if prev.is_nan() { 0.0 } else { margin.max(prev) }
}