Class RadioButton
ButtonGroup are mutually exclusive: selecting one deselects the rest.
Unlike a Checkbox, clicking a selected radio does not turn it
off. Selected by click, or Space/Enter when focused. Colours from Theme,
extents from the SizeTokens row resolved on this widget, pens from
Strokes.
The ring rides indicator, the same token as Checkbox's
box, so a form mixing the two keeps every label on one optical column at every
step.
The ring sits on the row's leading edge with the label running away from it, so
both swap sides with the LayoutDirection resolved on this widget. The ring
is a circle and the dot is concentric with it, so nothing inside the indicator has a side to
reflect; only the column the pair occupies moves.
This row is under the 24 pt pointer target
The row ismax(indicator, lineHeight) rather than the control-height ramp, and the
indicator wins that max at every step (18 pt at MEDIUM), 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 radio's pointer target is exactly the ring-and-label box it paints.
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 radio already satisfies that; a group (the normal case) does not until the pitch
reaches 24, which is what Tokens.toggleColumnGap(limn.scene.Widget) gives. Stack
a ButtonGroup's radios on that gap, not a tighter one.
-
Constructor Summary
ConstructorsConstructorDescriptionRadioButton(String text) A radio with a fixed label; see theI18nStringconstructor for localized text.RadioButton(I18nString text) A radio button whose label follows the UI language; seeI18nString. -
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.protected voidhandleUserChange(Change.Aspect aspect) Runs this widget's application handler for a change it has just announced.booleanWhether this radio is the selected one in its group.protected voidWhat this radio is to an assistive technology: oneAccessible.Role.RADIO_BUTTON, named by the caption it holds, a member of its group's selection with its one-based position and the group's size, offeringAccessible.Action.SELECT, and nothing else.protected booleanonAccessibilityAction(Accessible.Action action, Accessible.Argument arg) Performs a select an assistive technology asked for, exactly as a click or a Space press does: through the seam atUSER, so a group deselects the previous member, both radios'onChangehandlers and the group's own listener are notified the same way, and the tab stop moves with the selection.protected voidCalled when this widget enters a scene (attached to the tree).Fires withtruewhen this radio becomes selected,falsewhen a sibling takes over.protected voidA group does not hold a member that is out of the scene: a form rebuilt against the same group would otherwise leave it every radio of every form before, each still counted.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.protected floatThe focus circle is the only thing that paints outside the box: radiusring/2 +Strokes.FOCUS_GAP_INDICATOR+ pen/2with a centredStrokes.FOCUS_RING_THINpen puts its outer ink 3pt past the indicator, which is flush with the widget's leading edge — the left one reading left to right and the right one reading right to left — and (since the row is the indicator at every step) with its top and bottom.voidselect()Selects this radio (idempotent).Replaces the label with a fixed string.setText(I18nString newText) Replaces the label with one that follows the UI language.text()Methods inherited from class limn.scene.Widget
acceptsTextInput, accessibleLabelAccessKey, accessibleLabelTarget, accessibleLabelText, add, add, announceChange, backdropReach, baselineOffsetOf, caretRect, children, clipboard, clipHeight, clipsChildren, clipWidth, clipX, clipY, controlSize, cursor, declaredControlSize, declaredLayoutDirection, declaredLocale, focusArrivedBackward, focusArrivedByTraversal, height, hitTest, hitTestChild, id, imageCursor, inheritanceHost, invalidate, invalidate, invalidateAccessible, isEnabled, isFocusable, isFocused, isRightToLeft, isSelfEnabled, isShowing, isVisible, isVisibleByItsOwnFlag, layoutBox, layoutDirection, locale, localToScene, localToSceneBounds, markNeedsContainedLayout, markNeedsLayout, markNeedsLayoutInPlace, maxHeight, maxWidth, measure, minHeight, minWidth, moveChild, moveChild, moveChild, neutralBase, notifyChange, notifyTextEdit, observeChanges, onAccessibilityChild, onAccessibilityChildAction, onAccessibilityChildIdentity, onCanEdit, onCharTyped, onDragExit, onDragOver, onDragStart, onDrop, onEdit, onHitTest, onLayout, onPaintOverlay, onPreedit, onSyntheticAction, overlayPassesPointer, paintChildren, paintsDecoration, paintsFromBackdrop, paintWidget, parent, remove, remove, requestFocus, requestFocus, revealInView, revealInView, runHandler, scene, sceneNanos, sceneToLocal, self, setAccessibleDescribedBy, setAccessibleDescription, setAccessibleDescription, setAccessibleIgnored, setAccessibleLabelledBy, setAccessibleName, setAccessibleName, setAccessibleRole, setControlSize, setCursor, setEnabled, setFocusable, setId, setImageCursor, setInheritanceHost, setLayoutDirection, setLocale, setMaxHeight, setMaxWidth, setMinHeight, setMinWidth, setPresentedElsewhere, setTooltip, setTooltip, setVisible, shapeText, textRuler, tooltip, tooltipSource, width, x, y
-
Constructor Details
-
RadioButton
A radio with a fixed label; see theI18nStringconstructor for localized text. -
RadioButton
A radio button whose label follows the UI language; seeI18nString.
-
-
Method Details
-
onChange
Fires withtruewhen this radio becomes selected,falsewhen a sibling takes over.One handler at a time:
nullclears it, and setting a second while one is set throwsIllegalStateException. -
handleUserChange
Description copied from class:WidgetRuns this widget's application handler for a change it has just announced. Called byWidget.runHandler(limn.scene.Change), and so byWidget.notifyChange(limn.scene.Change), only for aUSERorigin 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 boxselectedIndex(), a text fieldtext(). 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: aSearchFieldthat overrode this for its own aspect and did not chain would silently deleteTextField'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:
handleUserChangein classWidget<RadioButton>- Parameters:
aspect- what changed; the origin isUSERor this is not called
-
text
- Returns:
- the caption beside the indicator, resolved in this widget's own language. The accessible tree takes this as the control's name.
-
textSource
-
setText
Replaces the label with a fixed string. UI thread only. -
setText
Replaces the label with one that follows the UI language. UI thread only.- Parameters:
newText- the new caption- Returns:
- this radio button
- Throws:
NullPointerException- ifnewTextisnull
-
isSelected
public boolean isSelected()Whether this radio is the selected one in its group. -
select
public void select()Selects this radio (idempotent). In a group, deselects the previously selected sibling and tells the group; standalone, just selects itself. ACODEwrite spelled as a verb: it announcesVALUE/CODEon every member it moves and reaches no handler, exactly asButtonGroup.setSelectedIndex(int)does. -
onDetached
protected void onDetached()A group does not hold a member that is out of the scene: a form rebuilt against the same group would otherwise leave it every radio of every form before, each still counted.- Overrides:
onDetachedin classWidget<RadioButton>
-
onAttached
protected void onAttached()Description copied from class:WidgetCalled when this widget enters a scene (attached to the tree).Widget.scene()is the scene it just joined. Fires top-down, so a parent runs before its children. Default no-op.- Overrides:
onAttachedin classWidget<RadioButton>
-
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.- Specified by:
onMeasurein classWidget<RadioButton>
-
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; a single-line control's istextRuler().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.
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<?>, int), which moves without re-running layout.- Overrides:
baselineOffsetin classWidget<RadioButton>
-
onPaint
Description copied from class:WidgetWidget's own background/content, in local coordinates.- Overrides:
onPaintin classWidget<RadioButton>
-
paintOutset
protected float paintOutset()The focus circle is the only thing that paints outside the box: radiusring/2 +Strokes.FOCUS_GAP_INDICATOR+ pen/2with a centredStrokes.FOCUS_RING_THINpen puts its outer ink 3pt past the indicator, which is flush with the widget's leading edge — the left one reading left to right and the right one reading right to left — and (since the row is the indicator at every step) with its top and bottom. One outset covers every side, so the edge the indicator moves to is already inside the damage and the direction is not an input here.Sceneassumes only 1pt of AA feather, so without this the fading ring left stale pixels under partial rendering. Deliberately the same expression asCheckbox's: the two are in declared lockstep and must damage the same rectangle, or a form column mixing them repaints unevenly.- Overrides:
paintOutsetin classWidget<RadioButton>
-
onAccessibility
What this radio is to an assistive technology: oneAccessible.Role.RADIO_BUTTON, named by the caption it holds, a member of its group's selection with its one-based position and the group's size, offeringAccessible.Action.SELECT, and nothing else.The name is the held
I18nStringhanded over by reference, nevertext(): 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 radio with no tooltip publishes an empty name, which is the application's to fix and not this widget's to invent.The selection facet reads the
selectedfield and never the eased dot, and its two numbers come from the group through readers that touch its list and allocate nothing; the publicButtonGroup.members()is a copy per call and would cost one per damaged frame. A standalone radio has no set and says so with zeros, which a bridge publishes as nothing. There is no group node for the numbers to hang under, because aButtonGroupis not a widget and has no box, and there is no relation to it for the same reason: a relation's target has to be a published node. So the facet says the member has no container: a radio's selection change is its own selected-state event and is never laid on whatever layout node happens to be its published parent, and a bridge asked for its selection container answers none. The radio is deliberately not markedAccessible.State.ACTIVE: the publish step takes the first active node in a container's subtree as that container's active descendant, and a radio inside a list cell would hijack the list's.Only select is offered. A radio never toggles off, so there is no deselect, and the only route to no selection is
ButtonGroup.clearSelection()from code. The box, the focus and scroll-into-view verbs on the group's one tab stop, and the enabled, visible, showing, focusable and focused states are the walk's; roving focus is already expressed through the focusable flag, whose setter invalidates the tree itself.- Overrides:
onAccessibilityin classWidget<RadioButton>- Parameters:
a- the node being described
-
onAccessibilityAction
Performs a select an assistive technology asked for, exactly as a click or a Space press does: through the seam atUSER, so a group deselects the previous member, both radios'onChangehandlers and the group's own listener are notified the same way, and the tab stop moves with the selection. Selecting the member that is already selected is accepted and does nothing, because the state asked for holds. Any other verb is refused, including a press, which this node does not offer, and so is a select while this widget is disabled: the seam carries no such guard of its own, because it is also the pathButtonGroup.setSelectedIndex(int)takes to restore a disabled form's state, and the answer here has to be truthful.The state change that results is what a reader hears: the group's swap invalidates both members, the next frame republishes, and the difference raises the selected-state event on each. No invocation event is raised for a select, on any platform.
- Overrides:
onAccessibilityActionin classWidget<RadioButton>- Parameters:
action- what was askedarg- ignored; a select carries none- Returns:
- whether the select ran, or already held
-
onMouseEvent
Description copied from class:WidgetMouse events (bubbling); callevent.consume()when handled.- Overrides:
onMouseEventin classWidget<RadioButton>
-
onKeyEvent
Description copied from class:WidgetKey events (focused widget first, then ancestors).- Overrides:
onKeyEventin classWidget<RadioButton>
-
onFocusGained
protected void onFocusGained()Description copied from class:WidgetCalled when this widget takes keyboard focus. Default: nothing.- Overrides:
onFocusGainedin classWidget<RadioButton>
-
onFocusLost
protected void onFocusLost()Description copied from class:WidgetCalled when this widget loses keyboard focus. Default: nothing.- Overrides:
onFocusLostin classWidget<RadioButton>
-