Class Spinner
Spinner.Mode.NUMERIC:new Spinner(min, max, step)shows a number (decimals inferred from the step);Spinner.Mode.TIME:time()showsHH:MM, where the up/down arrows adjust the focused field (hours by 60, minutes by the step) and Left/Right (or a click) move between the two fields.
Adjust with the up/down buttons or the keyboard:
- Up/Down, and Left/Right in numeric mode = ±step;
- Shift + arrow = ±10 steps, the same as PageUp/PageDown;
- Alt + arrow = one unit of the last displayed digit, for the values a step deliberately overshoots. Alt wins if Shift is also held;
- PageUp/PageDown = ±10 steps, Home/End = min/max.
[min, max]; onChange fires on user changes.
The value can also be typed. Type a digit and the spinner turns into a
one-line text field with the old value selected, so the first keystroke replaces it;
click into the value to place the caret instead. Enter commits, Escape
restores, and moving focus away commits. Text that does not parse commits nothing and
the value stands; a number outside the bounds is clamped.
Copy and paste work in both states. With no edit in progress
Ctrl/Cmd+C copies the whole value as shown; while editing it copies the
selection, and Ctrl/Cmd+X cuts it. Pasting replaces the value when nothing is
being edited and inserts at the caret when something is. Within an edit,
Ctrl/Cmd+Z and Ctrl/Cmd+Y undo and redo the typing.
Stepping never goes away: Up/Down step even mid-edit, taking the typed text as the
number to step from, and Left/Right resume stepping (or switching time fields) once
the edit is committed. setEditable(boolean) turns typing off entirely.
A programmatic setValue(double) cancels an edit in progress, because the
half-typed text is no longer about the number now in the field and committing it later
would silently undo whatever moved it.
The mouse wheel does not change the value, and must not: a spinner in a scrolling panel would otherwise swallow the wheel whenever the pointer crossed it, stopping the scroll and editing a field the user was only scrolling past. The wheel passes through to the scrollable ancestor.
Reading right to left the stepper column and the value swap sides, and the arrows
that name the value swap with them: Left raises it and Right lowers it, because the
low end of a horizontal axis sits on the side reading starts from. Home and End do not swap;
they name min and max, which are not sides. Neither do time mode's Left and
Right: those pick the hours and the minutes of an HH:MM run, and a run of digits keeps
its own left-to-right order inside a right-to-left form, so the fields do not move and neither
does the key that selects one. The up and down arrows are drawn on the vertical axis and are
unchanged.
Sizes follow the widget's ControlSize. At XSMALL each
stepper half is 18 × 12 pt, below fine-motor comfort, so a dense form should treat the
keyboard as the primary way to adjust the value there.
Spinner qty = new Spinner(0, 99, 1).setValue(1);
Spinner alarm = Spinner.time().setValue(7 * 60 + 30); // 07:30
-
Nested Class Summary
Nested Classes -
Constructor Summary
ConstructorsConstructorDescriptionSpinner(double min, double max, double step) Numeric spinner over[min, max]stepping bystep(> 0). -
Method Summary
Modifier and TypeMethodDescriptionprotected floatDistance from this widget's top edge to its first text baseline, in logical points: the alignment reference forFlex.CrossAlignment.BASELINE.booleanWhether the value can be typed as well as stepped.booleanWhether the user is typing into the value right now.doublemax()Upper bound, inclusive.doublemin()Lower bound, inclusive.mode()Whether this spinner shows a number or a time.Called with the new value on user changes only, not onsetValue(double).protected voidonCharTyped(CharEvent event) Committed text input (focused widget first, then ancestors).protected voidCalled when this widget takes keyboard focus.protected voidCalled when this widget loses keyboard focus.protected voidonKeyEvent(KeyEvent event) Key events (focused widget first, then ancestors).protected SizeonMeasure(Constraints constraints) Reports the size this widget wants withinconstraints.protected voidonMouseEvent(MouseEvent event) Mouse events (bubbling); callevent.consume()when handled.protected voidWidget's own background/content, in local coordinates.setEditable(boolean canType) Whether the value can be typed into (defaulttrue).setSnapToStep(boolean snap) Whether values are snapped onto the step grid (min + k * step).setValue(double newValue) Sets the value programmatically (clamped + snapped); does not fireonChange.booleanWhether committed values are rounded to a multiple of the step.text()static Spinnertime()A 24-hourHH:MMtime spinner (00:00–23:59, 1-minute step).static Spinnertime(int minMinutes, int maxMinutes, int stepMinutes) A time spinner bounded to[minMinutes, maxMinutes], stepping the minutes field bystepMinutes.doublevalue()The current value, always within[min, max].Methods inherited from class limn.scene.Widget
acceptsTextInput, add, baselineOffsetOf, caretRect, children, clipboard, clipsChildren, controlSize, cursor, declaredControlSize, declaredLayoutDirection, declaredLocale, focusArrivedByTraversal, height, hitTest, imageCursor, invalidate, invalidate, isEnabled, isFocusable, isFocused, isShowing, isVisible, layoutBox, layoutDirection, locale, localToSceneX, localToSceneY, markNeedsContainedLayout, markNeedsLayout, measure, moveChild, neutralBase, onAttached, onDetached, onFileDrop, onLayout, onPaintOverlay, onPreedit, overlayPassesPointer, paintChildren, paintOutset, paintWidget, parent, remove, requestFocus, revealInView, scene, sceneToLocalX, sceneToLocalY, setControlSize, setCursor, setEnabled, setFocusable, setImageCursor, setInheritanceHost, setLayoutDirection, setLocale, setTooltip, setTooltip, setVisible, shapeText, textRuler, tooltip, width, x, y
-
Constructor Details
-
Spinner
public Spinner(double min, double max, double step) Numeric spinner over[min, max]stepping bystep(> 0).
-
-
Method Details
-
time
A 24-hourHH:MMtime spinner (00:00–23:59, 1-minute step). -
time
A time spinner bounded to[minMinutes, maxMinutes], stepping the minutes field bystepMinutes. -
setValue
Sets the value programmatically (clamped + snapped); does not fireonChange. Cancels an edit in progress; see the class comment. -
setEditable
Whether the value can be typed into (defaulttrue). Turning it off leaves a pure stepper: no caret, no clipboard, and Left/Right keep stepping (or switching time fields) as they always did. -
isEditable
public boolean isEditable()Whether the value can be typed as well as stepped. -
isEditing
public boolean isEditing()Whether the user is typing into the value right now. -
value
public double value()The current value, always within[min, max]. Minutes since midnight in time mode. -
setSnapToStep
Whether values are snapped onto the step grid (min + k * step). On by default: a spinner that owns its value keeps it tidy.Turn it off when the spinner displays a value it does not own: a property inspector bound to a document, a field showing a number that came from a file or another tool. Snapping would silently misreport such a value (
1200shown as1201on a grid anchored at 1), and rewrite it the moment the user nudges the field. Stepping still moves bystep; it just does so from the value actually held. -
snapsToStep
public boolean snapsToStep()Whether committed values are rounded to a multiple of the step. -
min
public double min()Lower bound, inclusive. -
max
public double max()Upper bound, inclusive. -
mode
Whether this spinner shows a number or a time. -
onChange
Called with the new value on user changes only, not onsetValue(double). -
text
- Returns:
- the value as it is displayed (
"07:30"in time mode, a number otherwise).
-
baselineOffset
protected float baselineOffset()Description copied from class:WidgetDistance from this widget's top edge to its first text baseline, in logical points: the alignment reference forFlex.CrossAlignment.BASELINE. Defaultheight(): align on the bottom edge, the correct fallback for a widget with no text. Text-bearing components override with the expression they already paint with,(height() - metrics.height()) / 2 + metrics.ascent().Valid only once this widget has been given a box.
Flexguarantees that: it lays every child of a BASELINE line out at cross position 0 first, reads the baselines, then repositions withWidget.moveChild(limn.scene.Widget, float, float), which moves without re-running layout.- Overrides:
baselineOffsetin classWidget
-
onMeasure
Description copied from class:WidgetReports the size this widget wants withinconstraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.Resolve the
ControlSizeand theLayoutDirectiononce each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, andI18n.locale()answers it wherever text is resolved. -
onPaint
Description copied from class:WidgetWidget's own background/content, in local coordinates. -
onMouseEvent
Description copied from class:WidgetMouse events (bubbling); callevent.consume()when handled.- Overrides:
onMouseEventin classWidget
-
onCharTyped
Description copied from class:WidgetCommitted text input (focused widget first, then ancestors).- Overrides:
onCharTypedin classWidget
-
onKeyEvent
Description copied from class:WidgetKey events (focused widget first, then ancestors).- Overrides:
onKeyEventin classWidget
-
onFocusGained
protected void onFocusGained()Description copied from class:WidgetCalled when this widget takes keyboard focus. Default: nothing.- Overrides:
onFocusGainedin classWidget
-
onFocusLost
protected void onFocusLost()Description copied from class:WidgetCalled when this widget loses keyboard focus. Default: nothing.- Overrides:
onFocusLostin classWidget
-