gpui_kit_material/components/
preview.rs1use crate::tokens::state;
16
17#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
19pub enum InteractionPreview {
20 #[default]
22 Enabled,
23 Hovered,
25 Focused,
28 Pressed,
30 Disabled,
32}
33
34impl InteractionPreview {
35 pub const ALL: [Self; 5] = [
37 Self::Enabled,
38 Self::Hovered,
39 Self::Focused,
40 Self::Pressed,
41 Self::Disabled,
42 ];
43
44 pub const fn name(self) -> &'static str {
47 match self {
48 Self::Enabled => "Enabled",
49 Self::Hovered => "Hovered",
50 Self::Focused => "Focused",
51 Self::Pressed => "Pressed",
52 Self::Disabled => "Disabled",
53 }
54 }
55
56 pub const fn is_disabled(self) -> bool {
57 matches!(self, Self::Disabled)
58 }
59 pub const fn is_hovered(self) -> bool {
60 matches!(self, Self::Hovered)
61 }
62 pub const fn is_focused(self) -> bool {
63 matches!(self, Self::Focused)
64 }
65 pub const fn is_pressed(self) -> bool {
66 matches!(self, Self::Pressed)
67 }
68
69 pub const fn layer_opacity(self) -> f32 {
71 match self {
72 Self::Hovered => state::HOVER,
73 Self::Focused => state::FOCUS,
74 Self::Pressed => state::PRESS,
75 Self::Enabled | Self::Disabled => 0.,
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::InteractionPreview::*;
83 use crate::tokens::state;
84
85 #[test]
86 fn states_use_the_state_layer_tokens_in_design_kit_order() {
87 assert_eq!(
88 super::InteractionPreview::ALL.map(|s| s.layer_opacity()),
89 [0., state::HOVER, state::FOCUS, state::PRESS, 0.]
90 );
91 assert_eq!(
92 super::InteractionPreview::ALL.map(|s| s.name()),
93 ["Enabled", "Hovered", "Focused", "Pressed", "Disabled"]
94 );
95 assert!(Disabled.is_disabled() && !Pressed.is_disabled());
96 }
97
98 use crate::components::focus::test_rings;
99 use crate::components::{
100 AssistChip, ChoiceTile, ConnectedButtonGroup, InteractionPreview, MaterialSwitch, TileScale,
101 };
102 use gpui::{Context, IntoElement, Render, TestAppContext, Window, div, prelude::*, px};
103
104 struct Specimens {
105 state: InteractionPreview,
106 }
107 impl Render for Specimens {
108 fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
109 window.set_rem_size(px(14.));
110 let state = self.state;
111 div()
112 .flex()
113 .flex_col()
114 .gap(px(24.))
115 .p(px(24.))
116 .child(AssistChip::new("specimen-chip", "Chip").preview_state(state))
117 .child(
118 div().debug_selector(|| "specimen-switch".into()).child(
119 MaterialSwitch::new("specimen-switch", "Switch").preview_state(state),
120 ),
121 )
122 .child(
123 TileScale::Regular
124 .button("specimen-tile", [true; 4])
125 .preview_state(state)
126 .child("Tile"),
127 )
128 .child(
129 ChoiceTile::new("specimen-choice", 0, 1, "Choice", false)
130 .preview_state(state)
131 .render(window, cx),
132 )
133 .child(
134 ConnectedButtonGroup::new("specimen-group", "Group")
135 .options([("a", "A"), ("b", "B")])
136 .selected("a")
137 .preview_state("b", state),
138 )
139 }
140 }
141
142 fn draw(cx: &mut gpui::VisualTestContext) {
143 cx.update(|window, cx| {
144 window.refresh();
145 window.draw(cx).clear(cx);
146 });
147 }
148
149 #[gpui::test]
150 fn forced_focus_draws_the_ring_without_keyboard_input(cx: &mut TestAppContext) {
151 cx.update(gpui_base::init);
152 let (view, cx) = cx.add_window_view(|_, _| Specimens { state: Focused });
153 draw(cx);
154 for part in ["specimen-chip", "specimen-tile", "specimen-choice"] {
155 assert!(test_rings::painted(part).is_some(), "{part} shows its ring");
156 }
157 for state in [Enabled, Hovered, Pressed, Disabled] {
158 view.update(cx, |view, cx| {
159 view.state = state;
160 cx.notify();
161 });
162 draw(cx);
163 for part in ["specimen-chip", "specimen-tile", "specimen-choice"] {
164 assert!(test_rings::painted(part).is_none(), "{part}: {state:?}");
165 }
166 }
167 }
168
169 #[gpui::test]
170 fn specimens_are_not_tab_stops(cx: &mut TestAppContext) {
171 cx.update(gpui_base::init);
172 let (_, cx) = cx.add_window_view(|_, _| Specimens { state: Enabled });
173 draw(cx);
174 cx.update(|window, cx| window.focus_next(cx));
175 assert!(cx.update(|window, cx| window.focused(cx)).is_none());
176 }
177
178 #[gpui::test]
179 fn a_forced_press_grows_the_switch_handle(cx: &mut TestAppContext) {
180 cx.update(gpui_base::init);
181 let (view, cx) = cx.add_window_view(|_, _| Specimens { state: Pressed });
182 draw(cx);
183 let pressed = cx.debug_bounds("switch-handle").unwrap().size.width;
184 assert_eq!(pressed, px(28.));
185 view.update(cx, |view, cx| {
186 view.state = Hovered;
187 cx.notify();
188 });
189 draw(cx);
190 cx.executor()
191 .advance_clock(std::time::Duration::from_secs(1));
192 draw(cx);
193 assert_eq!(
194 cx.debug_bounds("switch-handle").unwrap().size.width,
195 px(16.)
196 );
197 }
198}