Class Widget
- Direct Known Subclasses:
Button,Chart,Checkbox,ColorPicker,ColorPickerButton,ComboBox,Expanded,Flex,ImageView,Label,ListView,MediaControls,MenuBar,Padding,PerfFooter,ProgressBar,RadioButton,ScrollBar,ScrollView,SegmentedControl,Separator,SizedBox,Slider,Spinner,SplitPane,Stack,TabbedPane,TextArea,TextField,ThemeEditor,ThemePreview,TokenBox,ToolBar,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 enabled 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.voidAppends a child (UI thread only).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 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 booleanWhetherpaintChildren(limn.graphics.Canvas)clips its children to this widget's bounds (scroll views, list viewports, tab strips).final ControlSizecursor()final ControlSizefinal LayoutDirectionfinal Localeprotected 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.final floatheight()Laid-out height in logical points;0until the first layout pass.hitTest(float localX, float localY) Deepest visible/enabled descendant containing the point (local coords), or this widget itself;nullwhen outside.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 booleanWhether this widget accepts input; a disabled widget still occupies its box.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 booleanfinal booleanThis widget's own visibility flag; seeisShowing()for whether it is on screen.final voidlayoutBox(float x, float y, float width, float height) Parent assigns final bounds (parent coords); thenonLayout()places children.final LayoutDirectionfinal Localelocale()final floatThis widget's origin x in scene coordinates (its offsets summed to the root).final floatThis widget's origin y in scene coordinates (its offsets summed to the root).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.final Sizemeasure(Constraints constraints) Measures the preferred size underconstraints.protected static voidMoves a child without re-running layout: the scroll fast path (size is unchanged; only the offset moves).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 voidCalled when this widget enters a scene (attached to the tree).protected voidonCharTyped(CharEvent event) Committed text input (focused widget first, then ancestors).protected voidCalled when this widget leaves the scene (detached from the tree).protected voidonFileDrop(FileDropEvent event) Files dropped from the OS onto this widget (bubbling from the widget under the pointer); callevent.consume()when handled.protected voidCalled when this widget takes keyboard focus.protected voidCalled when this widget loses keyboard focus.protected voidonKeyEvent(KeyEvent event) Key events (focused widget first, then ancestors).protected 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 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).final voidpaintWidget(Canvas canvas) Paints this widget and its children (canvasorigin = this widget).final Widgetparent()The parent this widget was added to, ornullwhile it is unattached.voidRemoves a child (UI thread only).final voidAsks the scene to move keyboard focus here (UI thread only).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 Scenescene()The scene this widget belongs to, ornulluntil it is added to one.final floatsceneToLocalX(float sceneX) Converts a scene x coordinate into this widget's local space.final floatsceneToLocalY(float sceneY) Converts a scene y coordinate into this widget's local space.final voidsetControlSize(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).voidSets 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).voidsetEnabled(boolean enabled) Enables or disables input.final voidsetFocusable(boolean focusable) Declares whether this widget can take keyboard focus.voidsetImageCursor(ImageCursor cursor) Sets a customImageCursorshown while the pointer is over this widget (inherits down the tree likesetCursor(limn.backend.Cursor)).final voidsetInheritanceHost(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 voidsetLayoutDirection(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 voidSets the locale for this widget and every descendant that does not declare its own; it inherits down the tree likesetControlSize(limn.scene.ControlSize).voidsetTooltip(String text) Sets the hover tooltip text (nullclears it).voidsetTooltip(I18nString text) Sets a tooltip that follows the UI language (nullclears it).voidsetVisible(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).tooltip()final floatwidth()Laid-out width in logical points;0until the first layout pass.final floatx()final floaty()
-
Constructor Details
-
Widget
public Widget()
-
-
Method Details
-
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). -
remove
Removes a child (UI thread only). -
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).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. -
x
public final float x()- Returns:
- x in parent coordinates
-
y
public final float y()- 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. -
sceneToLocalX
public final float sceneToLocalX(float sceneX) Converts a scene x coordinate into this widget's local space. -
sceneToLocalY
public final float sceneToLocalY(float sceneY) Converts a scene y coordinate into this widget's local space. -
localToSceneX
public final float localToSceneX()This widget's origin x in scene coordinates (its offsets summed to the root). -
localToSceneY
public final float localToSceneY()This widget's origin y in scene coordinates (its offsets summed to the root). -
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. -
isVisible
public final boolean isVisible()This widget's own visibility flag; seeisShowing()for whether it is on screen. -
isShowing
public final boolean isShowing()- 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), and not scrolled fully out of an ancestor that clips (a scroll viewport). 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
public void setVisible(boolean visible) 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. -
isEnabled
public final boolean isEnabled()Whether this widget accepts input; a disabled widget still occupies its box. -
setEnabled
public void setEnabled(boolean enabled) Enables or disables input. Repaints without re-laying-out, since the box does not change; disabling revokes focus, hover and any press inside the subtree. UI thread only. -
isFocusable
public final boolean isFocusable()Whether keyboard focus can land here: false for containers and static chrome. -
setFocusable
public final void setFocusable(boolean focusable) Declares whether this widget can take keyboard focus. Does not move focus away if it currently holds it. UI thread only. -
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. -
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. -
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.
-
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. -
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). -
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. -
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.
- 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.
-
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 (ADR 035); 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 ADR 006 §4's escape hatch, delivered by ADR 035: 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,(height() - metrics.height()) / 2 + metrics.ascent().Valid only once this widget has been given a box.
Flexguarantees that: it lays every child of a BASELINE line out at cross position 0 first, reads the baselines, then repositions withmoveChild(limn.scene.Widget, float, float), which moves without re-running layout. -
baselineOffsetOf
Readschild'sbaselineOffset(). For container authors, the sameprotected staticbridge shape asmoveChild(limn.scene.Widget, float, float), 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. -
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 (ADR 035).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. -
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 enabled repaints only the changed region. A widget whose painting can extend beyond its bounds must widen the region viaScene.damage(Rect). -
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.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.
-
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. Default0: everything stays inside the bounds, which is true for most built-in components, but not all.Label(icon overhang) andButton,CheckboxandRadioButton(focus rings drawn outside the box) override it. -
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. -
paintChildren
Children pass; override to clip (e.g. scroll views). -
onPaintOverlay
Painted after children (scrollbars, focus rings…). -
hitTest
Deepest visible/enabled descendant containing the point (local coords), or this widget itself;nullwhen outside. Later children win because they paint on top. -
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). -
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. -
onFileDrop
Files dropped from the OS onto this widget (bubbling from the widget under the pointer); callevent.consume()when handled. Default: ignored. -
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
-
onFocusLost
protected void onFocusLost()Called when this widget loses keyboard focus. Default: nothing.
-