Skip to main content

tessera_components/list_item/
shapes.rs

1//! Expressive list item corners for positions and interaction states.
2//!
3//! ## Usage
4//!
5//! Group related rows and highlight their active states.
6
7use crate::{shape_def::Shape, theme::MaterialTheme};
8
9/// Position of a row within a segmented list.
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
11pub enum ListItemPosition {
12    /// An independent row without segmented outer corners.
13    #[default]
14    Standalone,
15    /// First row, with larger upper corners.
16    First,
17    /// Interior row, with smaller corners on all sides.
18    Middle,
19    /// Last row, with larger lower corners.
20    Last,
21    /// The only row, with larger corners on all sides.
22    Single,
23}
24
25/// Container shapes used by a list item in its interaction states.
26#[derive(Clone, Copy, Debug, PartialEq)]
27pub struct ListItemShapes {
28    /// Resting shape.
29    pub shape: Shape,
30    /// Shape when selected.
31    pub selected_shape: Shape,
32    /// Shape while pressed.
33    pub pressed_shape: Shape,
34    /// Shape while focused.
35    pub focused_shape: Shape,
36    /// Shape while hovered.
37    pub hovered_shape: Shape,
38    /// Shape while dragged.
39    pub dragged_shape: Shape,
40}
41
42impl Default for ListItemShapes {
43    fn default() -> Self {
44        Self::from_theme(&MaterialTheme::default())
45    }
46}
47
48impl ListItemShapes {
49    /// Resolve list shapes from the current theme's corner sizes.
50    pub fn from_theme(theme: &MaterialTheme) -> Self {
51        Self {
52            shape: theme.shapes.extra_small,
53            selected_shape: theme.shapes.large,
54            pressed_shape: theme.shapes.large,
55            focused_shape: theme.shapes.large,
56            hovered_shape: theme.shapes.medium,
57            dragged_shape: theme.shapes.large,
58        }
59    }
60
61    pub(super) fn segmented(mut self, position: ListItemPosition, outer: Shape) -> Self {
62        if let (
63            Shape::RoundedRectangle {
64                top_left,
65                top_right,
66                bottom_right,
67                bottom_left,
68            },
69            Shape::RoundedRectangle {
70                top_left: outer_tl,
71                top_right: outer_tr,
72                bottom_right: outer_br,
73                bottom_left: outer_bl,
74            },
75        ) = (self.shape, outer)
76        {
77            let top = matches!(position, ListItemPosition::First | ListItemPosition::Single);
78            let bottom = matches!(position, ListItemPosition::Last | ListItemPosition::Single);
79            self.shape = Shape::RoundedRectangle {
80                top_left: if top { outer_tl } else { top_left },
81                top_right: if top { outer_tr } else { top_right },
82                bottom_right: if bottom { outer_br } else { bottom_right },
83                bottom_left: if bottom { outer_bl } else { bottom_left },
84            };
85        }
86        self
87    }
88}