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
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 FernTech
//! Tier-3 style protocol for `ScrollBar`. See `docs/styling-system.md`.
use std::rc::Rc;
use serde::{Deserialize, Serialize};
use crate::build_context::BuildContext;
use crate::signal::Signal;
use crate::widget_id::WidgetId;
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
pub enum ScrollBarOrientation {
#[default]
Vertical,
Horizontal,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
pub enum ScrollBarVariant {
/// Full track + thumb, always visible. The classic always-on scroll
/// bar; reserves layout space when used inside a parent that honours
/// its full thickness. The widget's historical default.
#[default]
Permanent,
/// Thin resting indicator at idle, cross-fades to the full track +
/// thumb on hover or drag. macOS / Ubuntu / IntUI overlay style.
Overlay,
/// Thin resting indicator only — never reveals the full bar.
/// A passive scroll-position display for minimal UIs; interaction
/// (drag, track click, keyboard) still works against the full slot
/// bounds even though only the thin strip is painted.
Thin,
}
#[derive(Clone, Debug)]
pub struct ScrollBarStyleConfig {
/// Normalized `0.0..=1.0` scroll position: `0.0` at the start of the
/// content, `1.0` at the end. Re-renders the body on every scroll.
pub scroll_ratio: Signal<f32>,
/// Visible viewport as a fraction of total content
/// (`viewport_size / content_size`). Drives the thumb size.
pub viewport_ratio: Signal<f32>,
/// `true` whenever the pointer is over the scroll bar's slot.
pub is_hovered: Signal<bool>,
/// `true` while the user is drag-pressing the thumb.
pub is_dragging: Signal<bool>,
/// `true` whenever the content is non-scrollable (`max_scroll == 0`).
/// Default IntUI paints nothing in this case; custom impls may choose
/// to keep a faint placeholder.
pub is_idle: Signal<bool>,
pub orientation: ScrollBarOrientation,
pub variant: ScrollBarVariant,
/// Minimum thumb length in logical pixels. Sourced from
/// `teksilo_widgets::styles::recipe_scroll_bar_style` constants by
/// default; apps override per-instance via `ScrollBar::min_thumb_length(...)`.
pub min_thumb_length: f32,
/// Optional thumb tint override (`ScrollBar::thumb_color` /
/// `ScrollArea::scroll_bar_thumb_color`). `None` (the default) means the
/// style paints from the theme's `scrollbar_thumb*` tokens. When set, the
/// style tints the thumb from this `ColorProp` instead — resolved against
/// the live theme at paint, so a role (e.g. `TextRole::TooltipText`) or a
/// `Signal` stays reactive. Lets chrome on a non-standard surface — a
/// tooltip's inverse chip, a branded panel — give the thumb a contrasting
/// colour the surface-relative tokens can't. Mirrors `Button::text_role`.
pub thumb_color: Option<crate::color_prop::ColorProp>,
}
pub trait ScrollBarStyle: 'static {
fn make_body(&self, cfg: &ScrollBarStyleConfig, ctx: &mut BuildContext) -> WidgetId;
}
pub type SharedScrollBarStyle = Rc<dyn ScrollBarStyle>;