1use alloc::boxed::Box;
8use rlvgl_core::{
9 event::Event,
10 renderer::Renderer,
11 widget::{Rect, Widget},
12};
13use rlvgl_widgets::{button::Button as BaseButton, slider::Slider as BaseSlider};
14
15use crate::theme::{ColorScheme, ComponentSize, Theme, Variant};
16
17pub trait OnClick {
19 fn on_click<F: FnMut(&mut Self) + 'static>(self, handler: F) -> Self;
21}
22
23impl OnClick for BaseButton {
24 fn on_click<F: FnMut(&mut Self) + 'static>(mut self, handler: F) -> Self {
25 self.set_on_click(handler);
26 self
27 }
28}
29
30pub struct Slider {
32 inner: BaseSlider,
33 on_change: Option<Box<dyn FnMut(i32)>>,
34}
35
36impl Slider {
37 pub fn new(bounds: Rect, min: i32, max: i32) -> Self {
39 let inner = BaseSlider::new(bounds, min, max);
40 Self {
41 inner,
42 on_change: None,
43 }
44 }
45
46 pub fn on_change<F: FnMut(i32) + 'static>(mut self, handler: F) -> Self {
48 self.on_change = Some(Box::new(handler));
49 self
50 }
51
52 pub fn with_value(mut self, val: i32) -> Self {
54 self.inner.set_value(val);
55 self
56 }
57
58 pub fn knob_color(mut self, color: rlvgl_core::widget::Color) -> Self {
60 self.inner.knob_color = color;
61 self
62 }
63
64 pub fn knob_color_value(&self) -> rlvgl_core::widget::Color {
66 self.inner.knob_color
67 }
68
69 pub fn set_knob_color(&mut self, color: rlvgl_core::widget::Color) {
71 self.inner.knob_color = color;
72 }
73
74 pub fn themed(
76 mut self,
77 theme: &Theme,
78 scheme: ColorScheme,
79 variant: Variant,
80 size: ComponentSize,
81 ) -> Self {
82 let resolved = theme.component_style(scheme, variant, size);
83 self.inner.style = resolved.style;
84 self.inner.knob_color = resolved.accent_color;
85 self
86 }
87
88 pub fn value(&self) -> i32 {
90 self.inner.value()
91 }
92
93 pub fn set_value(&mut self, val: i32) {
95 self.inner.set_value(val);
96 }
97
98 pub fn style(&self) -> &rlvgl_core::style::Style {
100 &self.inner.style
101 }
102
103 pub fn style_mut(&mut self) -> &mut rlvgl_core::style::Style {
105 &mut self.inner.style
106 }
107}
108
109impl Widget for Slider {
110 fn bounds(&self) -> Rect {
111 self.inner.bounds()
112 }
113
114 fn draw(&self, renderer: &mut dyn Renderer) {
115 self.inner.draw(renderer);
116 }
117
118 fn handle_event(&mut self, event: &Event) -> bool {
119 let before = self.inner.value();
120 let handled = self.inner.handle_event(event);
121 let after = self.inner.value();
122 if !handled || after == before {
123 return handled;
124 }
125 if let Some(cb) = self.on_change.as_mut() {
126 cb(after);
127 }
128 handled
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135 use alloc::rc::Rc;
136 use core::cell::Cell;
137 use rlvgl_core::{event::Event, widget::Rect};
138 use rlvgl_widgets::button::Button;
139
140 #[test]
141 fn button_on_click_executes() {
142 let bounds = Rect {
143 x: 0,
144 y: 0,
145 width: 20,
146 height: 20,
147 };
148 let clicked = Rc::new(Cell::new(false));
149 let c = clicked.clone();
150 let mut btn = Button::new("ok", bounds).on_click(move |_| c.set(true));
151 let event = Event::PressRelease { x: 5, y: 5 };
152 btn.handle_event(&event);
153 assert!(clicked.get());
154 }
155
156 #[test]
157 fn slider_on_change_executes() {
158 let bounds = Rect {
159 x: 0,
160 y: 0,
161 width: 100,
162 height: 10,
163 };
164 let value = Rc::new(Cell::new(0));
165 let v = value.clone();
166 let mut slider = Slider::new(bounds, 0, 10).on_change(move |x| v.set(x));
167 let event = Event::PressRelease { x: 50, y: 5 };
168 slider.handle_event(&event);
169 assert_ne!(value.get(), 0);
170 }
171
172 #[test]
173 fn slider_themed_sets_style_and_knob() {
174 let theme = Theme::material_light();
175 let slider = Slider::new(
176 Rect {
177 x: 0,
178 y: 0,
179 width: 100,
180 height: 10,
181 },
182 0,
183 10,
184 )
185 .themed(
186 &theme,
187 ColorScheme::Primary,
188 Variant::Outline,
189 ComponentSize::Lg,
190 );
191
192 assert_eq!(
193 slider.knob_color_value(),
194 theme.scheme(ColorScheme::Primary).solid
195 );
196 assert_eq!(slider.style().border_width, 2);
197 }
198}