Tryst::ValueSlider
Inherits Tryst::OwnerDrawnWidget < Reference < Object
A single-thumb value slider: rounded track with a filled portion, an antialiased thumb, optional tick marks with min/max labels, and a value bubble that tracks the thumb while dragging or keyboard- adjusting. Built on OwnerDrawnWidget - construct it, #pack/#grid it, #on_change it. Nothing about canvas, Surface, or ThorVG is part of its own public surface; see OwnerDrawnWidget's own doc comment for why #canvas itself is protected, not public.
Rendering split
Track, fill, ticks, and thumb are all drawn each #redraw through a
tryst-vector Surface, then blitted in as one Photo (OwnerDrawnWidget#
blit) - real antialiasing, real gradients if a future version wants
them. The bubble's CHROME (rounded body + pointer arrow) draws the
same way, but its TEXT is a real Tk label floated over the canvas
with place - tryst-vector doesn't expose text yet (see its own
README), and a native label gets correct font metrics/DPI/RTL for
free where hand-measuring glyph widths wouldn't. font: defaults to
Tk's own TkTextFont (whatever system UI font Tk resolves per
platform) rather than a hardcoded face, and is a real Tk font
spec/name so any of Tk's own named fonts or a literal
"family size" string works the same way it would on any other
widget. The label's own
background is set to match the drawn chrome exactly, and the chrome
is sized a few pixels larger than the label on every side, so the
label's own square corners land inside the chrome's straight edges
and only the chrome's rounded corners are ever visible past it - the
label itself can't be rounded, so the surrounding chrome is drawn
larger specifically to hide that fact.
Both the chrome and the label are a FIXED size, not shrink-to-fit around the current value's own text - see #initialize's own comment on why sizing them per-frame made the whole bubble visibly wobble while dragging.
Value flow
#value= sets a value programmatically and does NOT fire #on_change -
only a user-driven change (drag, click-to-position, keyboard) does,
the same "user action vs Crystal-driven set" split every other
stateful widget in this codebase draws (see Tryst::UI::Var#on_change
for the DSL-layer version of the same idea). There is currently no
bridge from this App-layer widget into that DSL-layer Var/bind:
machinery - a caller wanting two-way sync with one wires
slider.on_change { |v| var.value = v } and var.on_change { |v| slider.value = v }
itself.
Constants
Layout constants, all in logical pixels (always rendered at Surface's default scale: 1.0 - see #ensure_surface's own doc comment on why). Not exposed as options: a slider that wants a genuinely different look is a different widget, not a config surface on this one.
Constructors
Class methods
Snaps raw to the nearest step from min, then clamps to
[min, max] - the pure math every value change (drag, click,
keyboard, #value=) funnels through. A class method, not an
instance one, so it's testable with no App/Tk involved at all.
Instance methods
Releases the underlying canvas (and its Photo, if any) now, rather than waiting for a collection - same contract as Photo#delete. Bind-callback cleanup for #canvas's own path happens for free (see App's setup_destroy_cleanup, installed unconditionally for every widget); only this class's own extra state (running tweens) needs explicit teardown here.
Fires on every user-driven change (drag, click-to-position, arrow/ Home/End/PageUp/PageDown keys) - never for a programmatic #value=.
Subclasses draw here - called after every resize and every state change (#hover?/#pressed?/#focused?/#disabled? all changing). Given no arguments deliberately: #canvas's own current width/height (via #canvas.width/#canvas.height, i.e. real winfo queries) are always the authority, not a value that could go stale between when a resize fired and when this actually runs.
The current value. See this class's own doc comment on why setting this does NOT fire #on_change.