use derive_setters::Setters;
use tessera_ui::{
Color, ComputedData, Constraint, DimensionValue, Dp, LayoutInput, LayoutOutput, LayoutSpec,
MeasurementError, Px, RenderInput, tessera, use_context,
};
use crate::{pipelines::simple_rect::command::SimpleRectCommand, theme::MaterialTheme};
fn resolve_thickness_px(thickness: Dp) -> Px {
if thickness == Dp::ZERO {
Px(1)
} else {
thickness.to_px()
}
}
fn clamp_wrap(min: Option<Px>, max: Option<Px>, measure: Px) -> Px {
min.unwrap_or(Px(0))
.max(measure)
.min(max.unwrap_or(Px::MAX))
}
fn fill_value(min: Option<Px>, max: Option<Px>, measure: Px) -> Px {
max.expect("Seems that you are trying to fill an infinite dimension, which is not allowed")
.max(measure)
.max(min.unwrap_or(Px(0)))
}
fn resolve_dimension(dim: DimensionValue, measure: Px) -> Px {
match dim {
DimensionValue::Fixed(v) => v,
DimensionValue::Wrap { min, max } => clamp_wrap(min, max, measure),
DimensionValue::Fill { min, max } => fill_value(min, max, measure),
}
}
pub struct DividerDefaults;
impl DividerDefaults {
pub const THICKNESS: Dp = Dp(1.0);
pub fn color() -> Color {
use_context::<MaterialTheme>()
.expect("MaterialTheme must be provided")
.get()
.color_scheme
.outline_variant
}
}
#[derive(Clone, Debug, Setters)]
pub struct DividerArgs {
pub thickness: Dp,
pub color: Color,
}
impl Default for DividerArgs {
fn default() -> Self {
Self {
thickness: DividerDefaults::THICKNESS,
color: DividerDefaults::color(),
}
}
}
#[derive(Clone, Copy, PartialEq)]
enum DividerOrientation {
Horizontal,
Vertical,
}
#[derive(Clone, Copy, PartialEq)]
struct DividerLayout {
thickness: Px,
color: Color,
orientation: DividerOrientation,
}
impl LayoutSpec for DividerLayout {
fn measure(
&self,
input: &LayoutInput<'_>,
_output: &mut LayoutOutput<'_>,
) -> Result<ComputedData, MeasurementError> {
let intrinsic = match self.orientation {
DividerOrientation::Horizontal => Constraint::new(
DimensionValue::FILLED,
DimensionValue::Fixed(self.thickness),
),
DividerOrientation::Vertical => Constraint::new(
DimensionValue::Fixed(self.thickness),
DimensionValue::FILLED,
),
};
let effective = intrinsic.merge(input.parent_constraint());
let (width, height) = match self.orientation {
DividerOrientation::Horizontal => (
resolve_dimension(effective.width, Px(0)),
resolve_dimension(effective.height, self.thickness),
),
DividerOrientation::Vertical => (
resolve_dimension(effective.width, self.thickness),
resolve_dimension(effective.height, Px(0)),
),
};
Ok(ComputedData { width, height })
}
fn record(&self, input: &RenderInput<'_>) {
input
.metadata_mut()
.push_draw_command(SimpleRectCommand { color: self.color });
}
}
#[tessera]
pub fn horizontal_divider(args: impl Into<DividerArgs>) {
let args: DividerArgs = args.into();
let thickness_px = resolve_thickness_px(args.thickness);
let color = args.color;
layout(DividerLayout {
thickness: thickness_px,
color,
orientation: DividerOrientation::Horizontal,
});
}
#[tessera]
pub fn vertical_divider(args: impl Into<DividerArgs>) {
let args: DividerArgs = args.into();
let thickness_px = resolve_thickness_px(args.thickness);
let color = args.color;
layout(DividerLayout {
thickness: thickness_px,
color,
orientation: DividerOrientation::Vertical,
});
}