Class Widget<W extends Widget<W>>
- Direct Known Subclasses:
Avatar,AvatarGroup,Badge,Button,CalendarView,Card,Chart,Checkbox,ColorPicker,ColorPickerButton,ComboBox,Container,DateField,DatePicker,Expanded,Expander,Field,ImageView,Label,ListView,MediaControls,MenuBar,Opacity,Padding,PrintPreview,ProgressBar,RadioButton,RangeSlider,ScrollBar,ScrollView,SegmentedControl,Separator,SizedBox,Slider,Spinner,SplitPane,Surface,TabbedPane,Table,TextArea,TextField,TokenBox,ToolBar,Transform,Tree,VideoView,Viewport3D
measure(limn.scene.Constraints) → layoutBox(float, float, float, float)),
per-frame painting and event bubbling.
Thread confinement is a hard rule: every tree/state mutation calls
Ui.checkUiThread() and throws off the UI thread.
Invalidation model: nothing repaints on its own.
invalidate() asks for a frame and records this widget's bounds as
damage; a scene with partial
rendering on (the default) then repaints only the damaged region, one without it
repaints the window, and either way the loop goes back to sleep once nothing
is asking. markNeedsLayout() additionally re-runs measure/layout,
which repaints everything. A change that goes through neither is not drawn:
every setter here invalidates, but a field written directly (from a posted
task, a timer, a background result) does not, and neither does anything a
custom onPaint(limn.graphics.Canvas) reads from outside the tree.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionprotected booleanWhether this widget edits text and should drive the platform input method (IME): the scene enables the IME while such a widget holds focus and disables it otherwise, so composition never intercepts keys meant for non-text UI.protected @Nullable StringThe chord of the access letter this widget, a caption, gives the widget it labels, ornull: what a reader is told reaches the field.protected Widget<?> The widget a label bound to this one names: this widget, unless it is a composite whose keyboard lands on an inner control, in which case that control.protected @Nullable I18nStringThe text this widget offers when it is named as another's label, ornullwhen it is not the kind of widget that captions anything.protected voidInserts a child at a position inchildren()(UI thread only).protected voidAppends a child (UI thread only).protected final voidannounceChange(Change change) The announcing half alone: this widget's watchers, then its scene's watchers, and no handler at any origin.protected floatHow far outside its box a widget that paints from its backdrop reads the pixels behind it, in points: a blur's radius, a refracting rim's depth.protected floatDistance from this widget's top edge to its first text baseline, in logical points: the alignment reference forFlex.CrossAlignment.BASELINE.protected static floatbaselineOffsetOf(Widget<?> child) Readschild'sbaselineOffset().protected @Nullable RectThe caret rectangle in scene coordinates (logical points) used to place the IME candidate window;nullwhen there is no caret to anchor (or the widget is not laid out yet).children()The children, in paint and hit-test order: an unmodifiable view of live state.protected final ClipboardSystem clipboard (never null; a local no-op when detached).protected floatclipHeight(Widget<?> child) The height of the rectangle this widget clipschildto; seeclipX(Widget).protected booleanWhetherpaintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips).protected floatThe width of the rectangle this widget clipschildto; seeclipX(Widget).protected floatThe left edge, in this widget's own coordinates, of the rectangleclipsChildren()clipschildto.protected floatThe top edge of the rectangle this widget clipschildto; seeclipX(Widget).final ControlSize@Nullable Cursorcursor()final @Nullable ControlSizefinal @Nullable LayoutDirectionfinal @Nullable Localeprotected final booleanWhether the focus this widget is being given arrived by Shift+Tab, walking backward: a widget with more than one stop of its own (a table's header and its rows) enters at its last stop then, and at its first otherwise, so that the two directions mirror.protected final booleanHow the focus this widget is being given arrived:truefor Tab or Shift+Tab,falsefor a click, arequestFocus()from code, or focus restored when an overlay closed.protected voidhandleUserChange(Change.Aspect aspect) Runs this widget's application handler for a change it has just announced.final floatheight()Laid-out height in logical points;0until the first layout pass.final @Nullable Widget<?> hitTest(float localX, float localY) The deepest widget under a point, this one, ornull: what a press at the point lands on.protected final @Nullable Widget<?> hitTestChild(Widget<?> child, float localX, float localY) Asks one child what a point lands on: maps the point from this widget's space into the child's and calls the child'shitTest(float, float).final @Nullable Stringid()@Nullable ImageCursorfinal @Nullable Widget<?> final voidRequests a repaint (event-driven: the loop wakes and redraws once).final voidinvalidate(float x, float y, float width, float height) Requests a repaint of a region in this widget's local coordinates: the fine-grainedinvalidate()for widgets that know exactly which pixels changed (a blinking caret, one cell of a grid).final voidSays that something about this widget an assistive technology would care about has changed, when nothing repainted and nothing moved.final booleanWhether this widget accepts input: it and every ancestor are enabled.final booleanWhether keyboard focus can land here: false for containers and static chrome.final booleanWhether this widget currently holds its scene's keyboard focus.final booleanWhether this widget reads right to left:layoutDirection()isRTL.final booleanThe flagsetEnabled(boolean)sets on this widget, whatever its ancestors say: the widget's own half ofisEnabled(), which answers for the whole ancestry.final booleanWhether this widget is drawn by every container above it, as well as visible through them.final booleanThis widget's own visibility flag; seeisShowing()for whether it is on screen.protected final booleanfinal voidlayoutBox(float x, float y, float width, float height) Parent assigns final bounds (parent coords); thenonLayout()places children.final LayoutDirectionfinal Localelocale()final Vec2localToScene(float x, float y) Where a point of this widget's space lies in the scene: the widget's own offsets and each ancestor's, summed to the scene's root, through anything an ancestor draws its children through on the way.final RectlocalToSceneBounds(float x, float y, float width, float height) The rectangle, in the scene, that a rectangle of this widget's space covers: its bounds there, as an axis-aligned box, which is the rectangle itself moved when nothing above turns it.final voidAsks for this widget's own subtree to be laid out again, without declaring that the frame is a full repaint: the form for a container whose insides move while its box does not, such as a virtualised list mounting and recycling rows as it scrolls.final voidMarks this widget's measure dirty and schedules a scene layout pass.protected final voidMarks this widget's measure dirty and asks for a layout that repaints only what it moves: the form for a widget whose size may change for a reason of its own, such as a label whose text changed width.final floatfinal floatmaxWidth()final Sizemeasure(Constraints constraints) Measures the preferred size underconstraints.final floatfinal floatminWidth()protected static voidMoves a child without re-running layout: the scroll fast path (size is unchanged; only the offset moves).protected voidMoves a child to another position inchildren()without taking it out of the tree (UI thread only): whatremove(limn.scene.Widget<?>)andadd(int, Widget)would do in two steps, without the detaching between them, so the focus, the hover and the press inside it stay where they are.protected voidmoveChild(Widget<?> child, int index, Change.Origin origin) moveChild(Widget, int), announcing the move with who asked for it: a container whose user moved a child says so in the oneCHILDRENchange the move makes, which then reaches its handler as a user's change does.protected final ShapedText.DirectionWhat a run of text with no strong character of its own falls back to: this widget's resolved direction, as the shaper's neutral base.protected final voidnotifyChange(Change change) Announces a change that has already settled, and then runs the handler if the origin isUSER: the last statement of an ordinary mutator, after the state, the layout marks, the focus move and the reveal.protected final voidnotifyTextEdit(Change.Origin origin, int offset, int removed, int inserted) The same asnotifyChange(limn.scene.Change), for a text edit, taking the edit's shape rather than a built value so that a keystroke on a tree nobody watches constructs nothing.final SubscriptionobserveChanges(ChangeObserver observer) Watches every change to this widget, from every origin, without touching whatever handler the application registered.protected voidSays what this widget is, for an assistive technology.protected booleanonAccessibilityAction(Accessible.Action action, Accessible.Argument arg) Performs an action an assistive technology asked of this widget, as the user.protected voidonAccessibilityChild(Widget<?> child, Accessibility a) Adds what only this widget knows about one of its children: the role a mounted list cell takes, its position in the data, its selected state.protected booleanonAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) Performs a verb this widget claimed on one of its widget children: a list'sSELECTon a row that is the application's own cell, published on the row where a reader addresses it and performed by the list, which is the only thing that knows what selecting that row means.protected voidonAccessibilityChildIdentity(Widget<?> child, Accessibility a) Says who one of this widget's children is, before that child describes itself: the identity key a container that pools its children owns and nothing else can, and, for a container that draws its rows, the synthetic row the child hangs under.protected voidCalled when this widget enters a scene (attached to the tree).protected booleanonCanEdit(EditCommand command) Whether this widget can do an editing command now: copy with something selected, paste with something to paste, undo with something to take back.protected voidonCharTyped(CharEvent event) Committed text input (focused widget first, then ancestors).protected voidCalled when this widget leaves the scene: detached from the tree, moved into another scene (told this before theonAttached()of the scene it joins), or still in it when the scene's window closes, which detaches every widget the scene has.protected voidonDragExit(DragEvent event) The drag this widget was last asked about inonDragOver(limn.scene.event.DragEvent)is no longer over it, whether it accepted or not: whatever it drew for the drag goes, the mark of the drop's place or the sign that it refuses.protected voidonDragOver(DragEvent event) A drag is over this widget, or has moved on it.protected @Nullable DragonDragStart(DragStartEvent event) Asked when a drag may begin on this widget: the pointer was pressed here and has moved a few points, or the drag key was pressed while this widget had the focus.protected voidWhat was dragged is dropped here: on the widget that accepted it last.protected booleanonEdit(EditCommand command) Does an editing command, as the user's own act.protected voidCalled when this widget takes keyboard focus.protected voidCalled when this widget loses keyboard focus.protected @Nullable Widget<?> onHitTest(float localX, float localY) What this widget answers for a point that reached it: one of its children's answers, itself, ornullto let the point through to what is beneath it.protected voidonKeyEvent(KeyEvent event) Key events (focused widget first, then ancestors).protected voidonLayout()Containers position children here (measure +layoutBox(float, float, float, float)per child).protected abstract 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 voidonPaintOverlay(Canvas canvas) Painted after children (scrollbars, focus rings…).protected voidonPreedit(PreeditEvent event) In-progress IME composition ("preedit") for this widget while it is focused: the still-composing text, shown inline but not yet committed (the commit later arrives asonCharTyped(limn.scene.event.CharEvent)).protected booleanonSyntheticAction(long key, Accessible.Action action, Accessible.Argument arg) Performs an action addressed to one of this widget's synthetic children: a menu row, a combo option, a chart series — something this widget draws and never instantiated as a widget.protected booleanoverlayPassesPointer(float sceneX, float sceneY) When this widget is the active modal overlay, whether a pointer hit at the given scene point should fall through to the content beneath it instead of being captured.protected voidpaintChildren(Canvas canvas) Children pass; override to clip (e.g.protected floatHow far beyond this widget's bounds its painting may reach, in logical points: its own painting and any descendant no ancestor clips (a drop shadow, a glow, a child positioned outside the parent's box).protected booleanWhether whatonPaint(limn.graphics.Canvas)draws is material rather than meaning: a wash, a blur, a shadow, a rule.protected booleanWhether this widget's picture is made partly of the pixels behind it: a wash, a frost, a refraction.final voidpaintWidget(Canvas canvas) Paints this widget and its children (canvasorigin = this widget).final @Nullable Widget<?> parent()The parent this widget was added to, ornullwhile it is unattached.protected voidRemoves a child (UI thread only); protected for the reasonadd(Widget)is.protected voidremove(Widget<?> child, Change.Origin origin) remove(Widget), announcing the removal with who asked for it: a container whose user closed a child says so in the oneCHILDRENchange the removal makes.final voidAsks the scene to move keyboard focus here (UI thread only).protected final voidrequestFocus(Change.Origin origin) The same, for a component moving focus as part of a gesture it is handling: a tab strip's arrow key, a radio group's.final voidAsks everyScrollableancestor, innermost first, to scroll this widget's bounds into view (each is handed the bounds in its own local coordinates, re-read after inner scrolls so nested scrollables compose).final voidrevealInView(float rectX, float rectY, float rectWidth, float rectHeight) Reveals one rectangle of this widget's own content, in this widget's coordinates, through every scrolling ancestor — whatrevealInView()does for the whole widget.protected final voidrunHandler(Change change) The handler half alone: runs this widget's handler for a change this widget has already announced, and nothing at any origin butUSER.final @Nullable Scenescene()The scene this widget belongs to, ornulluntil it is added to one.protected final longThe nanosecond clock this widget's scene animates on, or the wall clock while it belongs to no scene.final Vec2sceneToLocal(float sceneX, float sceneY) Where a point of the scene lies in this widget's space: what a widget does with a pointer event's position, which is the scene's, to know where in itself the pointer is.protected final Wself()This widget as its own type, which is what every setter here returns so that a chain of them keeps the type it started with:new Button("OK").setEnabled(false)is aButton.final WsetAccessibleDescribedBy(@Nullable Widget<?> source) Describes this widget from another widget's text, the way the message beneath a field says why the field is invalid.final WsetAccessibleDescription(@Nullable String text) Describes this widget with a fixed string.final WsetAccessibleDescription(@Nullable I18nString text) Describes this widget at more length than its name does, for an assistive technology.final WsetAccessibleIgnored(boolean ignored) Removes this widget and its subtree from the accessible tree: a spacer, a decorative rule, an image that repeats what the text beside it already says.final WsetAccessibleLabelledBy(@Nullable Widget<?> label) Names this widget from another widget's text, the way a form's caption names the field it sits beside.final WsetAccessibleName(@Nullable String name) Names this widget with a fixed string.final WsetAccessibleName(@Nullable I18nString name) Names this widget for an assistive technology, overriding whatever it would have derived for itself.final WsetAccessibleRole(@Nullable Accessible.Role role) Says what this widget is, overriding whatever role it would have declared.final WsetControlSize(@Nullable ControlSize size) Sets the step for this widget and every descendant that does not declare its own; it inherits down the tree likesetCursor(limn.backend.Cursor).final WSets the mouse cursor shown while the pointer is over this widget (and any descendant that does not set its own, since cursor inherits down the tree).final WsetEnabled(boolean enabled) Enables or disables input for this widget and its subtree: while it is disabled,isEnabled()answersfalsehere and on every descendant, whatever their own flags say, and each takes its own flag back when this one is enabled again.final WsetFocusable(boolean focusable) Declares whether this widget can take keyboard focus.final WNames this widget for the code that looks it up:Scene.find(String), a test's driver clicking it by id, and a tool that drives the running application from outside, which finds it by the same id through the system's accessibility: UI Automation's automation id on Windows, the accessibility identifier on macOS, the accessible id on Linux.final WsetImageCursor(@Nullable ImageCursor cursor) Sets a customImageCursorshown while the pointer is over this widget (inherits down the tree likesetCursor(limn.backend.Cursor)).final WsetInheritanceHost(@Nullable Widget<?> host) Links this widget's inherited axes — itsControlSize, itsLayoutDirectionand its locale alike — tohost, for the case the tree cannot express: a widget that is the root of its ownScene(a popup or dialog window) or a overlay, both of which have no parent.final WsetLayoutDirection(@Nullable LayoutDirection direction) Sets the layout direction for this widget and every descendant that does not declare its own; it inherits down the tree likesetControlSize(limn.scene.ControlSize).final WSets the locale for this widget and every descendant that does not declare its own; it inherits down the tree likesetControlSize(limn.scene.ControlSize).final WsetMaxHeight(float height) Keeps this widget at mostheightpoints tall wherever its parent lets it choose its height; seesetMinWidth(float).final WsetMaxWidth(float width) Keeps this widget at mostwidthpoints wide wherever its parent lets it choose its width, the bound a paragraph or a form needs so that it does not run the width of a wide window; seesetMinWidth(float)for where a bound takes effect.final WsetMinHeight(float height) Keeps this widget at leastheightpoints tall wherever its parent lets it choose its height; seesetMinWidth(float).final WsetMinWidth(float width) Keeps this widget at leastwidthpoints wide wherever its parent lets it choose its width.protected final voidsetPresentedElsewhere(boolean value) Takes this widget out of the scene's layout, painting and reading, or puts it back, for a widget whose content something outside the scene is showing: a menu bar the system holds in its own bar.final WsetTooltip(@Nullable String text) Sets the hover tooltip text (nullclears it).final WsetTooltip(@Nullable I18nString text) Sets a tooltip that follows the UI language (nullclears it).final WsetVisible(boolean visible) Shows or hides this widget and its subtree, re-running layout so siblings take the space back.protected final ShapedTextShapes one line oftextthe way this widget reads: the first-strong rule decides for any string that can decide for itself, andneutralBase()decides for the rest.protected final TextRulerText measurer for layout-time metrics (never null; NONE when detached).@Nullable Stringtooltip()@Nullable I18nStringfinal floatwidth()Laid-out width in logical points;0until the first layout pass.final floatx()Where layout put this widget's left edge, in its parent's space: the layout position, before anything an ancestor draws its children through.final floaty()Where layout put this widget's top edge, in its parent's space; seex().
-
Constructor Details
-
Widget
public Widget()
-
-
Method Details
-
self
This widget as its own type, which is what every setter here returns so that a chain of them keeps the type it started with:new Button("OK").setEnabled(false)is aButton. The one unchecked cast in the class: a widget declares itself as its own type argument (Button extends Widget<Button>), so it is always true.- Returns:
- this
-
parent
The parent this widget was added to, ornullwhile it is unattached. -
children
The children, in paint and hit-test order: an unmodifiable view of live state. -
add
Appends a child (UI thread only). Protected: a widget arranges children of its own, and only aContainertakes them from outside.A widget that is a scene's root, or an overlay on a scene's stack, is refused before anything moves, here and on every path a widget takes a child through (a container's add, a component's setter of its content): the scene that holds it would go on naming it as its own. That scene lets go of it when it is taken off the stack (
Scene.removeOverlay(limn.scene.Widget<?>)), when the scene is given another root (Scene.setRoot(limn.scene.Widget<?>)), or when its window closes.- Parameters:
child- the child to append; nevernull- Throws:
NullPointerException- ifchildisnullIllegalArgumentException- ifchildalready has a parent, or is an ancestor of this widget, a scene's root, or an overlay on a scene's stack
-
add
Inserts a child at a position inchildren()(UI thread only).add(Widget)appends, which is right for every container that builds its children once and in the order they are meant to be read. It is wrong for one that mounts them on demand from both ends: a virtualized list scrolling back up realizes the rows above its anchor in descending order, and appending each would leavechildren()— which is reading order and Tab order both — holding the order the rows happened to be realized in rather than the order the data is in. Re-adding a child to move it is not the alternative:remove(limn.scene.Widget<?>)detaches the subtree, which revokes the focus, hover and press inside it.- Parameters:
index- where the child goes, in[0, children().size()]child- the child to insert; nevernull- Throws:
IndexOutOfBoundsException- ifindexis outside that rangeNullPointerException- ifchildisnullIllegalArgumentException- ifchildalready has a parent, or is an ancestor of this widget, a scene's root, or an overlay on a scene's stack, asadd(Widget)says
-
moveChild
Moves a child to another position inchildren()without taking it out of the tree (UI thread only): whatremove(limn.scene.Widget<?>)andadd(int, Widget)would do in two steps, without the detaching between them, so the focus, the hover and the press inside it stay where they are. A tab dragged along its bar keeps the press that drags it.- Parameters:
child- a child of this widgetindex- where it goes, in[0, children().size())- Throws:
IllegalArgumentException- ifchildis not a child of this widgetIndexOutOfBoundsException- ifindexis outside that range
-
moveChild
moveChild(Widget, int), announcing the move with who asked for it: a container whose user moved a child says so in the oneCHILDRENchange the move makes, which then reaches its handler as a user's change does.- Parameters:
child- a child of this widgetindex- where it goes, in[0, children().size())origin- who asked for the move- Throws:
IllegalArgumentException- ifchildis not a child of this widgetIndexOutOfBoundsException- ifindexis outside that range
-
remove
Removes a child (UI thread only); protected for the reasonadd(Widget)is. -
remove
remove(Widget), announcing the removal with who asked for it: a container whose user closed a child says so in the oneCHILDRENchange the removal makes.- Parameters:
child- the child to take out; nothing happens for one that is not a childorigin- who asked for it
-
onAttached
protected void onAttached()Called when this widget enters a scene (attached to the tree).scene()is the scene it just joined. Fires top-down, so a parent runs before its children. Default no-op. -
onDetached
protected void onDetached()Called when this widget leaves the scene: detached from the tree, moved into another scene (told this before theonAttached()of the scene it joins), or still in it when the scene's window closes, which detaches every widget the scene has. What was taken inonAttached()from something that outlives the window (a listener's place in a list of the process's, a resource of the system's) is given back here.scene()still answers the scene being left, and becomesnullonce this returns, so releasing something the scene owns needs no field of your own to remember it by. Fires bottom-up, so children run before their parent, and every one of them can still reach the scene.Release resources here, but GPU resources must be handed to
Scene.disposeLater(limn.graphics.GpuSurface)(disposal needs the owning GL context, which is only current inside a frame). Not called onsetVisible(boolean). Default no-op. -
scene
The scene this widget belongs to, ornulluntil it is added to one. -
sceneNanos
protected final long sceneNanos()The nanosecond clock this widget's scene animates on, or the wall clock while it belongs to no scene.For a widget that keeps a timer of its own -- a hold before a fade, a delay before a reveal -- and would otherwise read
System.nanoTime()directly. A scene's clock is injectable precisely so a test can move time by decree instead of waiting for it, and a timer read off the wall clock is the one part of such a widget that injection would not reach: it expires on its own schedule, in the middle of whatever a test was measuring.- Returns:
- nanoseconds on the scene's clock, comparable only with other readings of it
-
x
public final float x()Where layout put this widget's left edge, in its parent's space: the layout position, before anything an ancestor draws its children through. For where the widget is in the scene, asklocalToScene(float, float)orlocalToSceneBounds(float, float, float, float).- Returns:
- x in parent coordinates
-
y
public final float y()Where layout put this widget's top edge, in its parent's space; seex().- Returns:
- y in parent coordinates
-
width
public final float width()Laid-out width in logical points;0until the first layout pass. -
height
public final float height()Laid-out height in logical points;0until the first layout pass. -
localToScene
Where a point of this widget's space lies in the scene: the widget's own offsets and each ancestor's, summed to the scene's root, through anything an ancestor draws its children through on the way.The middle of a widget, where a click on it lands, is
widget.localToScene(widget.width() / 2, widget.height() / 2). A point that cannot be seen because it lies behind the eye answers(NaN, NaN).- Parameters:
x- a point's x in this widget's spacey- a point's y in this widget's space- Returns:
- the point in the scene's space
-
sceneToLocal
Where a point of the scene lies in this widget's space: what a widget does with a pointer event's position, which is the scene's, to know where in itself the pointer is.Answers
(NaN, NaN)for a scene point that falls on no point of this widget's plane: one an ancestor draws the widget turned edge-on at, or collapsed, or one that would lie behind the eye.NaNfails every comparison, so the usual testp.x() >= 0 && p.x() < width()already counts such a point outside the widget. The point of aDRAGor aRELEASEthe widget receives while it holds the pointer is not one of these: the scene withholds a drag that has left the widget's plane and releases it at the last point it had there, so a drag handler computes with finite numbers and needs no guard of its own, but for a release on a widget of which nothing can be seen, its plane exactly edge-on or its whole box behind the eye, which has no point to give (seeMouseEvent).- Parameters:
sceneX- a point's x in the scenesceneY- a point's y in the scene- Returns:
- the point in this widget's space, or
(NaN, NaN)
-
localToSceneBounds
The rectangle, in the scene, that a rectangle of this widget's space covers: its bounds there, as an axis-aligned box, which is the rectangle itself moved when nothing above turns it. What a popup is anchored to, what a drag image starts from and what the accessible tree publishes; the widget's own box in the scene iswidget.localToSceneBounds(0, 0, widget.width(), widget.height()).The part of the rectangle that would lie behind the eye is cut away before the bounds are taken; a rectangle wholly behind it answers a rectangle of no size at
NaN.- Parameters:
x- the rectangle's left, in this widget's spacey- its topwidth- its widthheight- its height- Returns:
- its bounds in the scene
-
revealInView
public final void revealInView()Asks everyScrollableancestor, innermost first, to scroll this widget's bounds into view (each is handed the bounds in its own local coordinates, re-read after inner scrolls so nested scrollables compose). A no-op when everything is already visible. TheScenecalls this on focus changes, so keyboard traversal reveals the focused widget.The bounds are grown by
paintOutset()first, because the bounds are not what has to be visible. A focus ring is drawn outside the box it belongs to —Strokes.FOCUS_RING_OUTSETis 3 points for a Button, and the ring is the whole reason the widget is being revealed. Revealing the bare bounds parks the widget flush against the viewport's clip, which is exactly where the ring is chopped: the reveal reported success and the reader could not see what was focused.Nothing opts in and nothing is configured. Every widget already declares how far it paints, for partial rendering, and a widget that paints nothing outside its box returns 0 and reveals exactly as it did before. A viewport too small to hold bounds-plus-outset aligns the near edge, the same rule
Scrollable.revealRect(float, float, float, float)already applies to any oversize rectangle. -
revealInView
public final void revealInView(float rectX, float rectY, float rectWidth, float rectHeight) Reveals one rectangle of this widget's own content, in this widget's coordinates, through every scrolling ancestor — whatrevealInView()does for the whole widget.For a widget whose parts are not widgets: a calendar's day cell, a table's row, a chart's point. Such a part has no box of its own to reveal and no
paintOutset()of its own to grow by, so the rectangle is taken as given, and a caller that draws outside it passes the larger rectangle rather than relying on an inflation this method cannot know the size of. Added on 2026-09-17, when a reader'sSCROLL_INTO_VIEWon a day cell had to move the scroll view the calendar sits in and the whole-widget reveal would have scrolled the calendar's own edge into view instead.- Parameters:
rectX- the rectangle's left, in this widget's coordinatesrectY- the rectangle's top, in this widget's coordinatesrectWidth- its widthrectHeight- its height
-
isVisible
public final boolean isVisible()This widget's own visibility flag; seeisShowing()for whether it is on screen. -
isShowing
public final boolean isShowing()Whether this widget is drawn by every container above it, as well as visible through them.A
Transformthat draws its child as nothing, a back face turned to the viewer while it hides back faces, a scale of 0 on either axis, a plane seen exactly edge-on, leaves everything inside not showing, and out of reach as a hidden container leaves it: out of the Tab order, an access letter's and the pointer's reach, the focus, the hover and a press taken out of it as the change is made, and published to assistive technologies as hidden. Nothing of it is changed: it is showing again, and in reach, as soon as the face turns back or the scale returns; the focus it lost is not given back.- Returns:
- whether this widget is actually on screen: attached to a scene,
visible together with every ancestor (unlike
isVisible(), which reflects only this widget's own flag), drawn by every transform above it, and not scrolled fully out of an ancestor that clips (a scroll viewport), nor drawn wholly behind the eye or outside such an ancestor by a transform. Animations use this to pause while their widget is inside a hidden container (an unselected tab) or clipped out of view; without it, a progress bar scrolled away would keep ticking the frame loop at full rate while painting nothing. The repaint that reveals the widget re-arms them (the established re-arm-on-paint pattern).
-
setVisible
Shows or hides this widget and its subtree, re-running layout so siblings take the space back. Hiding revokes focus, hover and any press inside the subtree. UI thread only.Announces
VISIBLE/CODE, and after the revocation'sFOCUS/ADJUSTMENTwhen hiding took the focus away: the settling order of this record in the smallest case there is, with the call's own aspect last.final, like the three setters beside it, because an override that forgotsuperwould silently reopen the hole this closes -- a widget whose visibility changed and told nobody -- and because a subclass wanting to react to its own visibility hasobserveChanges(limn.scene.ChangeObserver). -
setPresentedElsewhere
protected final void setPresentedElsewhere(boolean value) Takes this widget out of the scene's layout, painting and reading, or puts it back, for a widget whose content something outside the scene is showing: a menu bar the system holds in its own bar. While it is so, the widget is laid out as an invisible one is, with no box and no gap beside it, andisVisible()answersfalse;isVisibleByItsOwnFlag()still says whatsetVisible(boolean)was told.Not a change of the widget's own visibility, and announced as none. UI thread only.
-
isVisibleByItsOwnFlag
protected final boolean isVisibleByItsOwnFlag()- Returns:
- what
setVisible(boolean)was last told, whateversetPresentedElsewhere(boolean)says
-
isEnabled
public final boolean isEnabled()Whether this widget accepts input: it and every ancestor are enabled. A disabled widget still occupies its box.The effective state, as
isShowing()is toisVisible(), and not the flagsetEnabled(boolean)sets, whichisSelfEnabled()reads: disabling a form disables every control in it, so each of them paints its disabled look, refuses the focus and is published to assistive technologies as not enabled, from this one reading. When it answered with the widget's own flag alone, a button inside a disabled section looked enabled, took the focus from code or from a dialog closing, and ran its action on Enter, while the pointer and Tab already passed it by.- Returns:
- whether this widget and all its ancestors are enabled
-
isSelfEnabled
public final boolean isSelfEnabled()The flagsetEnabled(boolean)sets on this widget, whatever its ancestors say: the widget's own half ofisEnabled(), which answers for the whole ancestry.The pair is
isVisible()andisShowing()the other way round: there the short name is the widget's own flag and the longer one the effective state, hereisEnabled()is the effective state, because that is what nearly every caller asks (may this control take input now?), and this is the flag. Read it to change the flag relative to itself:w.setEnabled(!w.isSelfEnabled())toggles the control inside a disabled form as it does outside one, wherew.setEnabled(!w.isEnabled())would writetrueover a control the application had disabled, and the control would come back enabled with the form.- Returns:
- what
setEnabled(boolean)was last told;trueuntil it is told otherwise
-
setEnabled
Enables or disables input for this widget and its subtree: while it is disabled,isEnabled()answersfalsehere and on every descendant, whatever their own flags say, and each takes its own flag back when this one is enabled again. The flag itself isisSelfEnabled(). Repaints without re-laying-out, since the box does not change; disabling revokes focus, hover and any press inside the subtree. UI thread only.Announces
ENABLED/CODEon this widget whenever its flag changes, then on every descendant whoseisEnabled()changed with it, in tree order (a parent before its children, siblings first to last): not on one disabled by its own flag or under another disabled container, whose state did not move, and on none at all when an ancestor of this widget is disabled. Each descendant is checked again on its turn, so one a watcher has since moved out of the subtree, or whose state a watcher undid, is not told of a state it does not have. All of it after the revocation'sFOCUSwhen disabling took the focus away. Moving a widget to another parent announces noENABLED, though itsisEnabled()may change with the move.finalfor the reasonsetVisible(boolean)is. -
isFocusable
public final boolean isFocusable()Whether keyboard focus can land here: false for containers and static chrome. -
setFocusable
Declares whether this widget can take keyboard focus. Does not move focus away if it currently holds it. UI thread only.It neither repaints nor re-lays-out, because being focusable changes nothing on screen until the focus actually arrives. It does tell the accessible tree, and this is the sharper of the two entry points that reach nothing else: being focusable is both a published state and the thing that keeps a widget in the tree at all, so making a scaffold widget focusable changes what the tree contains — and would otherwise do so silently, on the next unrelated repaint.
It announces
FOCUSABLE/CODE, which is the only way this change is observable at all: it produces no frame, so nothing sampling the tree could ever see it. The guard in front is what keeps that honest where a roving-focus reassignment writesfalseover every non-holder in a group on every selection change. -
cursor
- Returns:
- the mouse cursor this widget requests while hovered, or
nullto inherit from an ancestor (the scene falls back toCursor.DEFAULTat the root). Non-final so a component can override it with a state-dependent shape.
-
setCursor
Sets the mouse cursor shown while the pointer is over this widget (and any descendant that does not set its own, since cursor inherits down the tree).nullrestores inheritance. UI thread only.- Returns:
- this widget
-
imageCursor
- Returns:
- the custom image cursor this widget requests while hovered, or
nullto inherit. Resolved likecursor(), with an image cursor winning over a shape on the same widget.
-
setImageCursor
Sets a customImageCursorshown while the pointer is over this widget (inherits down the tree likesetCursor(limn.backend.Cursor)).nullrestores inheritance/shape resolution. UI thread only.- Returns:
- this widget
-
tooltip
- Returns:
- the hover tooltip text for this widget, or
null/empty for none. The scene shows it after a short dwell, near the pointer. Resolved by walking up from the hovered leaf (likecursor), under this widget's own locale: a tooltip belongs to the subtree it annotates, and the scene painting it is outside any pass that would put that locale in scope.
-
tooltipSource
- Returns:
- the localizable string behind
tooltip(), ornullfor none. The "is it set here" reader, matching thetext()/textSource()pairs the components expose: a tooltip is the natural description of an icon-only control and the natural name when it has no other, and neither can be re-resolved when the translation epoch moves unless the string itself is reachable.
-
setTooltip
Sets the hover tooltip text (nullclears it). UI thread only. -
setTooltip
Sets a tooltip that follows the UI language (nullclears it). UI thread only.It repaints nothing, because a tooltip is painted only while it is showing and the scene re-reads it then. It does tell the accessible tree, because a tooltip is a description there and often a name, and a change that paints nothing would otherwise reach a screen reader on the next unrelated repaint, or never.
Announces
DESCRIPTION/CODE, which is the other change here that produces no frame. The guard in front of it compares by value and not by reference, and that is load-bearing rather than a nicety: theStringoverload wraps its argument in a freshI18nStringon every call, so a!=guard would never hold and every repeatedsetTooltip("Play")would announce a description that did not change.I18nStringanswers this already, over its key and its English. -
isFocused
public final boolean isFocused()Whether this widget currently holds its scene's keyboard focus. -
requestFocus
public final void requestFocus()Asks the scene to move keyboard focus here (UI thread only).This is the method an application calls, so the focus move it makes is
CODE. -
requestFocus
The same, for a component moving focus as part of a gesture it is handling: a tab strip's arrow key, a radio group's. AnnouncesFOCUSwithoriginon the widget losing focus and on the widget gaining it.protectedand not package-private, because the two components that need it are inlimn.componentsand this islimn.scene. That places it where a component author can reach it and an application cannot -- which is the right side of the line, since labelling the origin is a component author's obligation and an application asserting that it is the user would be a claim the toolkit cannot check. A component reaches it on another widget through a package-private one-liner on that widget's own class, the way a radio group already drives its members.- Parameters:
origin- what moved the focus
-
declaredControlSize
- Returns:
- the step this widget declares for itself and its subtree, or
nullwhen it inherits. This is the "is it set here" reader; usecontrolSize()for the effective value (cf.isVisible()vsisShowing()).
-
controlSize
- Returns:
- the effective size step, never
null: this widget's declared value, else the nearest declaring ancestor's, else its scene default, else its host's, elseControlSize.processDefault().Read this inside
onMeasure(limn.scene.Constraints),onPaint(limn.graphics.Canvas)or an event handler. Never in a constructor or a field initializer: a widget has no parent while it is being constructed, so the answer there is the process default no matter what the eventual parent declares, and a captured value can never be corrected.Steady-state cost is one
longcompare and one field read. On the first resolution after an epoch bump this recurses one step and delegates, so a top-down measure or paint pass re-memoizes the whole tree in O(n) links total.finalby contract:measure(limn.scene.Constraints)keys its cache on the resolved step, so a subclass that computed a step on the fly would produce sizes the invalidation system cannot see. A composite that owns widgets outside its own subtree links them withsetInheritanceHost(limn.scene.Widget<?>)instead of overriding anything.
-
setControlSize
Sets the step for this widget and every descendant that does not declare its own; it inherits down the tree likesetCursor(limn.backend.Cursor).nullrestores inheritance. Re-measures whatever actually changed and repaints; a descendant that declares its own step keeps its measure cache. No-op when unchanged. UI thread only. -
inheritanceHost
- Returns:
- the widget this one's inherited axes resolve through when the tree cannot say, or
nullwhen there is none. Every popup, menu and dialog root writes this link, and it is the only path from such a root back to the control that opened it: the accessible tree publishes that link as a relation in both directions, so a client walking either way finds the other. - See Also:
-
setInheritanceHost
Links this widget's inherited axes — itsControlSize, itsLayoutDirectionand its locale alike — tohost, for the case the tree cannot express: a widget that is the root of its ownScene(a popup or dialog window) or a overlay, both of which have no parent. One link carries them all, because it says "this parentless panel belongs to that widget", which is a fact about no single axis; a second link that could name a different widget per axis would be a bug with no honest resolution.The chain then continues from
host, live, so a later change on the host reaches the popup while it is open, which explicit forwarding could not do (and which would additionally convert an inherited value into a declared one, pinning the popup if a process default changed underneath it).nullunlinks. UI thread only.Consulted after this widget's own scene default, so a popup scene that declares a step or a direction keeps it and one that declares nothing falls through to its host.
Install it before anything sizes the surface. A native popup or dialog measures its content to size its window before binding a scene; installing the host after that sizes the window at the process defaults and then re-measures the content at the owner's step inside a wrongly-sized window.
Live resolution is not live repaint. A hosted root in its own scene resolves the new value on its next pass, but nothing marks that scene dirty when the owner's axis changes, so a component holding an open popup has to ask for the pass itself.
- Parameters:
host- the widget to resolve through, ornullto unlink- Throws:
IllegalArgumentException- ifhostresolves through this widget
-
declaredLayoutDirection
- Returns:
- the direction this widget declares for itself and its subtree, or
nullwhen it inherits. The "is it set here" reader; uselayoutDirection()for the effective value.
-
layoutDirection
- Returns:
- the effective layout direction, never
null: this widget's declared value, else the nearest declaring ancestor's, else its scene default, else its host's, elseLayoutDirection.processDefault().Read this inside
onMeasure(limn.scene.Constraints),onPaint(limn.graphics.Canvas),onLayout()or an event handler, and resolve it once per pass into a local. Never in a constructor or a field initializer: a widget has no parent while it is being constructed, so the answer there is the process default no matter what the eventual parent declares, and a captured value can never be corrected. Two resolutions that disagree inside oneonPaintput the caret on one side and the selection band on the other.Steady-state cost is one
longcompare and one field read, and the epoch is its own: a control-size change does not invalidate this memo, and this does not invalidate that one. On the first resolution after a bump this recurses one step and delegates, so a top-down pass re-memoizes the whole tree in O(n) links total.finalby contract, forcontrolSize()'s reason:measure(limn.scene.Constraints)keys its cache on the resolved direction, so a subclass computing one on the fly would produce sizes the invalidation system cannot see.
-
isRightToLeft
public final boolean isRightToLeft()Whether this widget reads right to left:layoutDirection()isRTL. The test every mirrored pass makes, here once rather than as a private method in each widget. Read it wherelayoutDirection()may be read: in a pass or an event handler, never in a constructor.- Returns:
- whether the resolved direction is right to left
-
setLayoutDirection
Sets the layout direction for this widget and every descendant that does not declare its own; it inherits down the tree likesetControlSize(limn.scene.ControlSize).nullrestores inheritance. Re-measures whatever actually changed and repaints; a descendant that declares its own direction keeps its measure cache. No-op when unchanged. UI thread only. -
declaredLocale
- Returns:
- the locale this widget declares for itself and its subtree, or
nullwhen it inherits. The "is it set here" reader; uselocale()for the effective value.
-
locale
- Returns:
- the effective locale, never
null: this widget's declared value, else the nearest declaring ancestor's, else its scene default, else its host's, else the process locale. The language this subtree's strings resolve in, its numbers take their digits from, and its text breaks lines under; it is not a direction — a Hebrew-locale subtree still lays out by itslayoutDirection(), and the two axes are declared separately because they genuinely vary separately.Widgets rarely need to read this. While the toolkit is inside this widget's measure, layout, paint or an event handler,
I18n.locale()already answers it (the pass holds it in scope), soI18nString.get(),I18n.localizeDigitsand every other locale reader is correct unchanged. Read it explicitly to hand the answer somewhere the scope cannot follow: a posted task, a native window's title.Never read it in a constructor or a field initializer, for the reason the other axes forbid it: a widget has no parent while it is being constructed, so the answer there is the process locale no matter what the eventual parent declares.
Steady-state cost is two
longcompares and a field read. The memo is validated against its own epoch andI18n.epoch(), because the chain bottoms out in the process locale andI18n.setLocale(java.util.Locale)cannot reach a counter in this package; a bundle registration therefore re-resolves this memo spuriously, one link per widget, which is the recorded price of keeping the axes' writers apart.finalby contract, forcontrolSize()'s reason:measure(limn.scene.Constraints)keys its cache on the resolved locale.
-
setLocale
Sets the locale for this widget and every descendant that does not declare its own; it inherits down the tree likesetControlSize(limn.scene.ControlSize).nullrestores inheritance. This is the escape hatch from one process-wide locale: the recorded case is a Hebrew interface holding a left-to-right, English-locale code pane, where reading everything off the process locale is the shortcut that breaks it.The declared locale is retained while the declaration stands, so every bundle keeps a prepared table for it; clearing or replacing the declaration releases it. A widget discarded while still declaring one keeps that retain — clear the declaration (or accept a resident table per language the process ever declared, which is usually one) when a locale-declaring subtree is dropped for good.
Re-measures whatever actually changed and repaints; a descendant that declares its own locale keeps its measure cache. No-op when unchanged. UI thread only.
-
baselineOffset
protected float baselineOffset()Distance 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 withmoveChild(limn.scene.Widget<?>, int), which moves without re-running layout. -
baselineOffsetOf
Readschild'sbaselineOffset(). For container authors, the sameprotected staticbridge shape asmoveChild(limn.scene.Widget<?>, int), and necessary for the same reason:baselineOffset()isprotected, so aFlexinlimn.scene.layoutcannot invoke it on another instance (JLS 6.6.2.1), but aprotected staticmember carries no qualifying-type restriction. -
setMinWidth
Keeps this widget at leastwidthpoints wide wherever its parent lets it choose its width.0, the default, is no minimum.The parent wins. A bound narrows what the parent allows and never overrides it: a widget whose parent fixes its width (the scene's root, which is the window's size; a
STRETCHcolumn; a flexible child along its row or column, whose shareExpanded.atLeastandatMostbound instead) takes that width whatever its bounds say. Where the parent leaves the choice open, as a column does across and a row does for a child that is not flexible, the widget's natural size is brought within its bounds. UI thread only.- Parameters:
width- the narrowest, in logical points- Returns:
- this widget
- Throws:
IllegalArgumentException- when negative,NaNor infinite, or abovemaxWidth()
-
setMaxWidth
Keeps this widget at mostwidthpoints wide wherever its parent lets it choose its width, the bound a paragraph or a form needs so that it does not run the width of a wide window; seesetMinWidth(float)for where a bound takes effect.Constraints.UNBOUNDED_LIMIT, the default, is no maximum. UI thread only.- Parameters:
width- the widest, in logical points- Returns:
- this widget
- Throws:
IllegalArgumentException- when negative orNaN, or belowminWidth()
-
setMinHeight
Keeps this widget at leastheightpoints tall wherever its parent lets it choose its height; seesetMinWidth(float).0, the default, is no minimum. UI thread only.- Parameters:
height- the shortest, in logical points- Returns:
- this widget
- Throws:
IllegalArgumentException- when negative,NaNor infinite, or abovemaxHeight()
-
setMaxHeight
Keeps this widget at mostheightpoints tall wherever its parent lets it choose its height; seesetMinWidth(float).Constraints.UNBOUNDED_LIMIT, the default, is no maximum. UI thread only.- Parameters:
height- the tallest, in logical points- Returns:
- this widget
- Throws:
IllegalArgumentException- when negative orNaN, or belowminHeight()
-
minWidth
public final float minWidth()- Returns:
- the minimum width
setMinWidth(float)set,0for none
-
maxWidth
public final float maxWidth()- Returns:
- the maximum width
setMaxWidth(float)set,Constraints.UNBOUNDED_LIMITfor none
-
minHeight
public final float minHeight()- Returns:
- the minimum height
setMinHeight(float)set,0for none
-
maxHeight
public final float maxHeight()- Returns:
- the maximum height
setMaxHeight(float)set,Constraints.UNBOUNDED_LIMITfor none
-
measure
Measures the preferred size underconstraints. Results are cached untilmarkNeedsLayout(), and the cache key includes the resolved axes — the control size, the layout direction and the locale, as oneMeasuredAxesvalue — so a container's change on any axis re-measures exactly the descendants whose resolved value actually changed and leaves overriding subtrees on their caches. That is why no deep-invalidation API is needed for any axis. Subclasses implementonMeasure(limn.scene.Constraints).The direction belongs in the key because a line of mixed content genuinely measures a fraction of a point differently in the two directions: the paragraph direction decides which bidi level a boundary neutral takes, which decides which run it extends, which decides which face measures it. The locale belongs there because a keyed string resolves to different text under a different language, and a number to different digits. A cache that cannot see an axis returns a stale size.
The widget's effective locale is held in scope while
onMeasure(limn.scene.Constraints)runs, so everything the measure resolves or formats — anI18nString, a chart tick, a line break — answers in this subtree's language without the subclass doing anything.Correctness is a property of the key: the only way this can return a stale size is if the resolved axes and the constraints and
needsMeasureall say nothing changed, in which case nothing did. -
onMeasure
Reports 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. -
layoutBox
public final void layoutBox(float x, float y, float width, float height) Parent assigns final bounds (parent coords); thenonLayout()places children. -
onLayout
protected void onLayout()Containers position children here (measure +layoutBox(float, float, float, float)per child). -
moveChild
Moves a child without re-running layout: the scroll fast path (size is unchanged; only the offset moves). For container authors. -
markNeedsLayout
public final void markNeedsLayout()Marks this widget's measure dirty and schedules a scene layout pass. -
markNeedsLayoutInPlace
protected final void markNeedsLayoutInPlace()Marks this widget's measure dirty and asks for a layout that repaints only what it moves: the form for a widget whose size may change for a reason of its own, such as a label whose text changed width.It is the pass
setVisible(boolean)uses. The scene climbs from the parent re-running each ancestor's own measure against the constraints it last had, stops at the first whose size did not change, lays that one out in place, and repaints the children that moved, where they were and where they went, plus this widget. Because each ancestor's own layout code runs, a parent that measures a child twice (loose to learn what it wants, then tight) sees the new size as a full pass would; whatever the scene cannot compare, it lays out in full.markNeedsLayout()remains the form that makes no such claim. -
markNeedsContainedLayout
public final void markNeedsContainedLayout()Asks for this widget's own subtree to be laid out again, without declaring that the frame is a full repaint: the form for a container whose insides move while its box does not, such as a virtualised list mounting and recycling rows as it scrolls.A scene treats an ordinary
markNeedsLayout()as full damage, because a layout pass may move any widget without that widget invalidating where it used to be. This is the narrow case where that cannot happen, and it is narrow on purpose:- the widget must clip its children, so nothing inside it can paint outside the rectangle that gets damaged;
- its own measured size must come out unchanged. The scene re-measures against the constraints its parent last gave it and compares: if the size moved, the parent's layout is stale and the scene falls back to a full pass, so getting this wrong costs a frame rather than correctness.
Damage is this widget's bounds. A widget that paints outside them still has its
paintOutset()honoured, because the scene damages through the same pathinvalidate()uses.Adding or removing children from inside the pass is expected and does not escalate. That is what a list does when it recycles a row. The same call from anywhere else, or for a widget outside the subtree being laid out, escalates the way it always did.
-
invalidate
public final void invalidate()Requests a repaint (event-driven: the loop wakes and redraws once). Also records this widget's bounds as damage so a scene with partial rendering on, which is the default, repaints only the changed region. The region is the bounds grown bypaintOutset(): a widget whose painting extends beyond its bounds by a fixed reach (a shadow, a focus ring) answers that reach there, and this covers it.Scene.damage(Rect)is for painting somewhere else in the scene, outside any such reach. -
invalidate
public final void invalidate(float x, float y, float width, float height) Requests a repaint of a region in this widget's local coordinates: the fine-grainedinvalidate()for widgets that know exactly which pixels changed (a blinking caret, one cell of a grid). Under partial rendering only that region is repainted; regions may extend beyond this widget's bounds. -
textRuler
Text measurer for layout-time metrics (never null; NONE when detached). -
neutralBase
What a run of text with no strong character of its own falls back to: this widget's resolved direction, as the shaper's neutral base. A caption that is a bare number, a clock face or a punctuation mark reads the way the interface around it reads, and the first-strong rule cannot know that; the widget can. It is a fallback and not an imposition — a Latin caption in a right-to-left tree still reads left to right, because a strong character already decided it.Read it where
layoutDirection()may be read: in a pass or an event handler, never in a constructor or a field initializer. -
shapeText
Shapes one line oftextthe way this widget reads: the first-strong rule decides for any string that can decide for itself, andneutralBase()decides for the rest. This is the way for a widget to get a line. Hold the result (seeShapedText.matchesfor the idiom), take the natural width from the line's own metrics, and hand the line itself to the canvas.A chart is the widget this matters most to, and the reason is worth keeping in view: its chrome is application data that is very often entirely neutral — a series named
2024, a tooltip row reading3.5, a category calledQ1. Not one of those has a strong character, so the first-strong rule has nothing to decide with and the fallback decides all of it, and the fallback is the direction of the interface, which only the widget knows. A series namedVendasis unaffected, because its V already decided.The alternatives quietly drop the direction:
Canvas.drawText(String, …)andTextRuler.measurecarry no base, so they resolve every all-neutral string — a count, a year, a price — with a hard-coded left-to-right fallback, which is right almost always and silent when it is not. Neither signature can gain a direction (the ruler is a@FunctionalInterfaceevery test fake satisfies with a lambda), so the seam is here, on the widget, which is the one place that knows the answer. -
clipboard
System clipboard (never null; a local no-op when detached). -
paintWidget
Paints this widget and its children (canvasorigin = this widget).It leaves the canvas at the depth it found it. A widget's paint may push clips and transforms, down its own branch and through code the toolkit does not own (an application's icon, an adapter's row, a 3D render callback), and it may stop halfway by throwing. Either a forgotten
restore()or a throw would otherwise leak asave()into every ancestor still unwinding, and the frame would end unbalanced with the warning naming nobody, because whatever caused it left the stack long before.So the depth is taken before and trimmed back after, in a
finally. Individual containers still guard their own clips, which is more precise; this is the net under them, not a licence to stop.An imbalance on a NORMAL return is a plain bug in the widget rather than fallout from something else, so it is reported once per class. On an exceptional return nothing is reported: the throw is already being handled, and a second message about its side effect would only bury it.
An
Opacitycontainer below 1 draws its subtree as one picture: the canvas'slayeris opened around the three paints over what the subtree paints as partial rendering reckons it, each widget's box grown by a pixel and itspaintOutset(), a child's past this one's edge included (paintedBounds()), and the trim that closes every save closes it too. At 0 nothing is painted, as for a widget that misses the clip; at 1 no layer is opened, unless an ancestor's paint left aBlendModeother than normal in force, under which the container's subtree is one picture blended once at every opacity, asCanvas.saveLayer(float, float, float, float, float)draws it. Every other widget opens nothing here. -
paintOutset
protected float paintOutset()How far beyond this widget's bounds its painting may reach, in logical points: its own painting and any descendant no ancestor clips (a drop shadow, a glow, a child positioned outside the parent's box). Partial rendering uses it both ways:invalidate()damage grows by it, and a repaint pass skips this subtree only when bounds + outset miss the pass region; painting farther than declared leaves stale pixels.The work is divided by who knows the number. A widget declares what it paints itself outside its box:
Label's icon overhang, the focus ringButton,CheckboxandRadioButtondraw outside theirs. A widget that lays out children and does not clip them declares how far they may paint past it, because a ring on a child at its edge hangs past it too, and a pass landing only in that band would otherwise skip it and never reach the child. Every layout the toolkit ships does that by default with the reach the theme gives a focus ring, and takes another number through its publicpaintOverflow, which sets what this hook answers; a layout an application writes inherits both by extendinglimn.scene.layout.Container, and one that extends this class directly overrides this hook itself. A widget that clips its children declares only its own painting, since the clip bounds theirs.Asked of every widget a repaint pass considers, and on every
invalidate(), so an override answers in constant time without allocating: a number held or looked up, never a walk over the children. Default0: everything stays inside the bounds, which is true for most leaves. -
onPaint
Widget's own background/content, in local coordinates. -
clipsChildren
protected boolean clipsChildren()WhetherpaintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips). Partial rendering uses it to clamp a descendant'sinvalidate()damage to the visible region; a widget scrolled out of view damages nothing. Any override that clips inpaintChildrenshould also override this, and one whose clip is smaller than its box for some child should override the four clip accessors below too. -
clipX
The left edge, in this widget's own coordinates, of the rectangleclipsChildren()clipschildto.The whole box by default. A widget that paints one child inside an inset -- a scroll pane whose reserved gutters hold its bars, clipping the content to the viewport and the bars to the box -- answers per child, so that
isShowing()and the damage clamp agree with what it paints: until it did, a descendant lying wholly inside the gutter was published on screen in the very rectangle the bar occupies. Consulted only whileclipsChildren()is true, and it must not allocate, because the showing test runs for every node of every accessible walk.- Parameters:
child- the direct child the clipped descendant is under- Returns:
- the clip rectangle's left edge
-
clipY
The top edge of the rectangle this widget clipschildto; seeclipX(Widget).- Parameters:
child- the direct child the clipped descendant is under- Returns:
- the clip rectangle's top edge
-
clipWidth
The width of the rectangle this widget clipschildto; seeclipX(Widget).- Parameters:
child- the direct child the clipped descendant is under- Returns:
- the clip rectangle's width
-
clipHeight
The height of the rectangle this widget clipschildto; seeclipX(Widget).- Parameters:
child- the direct child the clipped descendant is under- Returns:
- the clip rectangle's height
-
paintChildren
Children pass; override to clip (e.g. scroll views).Indexed rather than an enhanced
for, for the reasonScene's own overlay loop already carries: an iterator here is an allocation per widget per frame, and an idle window is the one place in this toolkit that is not allowed to allocate at all. A subtree of two hundred widgets was paying two hundred short-lived objects a frame to walk a list it can index. -
onPaintOverlay
Painted after children (scrollbars, focus rings…). -
hitTest
The deepest widget under a point, this one, ornull: what a press at the point lands on. The point is in this widget's space;sceneToLocal(float, float)turns a scene point into it.nullfor a widget that is not visible or not enabled, for one whose subtree is passing out of reach, and for a point outside its box, a point ofNaNincluded; otherwise whatonHitTest(float, float)answers. One exception to the box: a widget that does not clip its children lets a point outside it through to a descendant drawn moved there, and answers that descendant ornull, never itself. Final, so that these hold for every widget and no container's own answer can skip them: a widget decides only where, inside itself, a point that reached it lands.- Parameters:
localX- the point's x in this widget's spacelocalY- the point's y in this widget's space- Returns:
- the widget the point lands on, or
nullto let it through to what is beneath
-
onHitTest
What this widget answers for a point that reached it: one of its children's answers, itself, ornullto let the point through to what is beneath it. Called byhitTest(float, float)only for a widget that is visible, enabled and in reach, with the point inside its box or where it draws a descendant moved past it, so an override checks none of that again.The default asks each child through
hitTestChild(limn.scene.Widget<?>, float, float), the last first because it is painted on top, and answers the first that is hit, or this widget when none is. A widget overrides it to ask one child before the others (a scroll bar over the content it scrolls), to keep a part of itself from its children, or to be transparent to the pointer.- Parameters:
localX- the point's x in this widget's spacelocalY- the point's y in this widget's space- Returns:
- the widget the point lands on, or
null
-
hitTestChild
Asks one child what a point lands on: maps the point from this widget's space into the child's and calls the child'shitTest(float, float). The only way a widget reaches a child's hit test, so that where this widget draws the child, and not only where layout put it, is where the pointer finds it:nullwhere this widget clips the child away, its clip rectangle for that child.- Parameters:
child- a child of this widgetlocalX- the point's x in this widget's spacelocalY- the point's y in this widget's space- Returns:
- what the child answers
- Throws:
IllegalArgumentException- ifchildis not a child of this widget
-
overlayPassesPointer
protected boolean overlayPassesPointer(float sceneX, float sceneY) When this widget is the active modal overlay, whether a pointer hit at the given scene point should fall through to the content beneath it instead of being captured. Defaultfalse: an overlay owns all pointer input. The in-scene menu overrides this so the menu bar behind the fullscreen fallback keeps hover-switching and its cursor, exactly as it does when the menu is a separate native window. -
onMouseEvent
Mouse events (bubbling); callevent.consume()when handled. -
onKeyEvent
Key events (focused widget first, then ancestors). -
onCanEdit
Whether this widget can do an editing command now: copy with something selected, paste with something to paste, undo with something to take back.Scene.canEdit(limn.scene.EditCommand)asks the widget that has the keyboard and then each of its ancestors, and a menu's item of an editing role is enabled by the answer.A widget answers for the commands it takes from the keyboard, and does for the command what it does for the key.
- Returns:
falsehere: a widget edits nothing until it says so
-
onEdit
Does an editing command, as the user's own act. Called on the first widget, from the one with the keyboard upward, whoseonCanEdit(limn.scene.EditCommand)answeredtrue.- Returns:
- whether it was done
-
onCharTyped
Committed text input (focused widget first, then ancestors). -
onPreedit
In-progress IME composition ("preedit") for this widget while it is focused: the still-composing text, shown inline but not yet committed (the commit later arrives asonCharTyped(limn.scene.event.CharEvent)). Only widgets that accept text input receive it. Default: ignored. -
acceptsTextInput
protected boolean acceptsTextInput()Whether this widget edits text and should drive the platform input method (IME): the scene enables the IME while such a widget holds focus and disables it otherwise, so composition never intercepts keys meant for non-text UI. Text widgets override this totrue. Default:false. -
caretRect
The caret rectangle in scene coordinates (logical points) used to place the IME candidate window;nullwhen there is no caret to anchor (or the widget is not laid out yet). Consulted by the scene only while this widget is focused andacceptsTextInput()is true. -
onDragStart
Asked when a drag may begin on this widget: the pointer was pressed here and has moved a few points, or the drag key was pressed while this widget had the focus. The question bubbles from the widget under the pointer, and the firstDraganswered starts the drag; the press is then over for the widget that had it. Default:null, which leaves the gesture to theDRAGevents it has always been.- Parameters:
event- where the press began, and whether the keyboard asked- Returns:
- what to drag, or null
-
onDragOver
A drag is over this widget, or has moved on it. A widget that would take what is offered callsevent.accept()and marks where the drop would land; one that says nothing passes the question to its parent, and may show that it refuses. The question goes to the widget under the pointer and then to each enabled ancestor in turn, up to the first that accepts. It is asked again on every move, so that the answer can depend on the place, and again when the content under a still pointer scrolls: a pointer drag resting near an edge of a scrolling view it is over, or past one, scrolls that view. In a keyboard drag only the widget the drag key, Tab or an arrow brings the drag to is asked. Files dragged in from the system arrive the same way, their offer holdingDataFormat.FILES. Whatever this draws, accepting or not,onDragExit(limn.scene.event.DragEvent)takes away. Default: says nothing.- Parameters:
event- what is offered and where
-
onDragExit
The drag this widget was last asked about inonDragOver(limn.scene.event.DragEvent)is no longer over it, whether it accepted or not: whatever it drew for the drag goes, the mark of the drop's place or the sign that it refuses. It comes once, at the first of these: a move whose question no longer reaches this widget (the pointer left it, or a widget under the pointer accepted before it was asked); the drag called off, by Escape, another button, the window losing the focus or the source leaving; the drag leaving the window; the drop, on another widget or over this one with no widget taking it; this widget taken out of the scene, hidden or disabled while the drag is over it. In a keyboard drag a widget that does not accept hears it at once, the drag going on to the next, and the target when the drag moves on or ends. A drag from another window or program is told the same. Never afteronDrop(limn.scene.event.DropEvent): the widget handed the drop hears no exit. Default: nothing.- Parameters:
event- the drag as it leaves
-
onDrop
What was dragged is dropped here: on the widget that accepted it last. Read what is wanted fromevent.offer()and carry outevent.action(); whatever marked the place goes now, since noonDragExit(limn.scene.event.DragEvent)follows. Default: nothing.- Parameters:
event- what was dropped and where
-
observeChanges
Watches every change to this widget, from every origin, without touching whatever handler the application registered.A watcher hears what a handler does not: a value a caller wrote, a selection the widget adjusted by itself, a focus move, a label's new text. It is the channel a two-way binding, an inspector, a test and anything describing this tree to something outside it uses, and any number of parties may use it at once without being able to disturb each other or the application.
The subscription belongs to the widget: it survives detach and re-attach, costs nothing to keep across the mount and unmount a recycled list cell performs on every frame of a scroll, and dies with the widget, so there are no weak references here and none are needed. A late subscriber is owed nothing -- there is no replay and no priming call, because nothing can enumerate every aspect of an arbitrary widget and a synthetic change is indistinguishable from a real one to a listener that pushes an undo entry or plays a sound. What a late subscriber does instead is read the widget: subscribe, then read.
- Parameters:
observer- told about every change to this widget; never null- Returns:
- a handle that unregisters; cancelling it twice is a no-op. UI thread
-
notifyChange
Announces a change that has already settled, and then runs the handler if the origin isUSER: the last statement of an ordinary mutator, after the state, the layout marks, the focus move and the reveal.Exactly
announceChange(limn.scene.Change)followed byrunHandler(limn.scene.Change)-- the two halves in one call, which is what every seam but a compound's wants. The order it delivers in is the guarantee:- this widget's watchers, in registration order;
- the watchers of the scene this widget is in, if it is in one;
- this widget's handler, if the origin is
USER.
The handler runs last, and that is load-bearing rather than arbitrary: a handler may mutate other widgets and each of those mutations announces itself as it happens, so a handler that ran first would have every consequence of a change announced before the change itself, and a watcher would see a label's new text before the checkbox that caused it.
Announce only what moved: a mutator handed the state it already holds calls nothing. Where a call moved other aspects on its way to its own, those are announced in the order they settled and the call's own aspect last, which is the signal that the call is finished.
- Parameters:
change- what changed on this widget, and what moved it- Throws:
IllegalStateException- if called from inside a paint. UI thread
-
announceChange
The announcing half alone: this widget's watchers, then its scene's watchers, and no handler at any origin.For a compound whose members must all be announced before any handler runs -- a radio group's leaver and enterer -- which is its only caller in this repository. A seam that calls this owes a matching
runHandler(limn.scene.Change)for the same change, or it has silently disabled the application's handler.- Parameters:
change- what changed on this widget, and what moved it- Throws:
IllegalStateException- if called from inside a paint. UI thread
-
runHandler
The handler half alone: runs this widget's handler for a change this widget has already announced, and nothing at any origin butUSER.It takes the whole change rather than its aspect, because the origin is what decides whether the handler runs at all -- which is what lets a compound seam hand it every member's change without branching once per member.
- Parameters:
change- the change this widget has just announced
-
notifyTextEdit
The same asnotifyChange(limn.scene.Change), for a text edit, taking the edit's shape rather than a built value so that a keystroke on a tree nobody watches constructs nothing. The three numbers come from the editing model, which is the only thing that has them.There is no announce-only sibling: no compound in this repository spans a text edit.
It reaches the handler exactly as
notifyChangedoes, and without aChangeto hand torunHandler(limn.scene.Change): the handler half needs only the aspect and the origin, and both are already here. So after every watcher has run it callshandleUserChange(TEXT)whenoriginisUSER, and nothing when it is not -- a user keystroke on a widget nobody watches still reaches the application's handler and still allocates nothing on this path.- Parameters:
origin- what moved the editoffset- where the damaged range begins, in charsremoved- how many chars it replacedinserted- how many chars it inserted- Throws:
IllegalStateException- if called from inside a paint. UI thread
-
handleUserChange
Runs this widget's application handler for a change it has just announced. Called byrunHandler(limn.scene.Change), and so bynotifyChange(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.- Parameters:
aspect- what changed; the origin isUSERor this is not called
-
onAccessibility
Says what this widget is, for an assistive technology.Called on the UI thread inside the publish step, with this widget's own locale in scope, and never from a paint. A widget that overrides nothing still gets a node with its bounds, its enabled, focusable, visible and showing states, its language, its children in tree order, the focus and scroll-into-view verbs when it is focusable, and its tooltip as a name when nothing else supplied one. What it does not get is a role: a widget that declares no role, no name, no description, no state, no verb and no facet is scaffolding, and the tree removes it and hoists its children in its place, so that a screen reader hears the controls rather than the boxes.
Nothing here may allocate. The builder's setters take a facet's fields rather than a facet, and a name is handed over as the localizable string this widget holds rather than as a resolved one, so that a frame which damaged this widget and changed nothing about it costs a comparison and no memory at all.
Accessibilitystates the two rules and what breaks when they are ignored.- Parameters:
a- the node being described; write into it, and never keep it
-
onAccessibilityChildIdentity
Says who one of this widget's children is, before that child describes itself: the identity key a container that pools its children owns and nothing else can, and, for a container that draws its rows, the synthetic row the child hangs under.Called on the UI thread before the child's node exists, so the only two things that may be written here are the two above; a fact about the child — its role, its position, a state — goes in
onAccessibilityChild(limn.scene.Widget<?>, limn.accessibility.Accessibility), which runs after the child's own hook. The split is what makes identity right: the child's node is begun under the identifier the key decides, so its name is carried over from the last frame under that identifier, and every node it declares inside itself — a synthetic child, a widget it holds — is scoped under it and follows the row when the child is recycled.- Parameters:
child- the child about to be describeda- the builder, open only for the two identity calls
-
onAccessibilityChild
Adds what only this widget knows about one of its children: the role a mounted list cell takes, its position in the data, its selected state.Called on the UI thread with the child's node current and this widget's locale in scope, after the child has described itself, so what is written here wins. The child's identity is not decided here but in
onAccessibilityChildIdentity(limn.scene.Widget<?>, limn.accessibility.Accessibility), which runs first;Accessibility.key(long)refuses to be called from here.This is the only place
Accessibility.delegate(limn.accessibility.Accessible.Action)may be called: a verb this widget claims on the child — a list'sSELECTon a row — is published on the child's node and routed toonAccessibilityChildAction(limn.scene.Widget<?>, long, limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument). The builder refuses a delegation from anywhere else, and refuses one here for a verb the child declared for itself, for a verb that takes an argument, and forFOCUSorSCROLL_INTO_VIEWon a focusable child, which the walk performs on its own. A verb written here withAccessibility.action(limn.accessibility.Accessible.Action)would be dispatched to the child's ownonAccessibilityAction(limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument), which is why a child hook writes facts and delegates verbs.- Parameters:
child- the child being describeda- the child's node
-
onAccessibilityAction
Performs an action an assistive technology asked of this widget, as the user.Called on the UI thread from a posted task, after the scene has re-checked that this widget is still attached, still enabled with every ancestor, still showing and still reachable. The widget performs its own action: it reaches its own private from-the-user path with its own guards, so an assistive technology's toggle notifies the application exactly as a click does and no component has to grow a public method that re-derives a guard it already has.
The platform is not told the answer. It was answered from the published snapshot before this ran: the bridge accepts a verb the node publishes and refuses one it does not, synchronously, and the post that reaches here carries no reply back. So
falsehere is honest bookkeeping and nothing more — a refusedPRESSraises noINVOKED— and the one contract a reader can see is the published one: a node accepts exactly the parameterless verbs in its action facet plus the setters its writable facets imply. A widget that would answertruefor a verb it did not publish is a defect the gallery ratchet inlimn-demoreports; a widget that accepts a synonym publishes it.- Parameters:
action- what was asked; one of the seventeen, including the four that carry an argumentarg- the argument, orAccessible.Argument.NONE- Returns:
- whether this widget did it.
falseis the honest answer for a verb it does not offer.
-
onAccessibilityChildAction
protected boolean onAccessibilityChildAction(Widget<?> child, long key, Accessible.Action action, Accessible.Argument arg) Performs a verb this widget claimed on one of its widget children: a list'sSELECTon a row that is the application's own cell, published on the row where a reader addresses it and performed by the list, which is the only thing that knows what selecting that row means.Called on the UI thread from a posted task, after the scene re-checked the child the way it re-checks any node an action lands on, and that this widget is still its parent — with one difference: the child need only be visible, and it is this widget that must be showing, because this widget performs the verb and a cursor row kept outside its viewport is not showing. The child is named twice on purpose: by the key this widget gave it in
onAccessibilityChildIdentity(limn.scene.Widget<?>, limn.accessibility.Accessibility), which is how a pooling container thinks of a row, and by the widget itself, for a container that keys nothing. Every verb the child declared for itself still reaches the child's ownonAccessibilityAction(limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument); only the verbs this widget delegated arrive here.- Parameters:
child- the child the verb was addressed tokey- the identity key this widget gave that child, or0when it gave noneaction- what was askedarg- the argument, orAccessible.Argument.NONE- Returns:
- whether this widget did it
-
onSyntheticAction
Performs an action addressed to one of this widget's synthetic children: a menu row, a combo option, a chart series — something this widget draws and never instantiated as a widget.Separate from
onAccessibilityAction(limn.accessibility.Accessible.Action, limn.accessibility.Accessible.Argument)deliberately, and not distinguished by a sentinel key: a model index of zero is a legitimate key, so the two cannot share one hook without the widget having to guess which of them it is in.- Parameters:
key- the key this widget gave that child when it declared itaction- what was askedarg- the argument, orAccessible.Argument.NONE- Returns:
- whether this widget did it
-
paintsFromBackdrop
protected boolean paintsFromBackdrop()Whether this widget's picture is made partly of the pixels behind it: a wash, a frost, a refraction. Defaultfalse.It exists for one mode and is inert outside it. With partial rendering on, a frame repaints only what was invalidated, and a widget like this can be stale without having changed at all: nothing about it moved, but what it is made of did. That was the mode's known limit, and this is its fix. A widget that answers
trueis registered with its scene, and its rectangle joins the damage of any frame whose damage reaches it.Reaching it is over-approximated on purpose. What actually stales the picture is a change behind it, and the test here is any intersection, front or back. Deciding front from back means knowing paint order, which the damage list does not carry; the over-approximation costs a repaint of something already being painted over and can never miss one. That is the right way round for a correctness rule.
Answer it from the class rather than from state: it is read when the widget joins a scene, not on every frame. A widget whose answer could change with a setter would have to re-register, and nothing in this toolkit needs that — a backdrop panel is built with an effect and never loses it.
- Returns:
- whether the pixels behind this widget are part of what it draws
-
backdropReach
protected float backdropReach()How far outside its box a widget that paints from its backdrop reads the pixels behind it, in points: a blur's radius, a refracting rim's depth. Default 0. Consulted on every frame, so it may follow the widget's state.The scene widens the rectangle it adds to a frame's damage by this much, for two reasons that are the same reason. A change that far from the widget changes its picture, so it has to repaint. And it has to be repainted together with that margin: outside the frame's passes the margin holds the last frame finished, with whatever was painted over it, where a whole frame lets the widget read it before anything later is drawn. A scroll bar laid over the end of a bar of frosted glass came out blurred into the glass on every partial repaint, and only there.
- Returns:
- the reach, 0 or more
-
paintsDecoration
protected boolean paintsDecoration()Whether whatonPaint(limn.graphics.Canvas)draws is material rather than meaning: a wash, a blur, a shadow, a rule. Defaultfalse.A widget that draws its own content and declares nothing is removed from the accessible tree by the transparency rule, and the toolkit says so once per class — because the usual cause is a gauge or a sparkline nobody named, and the interface it drew is then simply absent with nothing said. That guard asks "does this class draw?", which reflection can answer; it cannot ask whether the drawing means anything, and this widget is the only thing that can. Overriding this to
trueis that answer, and the removal then happens in silence.It is not
setAccessibleIgnored(boolean), and the difference is the subtree. Ignored takes this widget's children out of the tree with it, which on a panel whose whole purpose is to sit behind controls hides every control it wraps. Saying the painting is decoration changes nothing about the tree at all: the node is removed exactly as it would have been, the children hoist into its place, and only the warning goes away.Answer it
trueonly for a class whose drawing a reader loses nothing by never hearing about. A widget that paints information is named or given a role instead; there is no third answer that leaves the picture out and keeps the user informed.- Returns:
- whether this widget's own drawing carries no information
-
setAccessibleLabelledBy
Names this widget from another widget's text, the way a form's caption names the field it sits beside. UI thread only.One at a time, and declared rather than inferred. A caption that merely sits near a field does not name it: proximity is a layout accident, and a tree built from it says confident wrong things. So the link is written, it replaces any previous one, and passing
nullremoves it. The named node carries aAccessible.Relation.LABELLED_BYrelation to the label, so a client that would rather read the label's own node than a copied string can walk to it.The text is read from the label on every publish rather than copied here, so a caption that changes its string, or is re-resolved into another language, renames what it labels with nothing to keep in step. A widget that has no text to offer names nothing, and the relation still stands.
An explicit
setAccessibleName(limn.i18n.I18nString)still wins over this, and this wins over whatever the widget would have derived for itself.- Parameters:
label- the widget whose text names this one, ornullto remove the link- See Also:
-
accessibleLabelText
The text this widget offers when it is named as another's label, ornullwhen it is not the kind of widget that captions anything.Overridden by the widgets that are captions in their own right; everything else declines, which is why a link to one names nothing rather than inventing a string from its children.
- Returns:
- the caption text, or
null
-
accessibleLabelAccessKey
The chord of the access letter this widget, a caption, gives the widget it labels, ornull: what a reader is told reaches the field. Default none.- Returns:
- the access key's display form, or
null
-
accessibleLabelTarget
The widget a label bound to this one names: this widget, unless it is a composite whose keyboard lands on an inner control, in which case that control.A form's caption is bound to the widget the application holds — a date picker — and what a reader arrives at is the field inside it. Naming the group would put the caption on a node the keyboard never lands on and leave the focused field nameless. So a composite answers the child that should carry the caption, and the publish step names that child from the label with
NameFrom.LABEL, gives it theLABELLED_BYrelation, resolves the label's ownLABEL_FORto it, and leaves this widget's node without either — a group that declares nothing else is then transparent. The answer may redirect again (a composite inside a composite) and it must be a descendant of this widget: anything else is refused by the walk, loudly, because a caption that lands on a stranger is exactly the confidently wrong name the toolkit refuses to infer.A name and a description written on this widget with
setAccessibleName(limn.i18n.I18nString)andsetAccessibleDescription(limn.i18n.I18nString)go to the same widget, for the same reason: they are meant for the node a reader reaches. What that widget was given itself wins over them, as its own caption wins over one sent down to it.Called on the UI thread by the publish step, while a label is bound to this widget or a name or a description is written on it. A widget that answers
nullkeeps them itself.- Returns:
- the widget that carries a label bound to this one;
thisby default - See Also:
-
setAccessibleDescribedBy
Describes this widget from another widget's text, the way the message beneath a field says why the field is invalid. UI thread only.The same rule as
setAccessibleLabelledBy(limn.scene.Widget<?>): declared and never inferred, one at a time,nullremoves it, and the text is read from the source on every publish so a message that changes its string describes with nothing to keep in step. The described node carries aAccessible.Relation.DESCRIBED_BYrelation to the source, so a client may read either.A bound description beats the tooltip the walk would otherwise fall back on, and an explicit
setAccessibleDescription(limn.i18n.I18nString)beats both.- Parameters:
source- the widget whose text describes this one, ornullto remove the link- See Also:
-
setId
Names this widget for the code that looks it up:Scene.find(String), a test's driver clicking it by id, and a tool that drives the running application from outside, which finds it by the same id through the system's accessibility: UI Automation's automation id on Windows, the accessibility identifier on macOS, the accessible id on Linux. Nobody sees an id and no screen reader speaks one; that issetAccessibleName(limn.i18n.I18nString). An id need not be unique: a lookup answers the first match. A widget given none is published under a number that holds while it stays in the scene. UI thread only.- Parameters:
id- the id, ornullfor none (the default)- Returns:
- this widget
-
id
- Returns:
- the id
setId(java.lang.String)gave, ornull
-
setAccessibleName
Names this widget for an assistive technology, overriding whatever it would have derived for itself. UI thread only.An application-set name always wins, so renaming a component never requires subclassing it. It is resolved under this widget's own locale, like every other name in the tree. On a composite whose keyboard lands on a part inside it, it names that part; see
accessibleLabelTarget().- Parameters:
name- the name, ornullto let the widget name itself again
-
setAccessibleName
Names this widget with a fixed string. UI thread only.- Parameters:
name- the name, ornullto let the widget name itself again
-
setAccessibleDescription
Describes this widget at more length than its name does, for an assistive technology. UI thread only. On a composite whose keyboard lands on a part inside it, it describes that part, assetAccessibleName(limn.i18n.I18nString)names it.- Parameters:
text- the description, ornullto let the widget describe itself again
-
setAccessibleDescription
Describes this widget with a fixed string. UI thread only.- Parameters:
text- the description, ornullto let the widget describe itself again
-
setAccessibleRole
Says what this widget is, overriding whatever role it would have declared. UI thread only.This is also how a custom widget that paints its own content joins the tree at all: a widget that declares no role is scaffolding and is removed, and the toolkit warns once per class when the class it removes paints something.
- Parameters:
role- the role, ornullto let the widget declare its own
-
setAccessibleIgnored
Removes this widget and its subtree from the accessible tree: a spacer, a decorative rule, an image that repeats what the text beside it already says. UI thread only.Not for a control that can be operated. Marking one of those decorative is not a deferral, it is hiding it from the one user who cannot see it.
- Parameters:
ignored- whether to leave this widget out of the tree entirely
-
invalidateAccessible
public final void invalidateAccessible()Says that something about this widget an assistive technology would care about has changed, when nothing repainted and nothing moved.Almost nothing needs this. The accessible tree is rebuilt from the funnel every repaint goes through, so a value, a state, a caret and a selection all reach it already. What does not is a change that paints nothing at all — an application writing a field behind a setter's back, or setting one of the four accessibility overrides above, all of which call this themselves.
It sets the flag whether or not anything is listening, so switching a bridge on mid-session needs no audit of what was missed, and that costs one store. It buys the frame that reads the flag only while something is listening, because a flag set with no frame coming is a no-op with a comforting name.
-
onFocusGained
protected void onFocusGained()Called when this widget takes keyboard focus. Default: nothing. -
focusArrivedByTraversal
protected final boolean focusArrivedByTraversal()How the focus this widget is being given arrived:truefor Tab or Shift+Tab,falsefor a click, arequestFocus()from code, or focus restored when an overlay closed.The distinction exists because several desktop conventions apply to one and not the other: a single-line field selects its contents when tabbed into, so the next keystroke replaces them, and must not when clicked into, where the click placed a caret the user chose.
Only meaningful inside
onFocusGained(). Asked at any other time it answersfalse, because the flag is set for exactly the duration of the traversal that raised it. A widget that stored the answer to consult later would be reading the last traversal in the scene, not its own.- Returns:
- whether Tab brought the focus here
-
focusArrivedBackward
protected final boolean focusArrivedBackward()Whether the focus this widget is being given arrived by Shift+Tab, walking backward: a widget with more than one stop of its own (a table's header and its rows) enters at its last stop then, and at its first otherwise, so that the two directions mirror.falsefor a forward Tab and for every arrival that is not a traversal. Only meaningful insideonFocusGained(), asfocusArrivedByTraversal()is.- Returns:
- whether Shift+Tab brought the focus here
-
onFocusLost
protected void onFocusLost()Called when this widget loses keyboard focus. Default: nothing.
-