1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
// SPDX-License-Identifier: MIT OR Apache-2.0
use core::{ops::Deref, ptr::null_mut};
use oxivgl_sys::*;
use super::{
obj::{AsLvHandle, Obj},
subject::Subject,
WidgetError,
};
/// LVGL slider widget (native range 0–100 by default).
///
/// Use [`on_event`](Obj::on_event) with `EventCode::VALUE_CHANGED`
/// to react to slider movement.
#[derive(Debug)]
pub struct Slider<'p> {
obj: Obj<'p>,
}
impl<'p> AsLvHandle for Slider<'p> {
fn lv_handle(&self) -> *mut lv_obj_t {
self.obj.lv_handle()
}
}
impl<'p> Deref for Slider<'p> {
type Target = Obj<'p>;
fn deref(&self) -> &Obj<'p> {
&self.obj
}
}
/// Slider widget orientation.
#[repr(u32)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum SliderOrientation {
/// Automatic based on dimensions.
Auto = lv_slider_orientation_t_LV_SLIDER_ORIENTATION_AUTO,
/// Force horizontal.
Horizontal = lv_slider_orientation_t_LV_SLIDER_ORIENTATION_HORIZONTAL,
/// Force vertical.
Vertical = lv_slider_orientation_t_LV_SLIDER_ORIENTATION_VERTICAL,
}
/// Slider operating mode.
#[repr(u32)]
#[derive(Clone, Copy, Debug)]
pub enum SliderMode {
/// Normal slider (single value).
Normal = lv_slider_mode_t_LV_SLIDER_MODE_NORMAL,
/// Symmetrical from center.
Symmetrical = lv_slider_mode_t_LV_SLIDER_MODE_SYMMETRICAL,
/// Range slider (two handles, start + end).
Range = lv_slider_mode_t_LV_SLIDER_MODE_RANGE,
}
impl<'p> Slider<'p> {
/// Create a new slider widget.
pub fn new(parent: &impl AsLvHandle) -> Result<Self, WidgetError> {
let parent_ptr = parent.lv_handle();
assert_ne!(parent_ptr, null_mut(), "Parent widget cannot be null");
// SAFETY: parent_ptr non-null (asserted above); lv_init() called via
// LvglDriver.
let handle = unsafe { lv_slider_create(parent_ptr) };
if handle.is_null() {
Err(WidgetError::LvglNullPointer)
} else {
Ok(Slider {
obj: Obj::from_raw(handle),
})
}
}
/// Returns the current slider value (native LVGL integer range).
pub fn get_value(&self) -> i32 {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_get_value(self.obj.handle()) }
}
/// Sets the slider range (min and max values).
pub fn set_range(&self, min: i32, max: i32) -> &Self {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
unsafe { lv_slider_set_range(self.obj.handle(), min, max) };
self
}
/// Sets the slider value (native LVGL integer range).
pub fn set_value(&self, val: i32) -> &Self {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_set_value(self.obj.handle(), val, false) };
self
}
/// Sets the slider value with optional slide animation.
pub fn set_value_animated(&self, val: i32, anim: bool) -> &Self {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_set_value(self.obj.handle(), val, anim) };
self
}
/// Set slider mode (normal, symmetrical, or range).
pub fn set_mode(&self, mode: SliderMode) -> &Self {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_set_mode(self.obj.handle(), mode as lv_slider_mode_t) };
self
}
/// Set the start value (left handle in range mode).
pub fn set_start_value(&self, val: i32) -> &Self {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_set_start_value(self.obj.handle(), val, false) };
self
}
/// Get the left/start value (range mode).
pub fn get_left_value(&self) -> i32 {
assert_ne!(
self.obj.handle(),
null_mut(),
"Slider handle cannot be null"
);
// SAFETY: handle non-null (asserted above).
unsafe { lv_slider_get_left_value(self.obj.handle()) }
}
/// Get the minimum value of the slider range.
pub fn get_min_value(&self) -> i32 {
// SAFETY: handle non-null (checked in new()).
unsafe { lv_slider_get_min_value(self.lv_handle()) }
}
/// Get the maximum value of the slider range.
pub fn get_max_value(&self) -> i32 {
// SAFETY: handle non-null (checked in new()).
unsafe { lv_slider_get_max_value(self.lv_handle()) }
}
/// Get the current slider mode.
pub fn get_mode(&self) -> SliderMode {
// SAFETY: handle non-null (checked in new()).
let raw = unsafe { lv_slider_get_mode(self.lv_handle()) };
match raw {
x if x == lv_slider_mode_t_LV_SLIDER_MODE_SYMMETRICAL => SliderMode::Symmetrical,
x if x == lv_slider_mode_t_LV_SLIDER_MODE_RANGE => SliderMode::Range,
_ => SliderMode::Normal,
}
}
/// Set the slider orientation.
pub fn set_orientation(&self, orientation: SliderOrientation) -> &Self {
// SAFETY: lv_handle() is non-null (checked in new()).
unsafe {
lv_slider_set_orientation(self.lv_handle(), orientation as lv_slider_orientation_t)
};
self
}
/// Get the slider orientation.
pub fn get_orientation(&self) -> SliderOrientation {
// SAFETY: lv_handle() is non-null (checked in new()).
let raw = unsafe { lv_slider_get_orientation(self.lv_handle()) };
match raw {
x if x == lv_slider_orientation_t_LV_SLIDER_ORIENTATION_HORIZONTAL => {
SliderOrientation::Horizontal
}
x if x == lv_slider_orientation_t_LV_SLIDER_ORIENTATION_VERTICAL => {
SliderOrientation::Vertical
}
_ => SliderOrientation::Auto,
}
}
/// Return `true` if the slider is currently being dragged.
pub fn is_dragged(&self) -> bool {
// SAFETY: handle non-null (checked in new()).
unsafe { lv_slider_is_dragged(self.lv_handle()) }
}
/// Bind slider value to an integer subject (two-way).
///
/// Slider changes update the subject and subject changes update the slider.
pub fn bind_value(&self, subject: &Subject) -> &Self {
// SAFETY: lv_handle() is non-null (checked in new()); subject pointer
// is pinned and valid for the subject's lifetime.
unsafe { lv_slider_bind_value(self.lv_handle(), subject.as_ptr()) };
self
}
}