use std::sync::Arc;
use crate::builder::LayoutBuilder;
use crate::error::{ConstraintError, PaneError, TreeError};
use crate::layout::Layout;
use crate::panel::{fixed, grow};
use crate::validate::{check_f32_non_negative, float_invalid_to_constraint};
pub(crate) fn collect_kinds(
kinds: impl IntoIterator<Item = impl Into<Arc<str>>>,
) -> Arc<[Arc<str>]> {
kinds.into_iter().map(Into::into).collect()
}
pub(crate) fn build_single(kind: Arc<str>) -> Result<Layout, PaneError> {
let mut b = LayoutBuilder::new();
b.row(|r| {
r.panel(kind);
})?;
b.build()
}
pub(crate) fn validate_kinds(kinds: &[Arc<str>]) -> Result<(), PaneError> {
match kinds.is_empty() {
true => Err(PaneError::InvalidTree(TreeError::NoKinds)),
false => Ok(()),
}
}
pub(crate) fn validate_f32_param(name: &'static str, value: f32) -> Result<(), PaneError> {
check_f32_non_negative(value)
.map_err(|e| PaneError::InvalidConstraint(float_invalid_to_constraint(name, e)))
}
pub(crate) fn validate_share_param(name: &'static str, value: f32) -> Result<(), PaneError> {
validate_f32_param(name, value)?;
match value <= 1.0 {
true => Ok(()),
false => Err(PaneError::InvalidConstraint(ConstraintError::ExceedsOne(
name,
))),
}
}
pub(crate) fn validate_active(active: usize, len: usize) -> Result<(), PaneError> {
match active >= len {
true => Err(PaneError::InvalidTree(TreeError::ActiveOutOfBounds {
active,
len,
})),
false => Ok(()),
}
}
pub(crate) fn add_active_hidden_panels(
ctx: &mut crate::ContainerCtx,
kinds: &[Arc<str>],
active: usize,
) {
for (i, kind) in kinds.iter().enumerate() {
let constraint = match i == active {
true => grow(1.0),
false => fixed(0.0),
};
ctx.panel_with(Arc::clone(kind), constraint);
}
}
fn flex_style(direction: taffy::FlexDirection, flex_grow: f32, gap_px: f32) -> taffy::Style {
let gap = match direction {
taffy::FlexDirection::Row | taffy::FlexDirection::RowReverse => taffy::Size {
width: taffy::LengthPercentage::length(gap_px),
height: taffy::LengthPercentage::length(0.0),
},
_ => taffy::Size {
width: taffy::LengthPercentage::length(0.0),
height: taffy::LengthPercentage::length(gap_px),
},
};
taffy::Style {
flex_direction: direction,
flex_grow,
flex_basis: taffy::Dimension::length(0.0),
flex_shrink: 1.0,
gap,
..Default::default()
}
}
pub(crate) fn col_style(flex_grow: f32, gap_px: f32) -> taffy::Style {
flex_style(taffy::FlexDirection::Column, flex_grow, gap_px)
}
pub(crate) fn row_style(flex_grow: f32, gap_px: f32) -> taffy::Style {
flex_style(taffy::FlexDirection::Row, flex_grow, gap_px)
}
pub(crate) fn add_panels(
ctx: &mut crate::ContainerCtx,
kinds: &[Arc<str>],
constraints: crate::Constraints,
) {
for kind in kinds {
ctx.panel_with(Arc::clone(kind), constraints);
}
}
use taffy::prelude::{fr, minmax, repeat};
use taffy::style::{GridTemplateComponent, MaxTrackSizingFunction, MinTrackSizingFunction};
use crate::strategy::GridColumnMode;
pub(crate) fn columns_to_taffy(columns: GridColumnMode) -> Vec<GridTemplateComponent<String>> {
match columns {
GridColumnMode::Fixed(n) => vec![fr(1.0); n],
GridColumnMode::AutoFill { min_width } => vec![auto_repeat_track("auto-fill", min_width)],
GridColumnMode::AutoFit { min_width } => vec![auto_repeat_track("auto-fit", min_width)],
}
}
pub(crate) fn auto_repeat_track(kind: &str, min_width: f32) -> GridTemplateComponent<String> {
let track = minmax(
MinTrackSizingFunction::length(min_width),
MaxTrackSizingFunction::fr(1.0),
);
repeat(kind, vec![track])
}
pub(crate) fn simple_grid_style(mode: GridColumnMode, gap: f32, auto_rows: bool) -> taffy::Style {
let gap_len = taffy::LengthPercentage::length(gap);
let grid_auto_rows = match auto_rows {
true => vec![minmax(
MinTrackSizingFunction::auto(),
MaxTrackSizingFunction::auto(),
)],
false => vec![fr(1.0)],
};
taffy::Style {
display: taffy::Display::Grid,
size: taffy::Size {
width: taffy::Dimension::percent(1.0),
height: taffy::Dimension::percent(1.0),
},
grid_template_columns: columns_to_taffy(mode),
grid_auto_rows,
gap: taffy::Size {
width: gap_len,
height: gap_len,
},
..Default::default()
}
}
pub(crate) fn validate_grid_columns(columns: GridColumnMode) -> Result<(), PaneError> {
match columns {
GridColumnMode::Fixed(0) => Err(PaneError::InvalidTree(TreeError::DashboardNoColumns)),
GridColumnMode::AutoFill { min_width } | GridColumnMode::AutoFit { min_width }
if !(min_width > 0.0 && min_width.is_finite()) =>
{
Err(PaneError::InvalidTree(TreeError::DashboardMinWidthInvalid))
}
_ => Ok(()),
}
}
pub(crate) fn validate_grid_span(span: crate::strategy::CardSpan) -> Result<(), PaneError> {
match span {
crate::strategy::CardSpan::Columns(n) => {
u16::try_from(n).map_err(|_| {
PaneError::InvalidConstraint(crate::error::ConstraintError::GridSpanOverflow(n))
})?;
Ok(())
}
crate::strategy::CardSpan::FullWidth => Ok(()),
}
}
pub(crate) fn grid_item_style(span: crate::strategy::CardSpan) -> taffy::Style {
use crate::strategy::CardSpan;
use taffy::prelude::TaffyGridLine;
let grid_column = match span {
CardSpan::FullWidth => taffy::Line {
start: taffy::GridPlacement::from_line_index(1),
end: taffy::GridPlacement::from_line_index(-1),
},
CardSpan::Columns(n) => {
let span_u16 = n as u16;
taffy::Line {
start: taffy::GridPlacement::Auto,
end: taffy::GridPlacement::Span(span_u16),
}
}
};
taffy::Style {
grid_column,
..Default::default()
}
}
macro_rules! impl_preset {
($Type:ty, runtime($kinds_field:ident, |$this:ident| $($strategy_tokens:tt)+)) => {
impl $Type {
pub fn into_runtime(self) -> Result<$crate::runtime::LayoutRuntime, $crate::PaneError> {
let $this = self;
let strategy = $($strategy_tokens)+;
$crate::runtime::LayoutRuntime::from_strategy(strategy, &$this.$kinds_field)
}
}
super::impl_preset!($Type);
};
($Type:ty) => {
impl $Type {
pub fn resolve(
&self,
width: f32,
height: f32,
) -> Result<$crate::ResolvedLayout, $crate::PaneError> {
self.build()?.resolve(width, height)
}
}
impl TryFrom<$Type> for $crate::Layout {
type Error = $crate::PaneError;
fn try_from(preset: $Type) -> Result<Self, Self::Error> {
preset.build()
}
}
};
}
pub(crate) use impl_preset;