teksilo_core/styles/splitter_style.rs
1// SPDX-License-Identifier: MPL-2.0
2// SPDX-FileCopyrightText: 2026 FernTech
3
4//! Tier-3 style protocol for `Splitter`. See `docs/styling-system.md`.
5//!
6//! The `Splitter` widget owns all input handling (drag, keyboard,
7//! collapse, accessibility) and delegates only the *paint* of each
8//! divider handle to a [`SplitterStyle`] impl. Layout-affecting
9//! dimensions (gutter thickness, min pane sizes, keyboard step, snap
10//! offset) live on the `SplitterModel`, not here — the styling-system
11//! rule is that anything consumed by `place_children` must be resolved
12//! before layout, whereas a style body is built lazily and paints at
13//! paint time.
14//!
15//! The IntUI default ([`crate::styles`]'s `RecipeSplitterStyle`, in
16//! `teksilo-widgets`) reproduces the thin-line-with-hover-dwell-focus
17//! look out of the box; apps install a different look per-call via
18//! `Splitter::style(...)` or theme-wide via `theme.style_slots.splitter`.
19
20use std::rc::Rc;
21
22use teksilo_tokens::Orientation;
23
24use crate::build_context::BuildContext;
25use crate::focus::FocusOrigin;
26use crate::signal::Signal;
27use crate::widget_id::WidgetId;
28
29/// Reactive inputs handed to a [`SplitterStyle`] when it builds one
30/// divider handle's visual body. Every field a default impl repaints on
31/// is a `Signal`, so the body re-renders without a rebuild.
32#[derive(Clone, Debug)]
33pub struct SplitterStyleConfig {
34 /// Orientation of the parent `Splitter`. Note: a *horizontal*
35 /// splitter (panes side-by-side) draws a *vertical* handle bar, and
36 /// vice versa — the recipe accounts for this when painting the line.
37 pub orientation: Orientation,
38 pub is_hovered: Signal<bool>,
39 /// `true` while the user is drag-pressing this handle.
40 pub is_dragging: Signal<bool>,
41 pub is_disabled: Signal<bool>,
42 /// `Some(FocusOrigin::Keyboard)` while the handle has keyboard
43 /// focus; the IntUI default uses this to gate the full-strength
44 /// focus indicator. `Some(Pointer)` and `None` fall back to the
45 /// hover-dwell ramp.
46 pub focus_origin: Signal<Option<FocusOrigin>>,
47 /// Hover-dwell progress `0.0..=1.0` driving the focus-indicator
48 /// fade-in (animated by the handle; the style maps it to alpha).
49 pub hover_progress: Signal<f32>,
50}
51
52/// The visual contract for a `Splitter` divider handle.
53///
54/// `make_handle` returns the `WidgetId` of a leaf (or subtree) that
55/// paints the divider chrome. The host `Splitter` sizes it to the
56/// model's gutter thickness × the cross axis and routes all input
57/// itself — the body is purely presentational and should mark itself
58/// hidden from the accessibility tree (the splitter handle owns the
59/// `Role::Splitter` node).
60pub trait SplitterStyle: 'static {
61 fn make_handle(&self, cfg: &SplitterStyleConfig, ctx: &mut BuildContext) -> WidgetId;
62}
63
64pub type SharedSplitterStyle = Rc<dyn SplitterStyle>;