Package limn.scene

Class Widget

java.lang.Object
limn.scene.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

public abstract class Widget extends Object
Base of the widget tree. A widget has bounds in its parent's coordinate space, visibility/enabled/focusable flags, children, and takes part in the two-phase layout (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
    Constructor
    Description
     
  • Method Summary

    Modifier and Type
    Method
    Description
    protected boolean
    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.
    void
    add(Widget child)
    Appends a child (UI thread only).
    protected float
    Distance from this widget's top edge to its first text baseline, in logical points: the alignment reference for Flex.CrossAlignment.BASELINE.
    protected static float
    Reads child's baselineOffset().
    protected Rect
    The caret rectangle in scene coordinates (logical points) used to place the IME candidate window; null when there is no caret to anchor (or the widget is not laid out yet).
    final List<Widget>
    The children, in paint and hit-test order: an unmodifiable view of live state.
    protected final Clipboard
    System clipboard (never null; a local no-op when detached).
    protected boolean
    Whether paintChildren(limn.graphics.Canvas) clips its children to this widget's bounds (scroll views, list viewports, tab strips).
     
     
     
     
    final Locale
     
    protected final boolean
    How the focus this widget is being given arrived: true for Tab or Shift+Tab, false for a click, a requestFocus() from code, or focus restored when an overlay closed.
    final float
    Laid-out height in logical points; 0 until the first layout pass.
    hitTest(float localX, float localY)
    Deepest visible/enabled descendant containing the point (local coords), or this widget itself; null when outside.
     
    final void
    Requests a repaint (event-driven: the loop wakes and redraws once).
    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-grained invalidate() for widgets that know exactly which pixels changed (a blinking caret, one cell of a grid).
    final boolean
    Whether this widget accepts input; a disabled widget still occupies its box.
    final boolean
    Whether keyboard focus can land here: false for containers and static chrome.
    final boolean
    Whether this widget currently holds its scene's keyboard focus.
    final boolean
     
    final boolean
    This widget's own visibility flag; see isShowing() for whether it is on screen.
    final void
    layoutBox(float x, float y, float width, float height)
    Parent assigns final bounds (parent coords); then onLayout() places children.
     
    final Locale
     
    final float
    This widget's origin x in scene coordinates (its offsets summed to the root).
    final float
    This widget's origin y in scene coordinates (its offsets summed to the root).
    final void
    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.
    final void
    Marks this widget's measure dirty and schedules a scene layout pass.
    final Size
    measure(Constraints constraints)
    Measures the preferred size under constraints.
    protected static void
    moveChild(Widget child, float x, float y)
    Moves a child without re-running layout: the scroll fast path (size is unchanged; only the offset moves).
    protected final ShapedText.Direction
    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.
    protected void
    Called when this widget enters a scene (attached to the tree).
    protected void
    Committed text input (focused widget first, then ancestors).
    protected void
    Called when this widget leaves the scene (detached from the tree).
    protected void
    Files dropped from the OS onto this widget (bubbling from the widget under the pointer); call event.consume() when handled.
    protected void
    Called when this widget takes keyboard focus.
    protected void
    Called when this widget loses keyboard focus.
    protected void
    Key events (focused widget first, then ancestors).
    protected void
    Containers position children here (measure + layoutBox(float, float, float, float) per child).
    protected abstract Size
    onMeasure(Constraints constraints)
    Reports the size this widget wants within constraints.
    protected void
    Mouse events (bubbling); call event.consume() when handled.
    protected void
    onPaint(Canvas canvas)
    Widget's own background/content, in local coordinates.
    protected void
    Painted after children (scrollbars, focus rings…).
    protected void
    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 as onCharTyped(limn.scene.event.CharEvent)).
    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.
    protected void
    Children pass; override to clip (e.g.
    protected float
    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).
    final void
    Paints this widget and its children (canvas origin = this widget).
    final Widget
    The parent this widget was added to, or null while it is unattached.
    void
    remove(Widget child)
    Removes a child (UI thread only).
    final void
    Asks the scene to move keyboard focus here (UI thread only).
    final void
    Asks every Scrollable ancestor, 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 Scene
    The scene this widget belongs to, or null until it is added to one.
    final float
    sceneToLocalX(float sceneX)
    Converts a scene x coordinate into this widget's local space.
    final float
    sceneToLocalY(float sceneY)
    Converts a scene y coordinate into this widget's local space.
    final void
    Sets the step for this widget and every descendant that does not declare its own; it inherits down the tree like setCursor(limn.backend.Cursor).
    void
    setCursor(Cursor cursor)
    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).
    void
    setEnabled(boolean enabled)
    Enables or disables input.
    final void
    setFocusable(boolean focusable)
    Declares whether this widget can take keyboard focus.
    void
    Sets a custom ImageCursor shown while the pointer is over this widget (inherits down the tree like setCursor(limn.backend.Cursor)).
    final void
    Links this widget's inherited axes — its ControlSize, its LayoutDirection and its locale alike — to host, for the case the tree cannot express: a widget that is the root of its own Scene (a popup or dialog window) or a overlay, both of which have no parent.
    final void
    Sets the layout direction for this widget and every descendant that does not declare its own; it inherits down the tree like setControlSize(limn.scene.ControlSize).
    final void
    setLocale(Locale locale)
    Sets the locale for this widget and every descendant that does not declare its own; it inherits down the tree like setControlSize(limn.scene.ControlSize).
    void
    Sets the hover tooltip text (null clears it).
    void
    Sets a tooltip that follows the UI language (null clears it).
    void
    setVisible(boolean visible)
    Shows or hides this widget and its subtree, re-running layout so siblings take the space back.
    protected final ShapedText
    shapeText(String text, Font font)
    Shapes one line of text the way this widget reads: the first-strong rule decides for any string that can decide for itself, and neutralBase() decides for the rest.
    protected final TextRuler
    Text measurer for layout-time metrics (never null; NONE when detached).
     
    final float
    Laid-out width in logical points; 0 until the first layout pass.
    final float
    x()
     
    final float
    y()
     

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • Widget

      public Widget()
  • Method Details

    • parent

      public final Widget parent()
      The parent this widget was added to, or null while it is unattached.
    • children

      public final List<Widget> children()
      The children, in paint and hit-test order: an unmodifiable view of live state.
    • add

      public void add(Widget child)
      Appends a child (UI thread only).
    • remove

      public void remove(Widget child)
      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 becomes null once 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 on setVisible(boolean). Default no-op.

    • scene

      public final Scene scene()
      The scene this widget belongs to, or null until 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; 0 until the first layout pass.
    • height

      public final float height()
      Laid-out height in logical points; 0 until 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 every Scrollable ancestor, 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. The Scene calls 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_OUTSET is 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; see isShowing() 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

      public Cursor cursor()
      Returns:
      the mouse cursor this widget requests while hovered, or null to inherit from an ancestor (the scene falls back to Cursor.DEFAULT at the root). Non-final so a component can override it with a state-dependent shape.
    • setCursor

      public void setCursor(Cursor cursor)
      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). null restores inheritance. UI thread only.
    • imageCursor

      public ImageCursor imageCursor()
      Returns:
      the custom image cursor this widget requests while hovered, or null to inherit. Resolved like cursor(), with an image cursor winning over a shape on the same widget.
    • setImageCursor

      public void setImageCursor(ImageCursor cursor)
      Sets a custom ImageCursor shown while the pointer is over this widget (inherits down the tree like setCursor(limn.backend.Cursor)). null restores inheritance/shape resolution. UI thread only.
    • tooltip

      public String 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 (like cursor), 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

      public void setTooltip(String text)
      Sets the hover tooltip text (null clears it). UI thread only.
    • setTooltip

      public void setTooltip(I18nString text)
      Sets a tooltip that follows the UI language (null clears 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

      public final ControlSize declaredControlSize()
      Returns:
      the step this widget declares for itself and its subtree, or null when it inherits. This is the "is it set here" reader; use controlSize() for the effective value (cf. isVisible() vs isShowing()).
    • controlSize

      public final ControlSize 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, else ControlSize.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 long compare 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.

      final by 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 with setInheritanceHost(limn.scene.Widget) instead of overriding anything.

    • setControlSize

      public final void setControlSize(ControlSize size)
      Sets the step for this widget and every descendant that does not declare its own; it inherits down the tree like setCursor(limn.backend.Cursor). null restores 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

      public final void setInheritanceHost(Widget host)
      Links this widget's inherited axes — its ControlSize, its LayoutDirection and its locale alike — to host, for the case the tree cannot express: a widget that is the root of its own Scene (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). null unlinks. 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 - if host resolves through this widget
    • declaredLayoutDirection

      public final LayoutDirection declaredLayoutDirection()
      Returns:
      the direction this widget declares for itself and its subtree, or null when it inherits. The "is it set here" reader; use layoutDirection() for the effective value.
    • layoutDirection

      public final LayoutDirection 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, else LayoutDirection.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 one onPaint put the caret on one side and the selection band on the other.

      Steady-state cost is one long compare 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.

      final by contract, for controlSize()'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

      public final void setLayoutDirection(LayoutDirection direction)
      Sets the layout direction for this widget and every descendant that does not declare its own; it inherits down the tree like setControlSize(limn.scene.ControlSize). null restores 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

      public final Locale declaredLocale()
      Returns:
      the locale this widget declares for itself and its subtree, or null when it inherits. The "is it set here" reader; use locale() for the effective value.
    • locale

      public final Locale 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 its layoutDirection(), 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), so I18nString.get(), I18n.localizeDigits and 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 long compares and a field read. The memo is validated against its own epoch and I18n.epoch(), because the chain bottoms out in the process locale and I18n.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.

      final by contract, for controlSize()'s reason: measure(limn.scene.Constraints) keys its cache on the resolved locale.

    • setLocale

      public final void setLocale(Locale locale)
      Sets the locale for this widget and every descendant that does not declare its own; it inherits down the tree like setControlSize(limn.scene.ControlSize). null restores 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 for Flex.CrossAlignment.BASELINE. Default height(): align on the bottom edge, the correct fallback for a widget with no text. Text-bearing components override with the expression they already paint with, (height() - metrics.height()) / 2 + metrics.ascent().

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

    • baselineOffsetOf

      protected static float baselineOffsetOf(Widget child)
      Reads child's baselineOffset(). For container authors, the same protected static bridge shape as moveChild(limn.scene.Widget, float, float), and necessary for the same reason: baselineOffset() is protected, so a Flex in limn.scene.layout cannot invoke it on another instance (JLS 6.6.2.1), but a protected static member carries no qualifying-type restriction.
    • measure

      public final Size measure(Constraints constraints)
      Measures the preferred size under constraints. Results are cached until markNeedsLayout(), and the cache key includes the resolved axes — the control size, the layout direction and the locale, as one MeasuredAxes value — 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 implement onMeasure(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 — an I18nString, 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 needsMeasure all say nothing changed, in which case nothing did.

    • onMeasure

      protected abstract Size onMeasure(Constraints constraints)
      Reports the size this widget wants within constraints. Called once per layout pass, and the result is cached against the constraints and the resolved axes (size step, layout direction, locale), so it must be a pure function of them and of this widget's own state.

      Resolve the ControlSize and the LayoutDirection once each here and thread them down; never read either in a constructor. The locale needs no threading: it is in scope, and I18n.locale() answers it wherever text is resolved.

    • layoutBox

      public final void layoutBox(float x, float y, float width, float height)
      Parent assigns final bounds (parent coords); then onLayout() places children.
    • onLayout

      protected void onLayout()
      Containers position children here (measure + layoutBox(float, float, float, float) per child).
    • moveChild

      protected static void moveChild(Widget child, float x, float y)
      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 path invalidate() 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 via Scene.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-grained invalidate() 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

      protected final TextRuler textRuler()
      Text measurer for layout-time metrics (never null; NONE when detached).
    • neutralBase

      protected final ShapedText.Direction 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

      protected final ShapedText shapeText(String text, Font font)
      Shapes one line of text the way this widget reads: the first-strong rule decides for any string that can decide for itself, and neutralBase() decides for the rest. This is the way for a widget to get a line. Hold the result (see ShapedText.matches for 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, …) and TextRuler.measure carry 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 @FunctionalInterface every test fake satisfies with a lambda), so the seam is here, on the widget, which is the one place that knows the answer.

    • clipboard

      protected final Clipboard clipboard()
      System clipboard (never null; a local no-op when detached).
    • paintWidget

      public final void paintWidget(Canvas canvas)
      Paints this widget and its children (canvas origin = 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 a save() 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. Default 0: everything stays inside the bounds, which is true for most built-in components, but not all. Label (icon overhang) and Button, Checkbox and RadioButton (focus rings drawn outside the box) override it.
    • onPaint

      protected void onPaint(Canvas canvas)
      Widget's own background/content, in local coordinates.
    • clipsChildren

      protected boolean clipsChildren()
      Whether paintChildren(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's invalidate() damage to the visible region; a widget scrolled out of view damages nothing. Any override that clips in paintChildren should also override this.
    • paintChildren

      protected void paintChildren(Canvas canvas)
      Children pass; override to clip (e.g. scroll views).
    • onPaintOverlay

      protected void onPaintOverlay(Canvas canvas)
      Painted after children (scrollbars, focus rings…).
    • hitTest

      public Widget hitTest(float localX, float localY)
      Deepest visible/enabled descendant containing the point (local coords), or this widget itself; null when 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. Default false: 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

      protected void onMouseEvent(MouseEvent event)
      Mouse events (bubbling); call event.consume() when handled.
    • onKeyEvent

      protected void onKeyEvent(KeyEvent event)
      Key events (focused widget first, then ancestors).
    • onCharTyped

      protected void onCharTyped(CharEvent event)
      Committed text input (focused widget first, then ancestors).
    • onPreedit

      protected void onPreedit(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 as onCharTyped(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 to true. Default: false.
    • caretRect

      protected Rect caretRect()
      The caret rectangle in scene coordinates (logical points) used to place the IME candidate window; null when there is no caret to anchor (or the widget is not laid out yet). Consulted by the scene only while this widget is focused and acceptsTextInput() is true.
    • onFileDrop

      protected void onFileDrop(FileDropEvent event)
      Files dropped from the OS onto this widget (bubbling from the widget under the pointer); call event.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: true for Tab or Shift+Tab, false for a click, a requestFocus() 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 answers false, 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.