tessera_ui_basic_components/checkmark.rs
1//! Provides a GPU-accelerated, animated checkmark component for UI elements.
2//!
3//! This module defines the [`checkmark`] function and related types for rendering a customizable,
4//! animated checkmark, typically used to visually indicate a "checked" state in controls such as
5//! checkboxes. The checkmark supports smooth animation, color and stroke customization, and is
6//! rendered using Tessera's GPU pipeline for high performance and visual fidelity.
7//!
8//! # Typical Usage
9//!
10//! The checkmark is most commonly used within checkbox components, but can be integrated into any
11//! UI element requiring a checkmark indicator. It is suitable for applications needing responsive,
12//! theme-adaptable, and animated visual feedback for selection or confirmation states.
13//!
14//! # Features
15//! - Customizable color, stroke width, size, and padding
16//! - Smooth animation progress control
17//! - High-performance GPU rendering
18//!
19//! See [`CheckmarkArgs`] for configuration options and usage examples in the [`checkmark`] function documentation.
20
21use derive_builder::Builder;
22use tessera_ui::{Color, ComputedData, Dp, Px};
23use tessera_ui_macros::tessera;
24
25use crate::pipelines::CheckmarkCommand;
26
27/// Arguments for the `checkmark` component.
28#[derive(Builder, Clone)]
29#[builder(pattern = "owned")]
30pub struct CheckmarkArgs {
31 /// Color of the checkmark stroke
32 #[builder(default = "Color::new(0.0, 0.6, 0.0, 1.0)")]
33 pub color: Color,
34
35 /// Width of the checkmark stroke in pixels
36 #[builder(default = "5.0")]
37 pub stroke_width: f32,
38
39 /// Animation progress from 0.0 (not drawn) to 1.0 (fully drawn)
40 #[builder(default = "1.0")]
41 pub progress: f32,
42
43 /// Padding around the checkmark within its bounds
44 #[builder(default = "[2.0, 2.0]")]
45 pub padding: [f32; 2], // [horizontal, vertical]
46
47 /// Size of the checkmark area
48 #[builder(default = "Dp(20.0)")]
49 pub size: Dp,
50}
51
52impl std::fmt::Debug for CheckmarkArgs {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("CheckmarkArgs")
55 .field("color", &self.color)
56 .field("stroke_width", &self.stroke_width)
57 .field("progress", &self.progress)
58 .field("padding", &self.padding)
59 .field("size", &self.size)
60 .finish()
61 }
62}
63
64impl Default for CheckmarkArgs {
65 fn default() -> Self {
66 CheckmarkArgsBuilder::default().build().unwrap()
67 }
68}
69
70/// Renders a checkmark, a visual indicator that is displayed when in a `checked` state.
71///
72/// This component is a GPU-rendered checkmark that provides a smooth, animated alternative
73/// to traditional emoji or icon-based checkmarks. It supports customization of color,
74/// stroke width, and animation progress.
75///
76/// # Arguments
77///
78/// The `args` parameter accepts any value that can be converted into `CheckmarkArgs`,
79/// including a `CheckmarkArgs` struct or its builder.
80///
81/// * `color`: The `Color` of the checkmark stroke. Defaults to a green color.
82/// * `stroke_width`: The width of the checkmark stroke in pixels. Defaults to `5.0`.
83/// * `progress`: The animation progress for drawing the checkmark, from `0.0` (not drawn)
84/// to `1.0` (fully drawn). Defaults to `1.0`.
85/// * `padding`: The padding `[horizontal, vertical]` around the checkmark within its bounds.
86/// Defaults to `[2.0, 2.0]`.
87/// * `size`: The size of the checkmark area as a `Dp` value. Defaults to `Dp(20.0)`.
88///
89/// # Example
90///
91/// ```
92/// use tessera_ui::Color;
93/// use tessera_ui_basic_components::checkmark::{checkmark, CheckmarkArgs};
94///
95/// checkmark(CheckmarkArgs {
96/// color: Color::from_rgb(0.0, 0.7, 0.0),
97/// ..Default::default()
98/// });
99/// ```
100#[tessera]
101pub fn checkmark(args: impl Into<CheckmarkArgs>) {
102 let args: CheckmarkArgs = args.into();
103
104 let size_px = args.size.to_px();
105
106 // Create the checkmark command
107 let command = CheckmarkCommand {
108 color: args.color,
109 stroke_width: args.stroke_width,
110 progress: args.progress,
111 padding: args.padding,
112 };
113
114 // Measure the component and push the draw command within the measure function
115 measure(Box::new(move |input| {
116 // Push the draw command to the current node's metadata
117 input.metadata_mut().push_draw_command(command.clone());
118
119 Ok(ComputedData {
120 width: Px::new(size_px.to_f32() as i32),
121 height: Px::new(size_px.to_f32() as i32),
122 })
123 }));
124}