teksilo_core/styles/slider_style.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Tier-3 style protocol for `Slider`. See `docs/styling-system.md`.
5
6use std::rc::Rc;
7
8use serde::{Deserialize, Serialize};
9
10use crate::build_context::BuildContext;
11use crate::focus::FocusOrigin;
12use crate::signal::Signal;
13use crate::widget_id::WidgetId;
14
15#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
16pub enum SliderVariant {
17 #[default]
18 Continuous,
19 /// Snaps to discrete tick positions; the style typically paints
20 /// the ticks above/below the track.
21 Discrete,
22 /// Two thumbs: the value is a `(low, high)` range. Here for
23 /// completeness; the IntUI default impl does NOT yet wire range
24 /// behaviour — apps that need range sliders write a custom
25 /// impl.
26 Range,
27}
28
29/// Slider orientation. Horizontal is the default; the value
30/// progresses left → right (or right → left in RTL — slider doesn't
31/// flip today, that's a known follow-up).
32#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
33pub enum SliderOrientation {
34 #[default]
35 Horizontal,
36 Vertical,
37}
38
39#[derive(Clone, Debug)]
40pub struct SliderStyleConfig {
41 /// Normalized `0.0..=1.0` thumb position.
42 pub value_normalized: Signal<f32>,
43 pub is_hovered: Signal<bool>,
44 /// `true` while the user is drag-pressing the thumb.
45 pub is_dragging: Signal<bool>,
46 pub is_disabled: Signal<bool>,
47 /// `Some(FocusOrigin::Keyboard)` while the slider has keyboard
48 /// focus; the IntUI default uses this to gate the focus ring on
49 /// the thumb. `Some(Pointer)` and `None` skip the ring.
50 pub focus_origin: Signal<Option<FocusOrigin>>,
51 pub orientation: SliderOrientation,
52 /// `Some(n)` ⇒ Discrete with `n` ticks; `None` ⇒ Continuous.
53 pub tick_count: Option<u32>,
54 pub variant: SliderVariant,
55}
56
57pub trait SliderStyle: 'static {
58 fn make_body(&self, cfg: &SliderStyleConfig, ctx: &mut BuildContext) -> WidgetId;
59
60 /// Diameter, in logical pixels, of the draggable thumb produced by
61 /// `make_body`. The host `Slider` widget uses this to compute the drag
62 /// hit-region and the position→value mapping at event time (when it can
63 /// no longer reach the theme). The default matches the IntUI recipe's
64 /// thumb; a custom style that paints a different thumb size MUST override
65 /// this too, or dragging will map to the wrong pixel boundary.
66 fn thumb_diameter(&self, _cfg: &SliderStyleConfig) -> f32 {
67 14.0
68 }
69}
70
71pub type SharedSliderStyle = Rc<dyn SliderStyle>;