Class Checkbox

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

public class Checkbox extends Widget
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(Consumer<Boolean> listener)
      Called with the new state on user toggles only, not on setChecked(boolean).
    • isChecked

      public boolean isChecked()
      The current state.
    • setChecked

      public Checkbox setChecked(boolean newChecked)
      Sets the state, animating the visual transition.
    • toggle

      public void toggle()
      Flips the state and fires onChange, as a click does. 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
    • onPaint

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

      protected float paintOutset()
      The focus ring is the only thing that paints outside the box, and it paints well outside: centred Strokes.FOCUS_GAP_INDICATOR out with a Strokes.FOCUS_RING_THIN pen, its outer ink reaches 2.25pt 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
    • 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, (height() - metrics.height()) / 2 + metrics.ascent().

      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, float, float), which moves without re-running layout.

      Overrides:
      baselineOffset in class Widget
    • onMouseEvent

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

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

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

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