use std::panic::Location;
use crate::layout::LayoutCtx;
use crate::tokens;
use crate::tree::*;
pub const DEFAULT_HEIGHT: f32 = 8.0;
#[track_caller]
pub fn progress(value: f32, fill_color: Color) -> El {
let value = value.clamp(0.0, 1.0);
let layout = move |ctx: LayoutCtx| {
let r = ctx.container;
vec![
Rect::new(r.x, r.y, r.w, r.h),
Rect::new(r.x, r.y, r.w * value, r.h),
]
};
stack([
El::new(Kind::Custom("progress-track"))
.fill(tokens::BG_MUTED)
.radius(tokens::RADIUS_PILL),
El::new(Kind::Custom("progress-fill"))
.fill(fill_color)
.radius(tokens::RADIUS_PILL),
])
.at_loc(Location::caller())
.layout(layout)
.width(Size::Fill(1.0))
.height(Size::Fixed(DEFAULT_HEIGHT))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn track_and_fill_use_expected_tokens() {
let p = progress(0.5, tokens::PRIMARY);
assert_eq!(p.children.len(), 2);
assert_eq!(p.children[0].fill, Some(tokens::BG_MUTED), "track is muted");
assert_eq!(
p.children[1].fill,
Some(tokens::PRIMARY),
"fill uses caller's color"
);
assert_eq!(p.children[0].radius, tokens::RADIUS_PILL);
assert_eq!(p.children[1].radius, tokens::RADIUS_PILL);
}
#[test]
fn layout_clamps_value_below_zero() {
use crate::layout::layout;
use crate::state::UiState;
let mut tree = progress(-0.5, tokens::PRIMARY);
let mut state = UiState::new();
let viewport = Rect::new(0.0, 0.0, 200.0, DEFAULT_HEIGHT);
layout(&mut tree, &mut state, viewport);
let fill_rect = state.rect(&tree.children[1].computed_id);
assert_eq!(fill_rect.w, 0.0, "negative values clamp to empty fill");
}
#[test]
fn layout_clamps_value_above_one() {
use crate::layout::layout;
use crate::state::UiState;
let mut tree = progress(1.5, tokens::PRIMARY);
let mut state = UiState::new();
let viewport = Rect::new(0.0, 0.0, 200.0, DEFAULT_HEIGHT);
layout(&mut tree, &mut state, viewport);
let fill_rect = state.rect(&tree.children[1].computed_id);
assert_eq!(fill_rect.w, 200.0, "values above 1.0 clamp to full track");
}
#[test]
fn layout_fills_proportionally_to_value() {
use crate::layout::layout;
use crate::state::UiState;
let mut tree = progress(0.25, tokens::PRIMARY);
let mut state = UiState::new();
let viewport = Rect::new(0.0, 0.0, 200.0, DEFAULT_HEIGHT);
layout(&mut tree, &mut state, viewport);
let fill_rect = state.rect(&tree.children[1].computed_id);
assert!(
(fill_rect.w - 50.0).abs() < 1e-3,
"0.25 * 200 = 50; got {}",
fill_rect.w
);
}
}