teksilo_core/styles/scroll_bar_style.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Tier-3 style protocol for `ScrollBar`. See `docs/styling-system.md`.
5
6use std::rc::Rc;
7
8use serde::{Deserialize, Serialize};
9
10use crate::build_context::BuildContext;
11use crate::signal::Signal;
12use crate::widget_id::WidgetId;
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
15pub enum ScrollBarOrientation {
16 #[default]
17 Vertical,
18 Horizontal,
19}
20
21#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
22pub enum ScrollBarVariant {
23 /// Full track + thumb, always visible. The classic always-on scroll
24 /// bar; reserves layout space when used inside a parent that honours
25 /// its full thickness. The widget's historical default.
26 #[default]
27 Permanent,
28 /// Thin resting indicator at idle, cross-fades to the full track +
29 /// thumb on hover or drag. macOS / Ubuntu / IntUI overlay style.
30 Overlay,
31 /// Thin resting indicator only — never reveals the full bar.
32 /// A passive scroll-position display for minimal UIs; interaction
33 /// (drag, track click, keyboard) still works against the full slot
34 /// bounds even though only the thin strip is painted.
35 Thin,
36}
37
38#[derive(Clone, Debug)]
39pub struct ScrollBarStyleConfig {
40 /// Normalized `0.0..=1.0` scroll position: `0.0` at the start of the
41 /// content, `1.0` at the end. Re-renders the body on every scroll.
42 pub scroll_ratio: Signal<f32>,
43 /// Visible viewport as a fraction of total content
44 /// (`viewport_size / content_size`). Drives the thumb size.
45 pub viewport_ratio: Signal<f32>,
46 /// `true` whenever the pointer is over the scroll bar's slot.
47 pub is_hovered: Signal<bool>,
48 /// `true` while the user is drag-pressing the thumb.
49 pub is_dragging: Signal<bool>,
50 /// `true` whenever the content is non-scrollable (`max_scroll == 0`).
51 /// Default IntUI paints nothing in this case; custom impls may choose
52 /// to keep a faint placeholder.
53 pub is_idle: Signal<bool>,
54 pub orientation: ScrollBarOrientation,
55 pub variant: ScrollBarVariant,
56 /// Minimum thumb length in logical pixels. Sourced from
57 /// `teksilo_widgets::styles::recipe_scroll_bar_style` constants by
58 /// default; apps override per-instance via `ScrollBar::min_thumb_length(...)`.
59 pub min_thumb_length: f32,
60 /// Optional thumb tint override (`ScrollBar::thumb_color` /
61 /// `ScrollArea::scroll_bar_thumb_color`). `None` (the default) means the
62 /// style paints from the theme's `scrollbar_thumb*` tokens. When set, the
63 /// style tints the thumb from this `ColorProp` instead — resolved against
64 /// the live theme at paint, so a role (e.g. `TextRole::TooltipText`) or a
65 /// `Signal` stays reactive. Lets chrome on a non-standard surface — a
66 /// tooltip's inverse chip, a branded panel — give the thumb a contrasting
67 /// colour the surface-relative tokens can't. Mirrors `Button::text_role`.
68 pub thumb_color: Option<crate::color_prop::ColorProp>,
69}
70
71pub trait ScrollBarStyle: 'static {
72 fn make_body(&self, cfg: &ScrollBarStyleConfig, ctx: &mut BuildContext) -> WidgetId;
73}
74
75pub type SharedScrollBarStyle = Rc<dyn ScrollBarStyle>;