egui_charts/ui_kit/list_item/
visuals.rs1use egui::{Color32, Visuals};
7
8#[derive(Clone, Debug)]
10pub struct ListVisuals {
11 pub hovered: bool,
13 pub selected: bool,
15 pub active: bool,
17 pub interactive: bool,
19 pub strong: bool,
21 pub openness: Option<f32>,
23}
24
25impl Default for ListVisuals {
26 fn default() -> Self {
27 Self {
28 hovered: false,
29 selected: false,
30 active: false,
31 interactive: true,
32 strong: false,
33 openness: None,
34 }
35 }
36}
37
38impl ListVisuals {
39 pub fn new() -> Self {
41 Self::default()
42 }
43
44 #[must_use]
46 pub fn with_hovered(mut self, hovered: bool) -> Self {
47 self.hovered = hovered;
48 self
49 }
50
51 #[must_use]
53 pub fn with_selected(mut self, selected: bool) -> Self {
54 self.selected = selected;
55 self
56 }
57
58 #[must_use]
60 pub fn with_active(mut self, active: bool) -> Self {
61 self.active = active;
62 self
63 }
64
65 #[must_use]
67 pub fn with_interactive(mut self, interactive: bool) -> Self {
68 self.interactive = interactive;
69 self
70 }
71
72 #[must_use]
74 pub fn with_strong(mut self, strong: bool) -> Self {
75 self.strong = strong;
76 self
77 }
78
79 #[must_use]
81 pub fn with_openness(mut self, openness: f32) -> Self {
82 self.openness = Some(openness);
83 self
84 }
85
86 pub fn bg_color(&self, visuals: &Visuals) -> Option<Color32> {
90 if self.selected {
91 Some(visuals.selection.bg_fill)
92 } else if self.active {
93 Some(visuals.widgets.active.bg_fill)
94 } else if self.hovered && self.interactive {
95 Some(visuals.widgets.hovered.bg_fill)
96 } else {
97 None
98 }
99 }
100
101 pub fn text_color(&self, visuals: &Visuals) -> Color32 {
103 if self.selected {
104 visuals.selection.stroke.color
105 } else if !self.interactive {
106 visuals.widgets.noninteractive.fg_stroke.color
107 } else if self.hovered || self.active || self.strong {
108 visuals.widgets.hovered.fg_stroke.color
109 } else {
110 visuals.widgets.inactive.fg_stroke.color
111 }
112 }
113
114 pub fn icon_tint(&self, visuals: &Visuals) -> Color32 {
116 if self.selected {
117 visuals.selection.stroke.color
118 } else if (self.hovered && self.interactive) || self.active {
119 visuals.widgets.hovered.fg_stroke.color
120 } else {
121 visuals.widgets.noninteractive.fg_stroke.color
122 }
123 }
124
125 pub fn interactive_icon_tint(&self, icon_hovered: bool, visuals: &Visuals) -> Color32 {
129 if icon_hovered && !self.selected {
130 visuals.widgets.hovered.fg_stroke.color
131 } else {
132 self.icon_tint(visuals)
133 }
134 }
135
136 pub fn is_collapsible(&self) -> bool {
138 self.openness.is_some()
139 }
140
141 pub fn is_collapsed(&self) -> bool {
143 self.openness.is_none_or(|o| o <= 0.0)
144 }
145
146 pub fn openness_value(&self) -> f32 {
148 self.openness.unwrap_or(0.0)
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn test_collapsible() {
158 let non_collapsible = ListVisuals::new();
159 assert!(!non_collapsible.is_collapsible());
160 assert!(non_collapsible.is_collapsed());
161
162 let collapsed = ListVisuals::new().with_openness(0.0);
163 assert!(collapsed.is_collapsible());
164 assert!(collapsed.is_collapsed());
165
166 let expanded = ListVisuals::new().with_openness(1.0);
167 assert!(expanded.is_collapsible());
168 assert!(!expanded.is_collapsed());
169 }
170}