Class Checkbox

java.lang.Object
limn.scene.Widget<Checkbox>
limn.components.Checkbox

public final class Checkbox extends Widget<Checkbox>
Toggle with two visual variants: the classic BOX with a check mark, and the mobile-style SWITCH whose thumb slides. The check/slide is driven by a shared Transition (the toolkit's reusable animator), so it only renders while moving. Toggled by click, or Space/Enter when focused. Colours come from the Theme, extents from the SizeTokens row resolved on this widget, and every pen from Strokes.

This row is under the 24 pt pointer target

The row measures max(indicator, lineHeight) and the indicator wins that max at every step (18 pt at MEDIUM for BOX, 22 for SWITCH), which is below the 24 pt target of WCAG 2.2 SC 2.5.8 (AA) on the axis that decides it: a label widens a target, it never heightens it. A toggle's pointer target is exactly the box it paints; there is no hit outset.

The standard's own Spacing exception is what an application relies on: an undersized target conforms while a 24 pt circle centred on it clears every neighbour. A lone toggle already satisfies that; a column of them does not until the pitch reaches 24, which is what Tokens.toggleColumnGap(limn.scene.Widget) gives. Stack toggles on that gap, not a tighter one.

Which way the row reads

The indicator sits on the leading edge and the label follows it, so the whole row moves to the other side when the resolved direction is right to left. That move is a translation of the indicator and nothing else: the check mark is a tick, and no platform mirrors a tick. The thumb of a SWITCH is the one thing inside the indicator that does know a direction, because its two ends are a value axis: OFF is the leading end and ON the trailing one.
  • Constructor Details

  • Method Details

    • onChange

      public Checkbox onChange(@Nullable Consumer<Boolean> listener)
      The application's response to the user toggling the box: a click, Space, Enter or an assistive technology's toggle. Never for setChecked(boolean) or toggle(), which are a caller's writes; to hear every flip whatever caused it, watch the checkbox instead.
      Parameters:
      listener - the handler, or null to clear the slot
      Returns:
      this checkbox
      Throws:
      IllegalStateException - if a handler is already registered
    • handleUserChange

      protected void handleUserChange(Change.Aspect aspect)
      Description copied from class: Widget
      Runs this widget's application handler for a change it has just announced. Called by Widget.runHandler(limn.scene.Change), and so by Widget.notifyChange(limn.scene.Change), only for a USER origin and after every watcher has run.

      A component overrides it once and switches on the aspect, reading its own accessors for the payload: a slider passes value(), a combo box selectedIndex(), a text field text(). A component with no handler does not override it.

      An override that does not recognise the aspect must call super, because a subclass of a component that has a handler inherits that component's dispatch through this method: a SearchField that overrode this for its own aspect and did not chain would silently delete TextField's. The ordering lives in the base class, this is the one link in it a subclass can break, and the contract test drives every subclass through its parent's gestures to catch one that does.

      Overrides:
      handleUserChange in class Widget<Checkbox>
      Parameters:
      aspect - what changed; the origin is USER or this is not called
    • text

      public String text()
      Returns:
      the caption beside the indicator, resolved in this widget's own language. The accessible tree takes this as the control's name, so a caption that is not reachable is a control a screen reader cannot announce.
    • textSource

      public I18nString textSource()
      Returns:
      the localizable string behind text(). The "is it set here" reader, matching the pair Button and Label expose: a name has to be re-resolvable when the translation epoch or the subtree's language moves, and a private field with no getter cannot be.
    • variant

      public Checkbox.Variant variant()
      Returns:
      how the toggle is drawn: a box with a check mark or a switch
    • isChecked

      public boolean isChecked()
      The current state.
    • setChecked

      public Checkbox setChecked(boolean newChecked)
      Sets the state, animating the visual transition. Announces VALUE/CODE when it moved, and reaches no handler.
    • toggle

      public void toggle()
      Flips the state: a CODE write spelled as a verb, exactly like setChecked(boolean), so it announces VALUE/CODE and reaches no handler. It carries no enabled guard for the same reason setChecked carries none: a write may move a disabled widget's state and always could, and the entry point a gesture or an assistive technology reaches is guarded already. UI thread only.
    • onMeasure

      protected Size onMeasure(Constraints constraints)
      Description copied from class: Widget
      Reports the size this widget wants within constraints. 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 ControlSize and the LayoutDirection once each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, and I18n.locale() answers it wherever text is resolved.

      Specified by:
      onMeasure in class Widget<Checkbox>
    • onPaint

      protected void onPaint(Canvas canvas)
      Description copied from class: Widget
      Widget's own background/content, in local coordinates.
      Overrides:
      onPaint in class Widget<Checkbox>
    • paintOutset

      protected float paintOutset()
      The focus ring is the only thing that paints outside the box, and it paints well outside: a Strokes.FOCUS_RING_THIN pen beyond a clear gap of Strokes.FOCUS_GAP_INDICATOR, its outer ink 3pt past the indicator. The indicator is flush with the widget's leading edge, with its top and bottom at every step (the row is the indicator), and with the trailing edge when there is no label, so that reach is outside bounds on all four sides whichever way the row reads. Scene assumes only 1pt of AA feather, so the fading ring left stale pixels under partial rendering even at a 1pt gap (reach 1.75); the 1.5pt gap widens the gap to 1.5 and the reach with it. Locked, like both quantities it is composed of.
      Overrides:
      paintOutset in class Widget<Checkbox>
    • baselineOffset

      protected float baselineOffset()
      Description copied from class: Widget
      Distance from this widget's top edge to its first text baseline, in logical points: the alignment reference for Flex.CrossAlignment.BASELINE. Default height(): 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; a single-line control's is textRuler().centredBaseline(font, height()), which stands the line's capitals in the middle of the box.

      Valid only once this widget has been given a box. Flex guarantees that: it lays every child of a BASELINE line out at cross position 0 first, reads the baselines, then repositions with Widget.moveChild(limn.scene.Widget<?>, int), which moves without re-running layout.

      Overrides:
      baselineOffset in class Widget<Checkbox>
    • onAccessibility

      protected void onAccessibility(Accessibility a)
      What this toggle is to an assistive technology: one Accessible.Role.CHECK_BOX or Accessible.Role.SWITCH, by variant, named by the caption it holds, carrying a two-state toggle facet and offering Accessible.Action.TOGGLE, and nothing else.

      The name is the held I18nString handed over by reference, never text(): the walk compares the reference, the locale and the translation epoch, so a hover or focus fade that damages this row on every frame concludes that nothing moved without allocating. It is handed over even when the caption is empty, because the walk names a nameless node from its tooltip and describes a named one with it; a caption-less toggle with no tooltip publishes an empty name, which is the application's to fix and not this widget's to invent.

      The toggle facet reads the checked field and never the eased visual: the model flips and the handler fires before the animation starts, so a reader asking mid-slide is told what the application already knows. There is no third state, so the facet is on or off and the checked bit follows it. The caption is not a child node: the indicator and its label are one control, and a label node beside it would be announced twice. The box, the focus and scroll-into-view verbs and the enabled, visible, showing, focusable and focused states are the walk's; Space and Enter are the platform's own convention for a toggle, not an accelerator, so no key binding is declared.

      Overrides:
      onAccessibility in class Widget<Checkbox>
      Parameters:
      a - the node being described
    • onAccessibilityAction

      protected boolean onAccessibilityAction(Accessible.Action action, Accessible.Argument arg)
      Performs a toggle an assistive technology asked for, exactly as a click or a Space press does: through the from-the-user seam, so the application's onChange handler is notified the same way, and never through setChecked(boolean) or toggle(), which are a caller's writes. Any other verb is refused, and so is a toggle while this widget is disabled, because the answer here has to be truthful.

      The state change that results is what a reader hears: the flip invalidates, the next frame republishes, and the difference raises the checked-state event on this node. No invocation event is raised for a toggle, on any platform.

      Overrides:
      onAccessibilityAction in class Widget<Checkbox>
      Parameters:
      action - what was asked
      arg - ignored; a toggle carries none
      Returns:
      whether the toggle ran
    • onMouseEvent

      protected void onMouseEvent(MouseEvent event)
      Description copied from class: Widget
      Mouse events (bubbling); call event.consume() when handled.
      Overrides:
      onMouseEvent in class Widget<Checkbox>
    • onKeyEvent

      protected void onKeyEvent(KeyEvent event)
      Description copied from class: Widget
      Key events (focused widget first, then ancestors).
      Overrides:
      onKeyEvent in class Widget<Checkbox>
    • onFocusGained

      protected void onFocusGained()
      Description copied from class: Widget
      Called when this widget takes keyboard focus. Default: nothing.
      Overrides:
      onFocusGained in class Widget<Checkbox>
    • onFocusLost

      protected void onFocusLost()
      Description copied from class: Widget
      Called when this widget loses keyboard focus. Default: nothing.
      Overrides:
      onFocusLost in class Widget<Checkbox>