Class Scene
- All Implemented Interfaces:
WindowInput
Per-frame pipeline (deterministic, spec order): user input (dispatched
from the backend's input-batch hook, before the Ui.post drain) →
posted tasks → animation tick → layout if dirty → paint.
Coalescing: consecutive mouse-moves collapse to the newest, consecutive scrolls accumulate deltas, consecutive resizes collapse; clicks and keys are never dropped and never reordered.
Slow-handler instrumentation: every widget handler invocation is timed; exceeding the budget (default 8 ms) logs a warning naming the widget, the "StrictMode" that keeps the render thread honest.
V1 limitation: mouse capture tracks a single pressed widget/button, so chorded presses (two buttons held at once) synthesize a CLICK only for the most recent one.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic interfaceAnimation hook, called once per frame while registered. -
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final doubleLongest step any ticker is handed in one frame, whatever the wall clock says. -
Constructor Summary
ConstructorsConstructorDescriptionA scene overroot.Scene(Widget root, LongSupplier clock) Injectable clock (slow-handler instrumentation and animation ticks), public so component tests can drive animations deterministically. -
Method Summary
Modifier and TypeMethodDescriptionvoidaddRealTimeTicker(Scene.Ticker ticker) Registers a ticker on wall time:setTimeScale(double)andsetPaused(boolean)do not reach it.addShortcutHandler(Predicate<KeyEvent> handler) Registers a scene-wide keyboard handler for chords the focused widget did not want: menu accelerators, and anything else that must work without being focused.voidaddTicker(Scene.Ticker ticker) Registers an animation ticker on scene time; frames keep coming while any is active.The colour cleared behind the tree each frame.voidbind(NativeWindow window) Wires this scene into a window: input, frame rendering, invalidation, clipboard.voidcharTyped(int codepoint) Committed text input, one code point (IME/layout-aware, after key events).voidMarks a scene-space region as damaged and schedules a frame, for painting that extends beyond the invalidating widget's bounds (shadows, carets in overlays).voiddisposeLater(Runnable gpuCleanup) LikedisposeLater(GpuSurface)for any GPU cleanup that must run with this scene's GL context current, e.g.voiddisposeLater(GpuSurface surface) Schedules aGpuSurfacefor disposal at the next frame, when this scene's window/GL context is current.voidfadeWindow(float target, double seconds, Runnable onArrive) Animates this scene's native-window opacity towardtarget([0..1]) overseconds, runningonArriveonce it settles.voidfadeWindowIn(double seconds) Snaps the window opacity to0immediately, then eases it to1overseconds, the flash-free "appear" sequence.voidfadeWindowOut(double seconds, Runnable onGone) Eases the window opacity to0, then runsonGone(typically a close).voidfilesDropped(List<Path> paths) Files dragged from the OS and dropped on the window.The widget holding keyboard focus, ornullwhen nothing does.voidfocusTraverse(boolean backward) Moves focus to the next/previous focusable widget in layout (DFS) order.voidinheritRenderingFlags(Scene owner) Copies the rendering flags (partial rendering, damage debug) from the scene that spawned this one: popup/dialog scenes live in their own native windows, so without this the flags would only ever affect the window they were toggled in.voidEnd of one native poll: dispatch the accumulated batch now.static voidinstallTooltipStyle(Function<ControlSize, TooltipStyle> supplier) Installs the themed tooltip appearance (the components layer calls this once).booleanWhether each frame's damage region is highlighted for inspection.booleanWhether only damaged regions are repainted rather than the whole window.booleanisPaused()booleanWhether the bound window holds OS input focus, as reported by the last focus event (falseuntil the first one arrives; windows shown without stealing focus, like popups, start unfocused).voidkeyEvent(int key, boolean pressed, boolean repeat, int modifiers) voidlayoutPass(float newWidth, float newHeight) Runs measure/layout when dirty or resized (public for headless tests/embedding).locale()metrics()intvoidmouseButton(int button, boolean pressed, int modifiers, float x, float y) Button press/release at the given cursor position.voidmouseDelta(float dx, float dy) Relative pointer motion while the window is inPointerMode.RELATIVE: unbounded deltas in logical points (raw/unaccelerated where the platform supports it), replacingWindowInput.mouseMoved(float, float)for the duration of the capture.voidmouseMoved(float x, float y) observePresses(Consumer<Widget> observer) Observes every mouse press with its hit-tested target (after normal dispatch), the hook for "click outside to dismiss" overlays and popups that must react to presses landing on non-focusable widgets.observeWindowBlur(Runnable observer) Observes the bound window losing OS focus, the cue that dismisses transient popups (dropdowns, menus) anchored to this window: a press in another window or application never reachesobservePresses(java.util.function.Consumer<limn.scene.Widget>).observeWindowClosed(Runnable observer) Runsobserverwhen the bound window is destroyed, the hook for completion work driven by tickers/frames, which stop forever at that point (an abandoned fade would otherwise leak an uncompleted future).voidpointerEntered(boolean entered) Cursor entered (true) or left the window's content area.voidpreeditChanged(String text, int[] blockSizes, int focusedBlock, int caret) In-progress IME composition ("preedit") for the focused text input: the still-composing text plus its styled blocks and caret, shown inline but not yet committed (the commit arrives later viaWindowInput.charTyped(int)).voidpushOverlay(Widget overlay) Pushesoverlayas a full-scene modal layer painted on top of everything: it captures all input and confines focus (hit-testing and Tab traversal ignore the content and lower overlays).voidrelayout()Re-measures and repaints this whole scene, root and overlays.voidremoveOverlay(Widget overlay) Removes a modal overlay and restores focus to whatever it took it from.voidrenderFrame(Canvas canvas) Full frame: animation tick → layout (if dirty) → paint (root then overlays) → modal scrim.voidrenderFrame(Canvas canvas, boolean rePresent) voidrenderFrame(Canvas canvas, boolean rePresent, float gpuFrameMs) voidrequestFocus(Widget widget) Moves keyboard focus towidget, or clears it whennull.voidSchedules a frame and marks the whole scene damaged.root()The root widget of this scene's tree.voidscrolled(float deltaX, float deltaY, float x, float y) Wheel/trackpad scroll; deltas in native notches (positive = up/left).voidsetBackground(Color color) The colour cleared behind the tree each frame.voidsetClipboard(Clipboard newClipboard) Overrides the clipboard (tests inject mocks).voidsetControlSize(ControlSize size) Sets this window's default step, the per-window root of the inheritance chain (a compact tool window beside a regular main window).voidsetDamageDebug(boolean enabled) Highlights each frame's fresh damage region (magenta wash + border), the tool for spotting under-invalidation bugs and seeing what partial rendering repaints.voidsetFrontPainter(Consumer<Canvas> painter) Draws over the finished frame: after the root, after every overlay, after the modal scrim and after the tooltip.voidsetLayoutDirection(LayoutDirection direction) Sets this window's default layout direction, the per-window root of the inheritance chain: the one line an application whose interface reads right to left writes.voidSets this window's default locale, the per-window root of the inheritance chain: what makes two windows in two languages expressible, which one process-wide locale could not say (ADR 006 §4, delivered by ADR 035).voidsetPartialRendering(boolean enabled) Enables partial rendering: frames repaint only the damaged region (plus the previous frame's, for double buffering) instead of the whole window.voidsetPaused(boolean newPaused) Freezes or resumes scene time, keeping thetimeScale()the app chose (so a paused slow-motion scene resumes in slow motion).voidsetSlowHandlerBudgetMillis(long millis) Budget above which an event handler is reported (default 8 ms).voidsetTextRuler(TextRuler ruler) Overrides the text measurer (tests use deterministic fake metrics).voidsetTimeScale(double scale) Speed of scene time, the clock everyaddTicker(limn.scene.Scene.Ticker)ticker integrates.intdoublewindow()voidThe bound window was destroyed.voidwindowFocusChanged(boolean focused) The window gained or lost OS input focus.voidwindowResized(float logicalWidth, float logicalHeight) Window content resized (logical points).
-
Field Details
-
MAX_TICK_SECONDS
public static final double MAX_TICK_SECONDSLongest step any ticker is handed in one frame, whatever the wall clock says.The frame interval is not bounded by anything: a GC pause, a window drag, a breakpoint, a laptop lid or a monitor switch can leave seconds between two frames. Unclamped, that interval reaches every ticker at once: a 0.14 s focus fade jumps straight to its end (merely ugly), and anything integrating it (a game step, a physics tick, a sweep that wraps modulo 1) skips through states it was supposed to pass through, which is how tunnelling and NaN-poisoned simulations start. 0.25 s is the conventional guard: long enough that no healthy frame is ever clipped (it is 15 frames at 60 Hz), short enough that the worst a stall can do is one slow-motion step.
Clamping loses time on purpose: the toolkit does not bank the missing seconds and replay them. A stalled second is a second the app did not run, not a debt.
- See Also:
-
-
Constructor Details
-
Scene
A scene overroot. Subscribes to font, size-step and language changes. -
Scene
Injectable clock (slow-handler instrumentation and animation ticks), public so component tests can drive animations deterministically.
-
-
Method Details
-
metrics
- Returns:
- live per-frame performance samples (FPS, frame time, event time)
-
setTextRuler
Overrides the text measurer (tests use deterministic fake metrics). -
textRuler
- Returns:
- the scene's text measurer (defaults to the backend-installed one)
-
bind
Wires this scene into a window: input, frame rendering, invalidation, clipboard. -
relayout
public void relayout()Re-measures and repaints this whole scene, root and overlays. Call it after changing an input to measurement that is not a widget property: the activeTheme, the UI font family, this scene's default control size.root().markNeedsLayout()is not enough: it dirties only the path to the root, so children return cached sizes under unchanged constraints, and it misses overlays entirely (open dialogs, open menus). The global path must therefore go throughScene, never through app code. -
window
- Returns:
- the bound window, or
nullwhen headless
-
setClipboard
Overrides the clipboard (tests inject mocks). -
clipboard
- Returns:
- the clipboard (window's, injected, or a local no-op; never null)
-
root
The root widget of this scene's tree. -
controlSize
- Returns:
- this scene's default step, or
nullto fall through to a hosted root's host link and then toControlSize.processDefault(). Nullable by design: a popup's own scene declares nothing, which is what lets it inherit from the widget that opened it.
-
setControlSize
Sets this window's default step, the per-window root of the inheritance chain (a compact tool window beside a regular main window). Widgets that declare their own step, and their subtrees, are unaffected.nullrestores fall-through. UI thread only. -
layoutDirection
- Returns:
- this scene's default layout direction, or
nullto fall through to a hosted root's host link and then toLayoutDirection.processDefault(). Nullable by design, for the reasoncontrolSize()is: a popup's own scene declares nothing, which is what lets it inherit from the widget that opened it.
-
setLayoutDirection
Sets this window's default layout direction, the per-window root of the inheritance chain: the one line an application whose interface reads right to left writes. Widgets that declare their own direction, and their subtrees, are unaffected — which is what lets a left-to-right code editor, log pane or URL bar sit inside it.nullrestores fall-through. UI thread only. -
locale
- Returns:
- this scene's default locale, or
nullto fall through to a hosted root's host link and then toI18n.processLocale(). Nullable by design, for the reasoncontrolSize()is: a popup's own scene declares nothing, which is what lets it inherit from the widget that opened it.
-
setLocale
Sets this window's default locale, the per-window root of the inheritance chain: what makes two windows in two languages expressible, which one process-wide locale could not say (ADR 006 §4, delivered by ADR 035). Widgets that declare their own locale, and their subtrees, are unaffected.nullrestores fall-through. Retains the declared locale's bundle tables exactly asWidget.setLocale(java.util.Locale)does. UI thread only. -
pushOverlay
Pushesoverlayas a full-scene modal layer painted on top of everything: it captures all input and confines focus (hit-testing and Tab traversal ignore the content and lower overlays). This is how modal dialogs work: an in-scene overlay, no nested event loop. Focus moves to the overlay's first focusable widget. -
removeOverlay
Removes a modal overlay and restores focus to whatever it took it from. -
setBackground
The colour cleared behind the tree each frame. A translucent one only shows through where the window itself is translucent. -
background
The colour cleared behind the tree each frame. Read it before replacing it to tell a deliberately translucent scene from one that simply took a theme's canvas: a caller swapping the palette under a built scene must leave the first alone. -
setSlowHandlerBudgetMillis
public void setSlowHandlerBudgetMillis(long millis) Budget above which an event handler is reported (default 8 ms). -
slowHandlerCount
public int slowHandlerCount()- Returns:
- handler-budget violations seen so far (debug/tests)
-
setPartialRendering
public void setPartialRendering(boolean enabled) Enables partial rendering: frames repaint only the damaged region (plus the previous frame's, for double buffering) instead of the whole window. A subtree that misses the pass region is skipped from the paint walk too (culledFromPaint), as one that misses the canvas clip is in every mode. Default off. -
isPartialRendering
public boolean isPartialRendering()Whether only damaged regions are repainted rather than the whole window. -
setDamageDebug
public void setDamageDebug(boolean enabled) Highlights each frame's fresh damage region (magenta wash + border), the tool for spotting under-invalidation bugs and seeing what partial rendering repaints. Works with partial rendering on or off (off shows what would be repainted). Default off. -
isDamageDebug
public boolean isDamageDebug()Whether each frame's damage region is highlighted for inspection. -
inheritRenderingFlags
Copies the rendering flags (partial rendering, damage debug) from the scene that spawned this one: popup/dialog scenes live in their own native windows, so without this the flags would only ever affect the window they were toggled in. (A transparent-background popup still falls back to full frames; the debug overlay works regardless.) -
requestRender
public void requestRender()Schedules a frame and marks the whole scene damaged. -
setFrontPainter
Draws over the finished frame: after the root, after every overlay, after the modal scrim and after the tooltip.nullremoves it.It is not in the tree and not in hit-testing. That is the whole difference from a widget, and it is what this exists for. A widget added last to the root paints under any overlay, so it disappears the moment a modal dialog opens; an overlay pushed on top paints correctly but becomes the topmost layer, and
hitAt(float, float)then routes every press to it alone, so a dialog underneath can never be driven. A front painter has neither problem because it takes no input at all.Meant for a recording or capture harness that has to draw something over the whole window: a mouse pointer into a filmed frame, a watermark, a debug readout. It is not a place to put user interface. Nothing here can be clicked, focused, or reached by a keyboard, and a control drawn from here would look live and be inert.
It draws on every repaint, including partial ones, and it is given the same canvas the frame used, so what it draws outside the damaged region will be clipped away. A caller that moves what it draws must therefore mark the scene damaged itself, which
requestRender()does; the capture harness renders whole frames and so does not have to.Painted before the damage-debug flashes, so those stay legible on top of it.
-
frontPainter
- Returns:
- the front painter, or
null.
-
damage
Marks a scene-space region as damaged and schedules a frame, for painting that extends beyond the invalidating widget's bounds (shadows, carets in overlays).Widget.invalidate()is the common path. -
observePresses
Observes every mouse press with its hit-tested target (after normal dispatch), the hook for "click outside to dismiss" overlays and popups that must react to presses landing on non-focusable widgets. Returns a handle that unregisters the observer. -
isWindowFocused
public boolean isWindowFocused()Whether the bound window holds OS input focus, as reported by the last focus event (falseuntil the first one arrives; windows shown without stealing focus, like popups, start unfocused). -
observeWindowBlur
Observes the bound window losing OS focus, the cue that dismisses transient popups (dropdowns, menus) anchored to this window: a press in another window or application never reachesobservePresses(java.util.function.Consumer<limn.scene.Widget>). Returns a handle that unregisters the observer. -
modifiers
public int modifiers()- Returns:
- the modifier keys held right now (see
Keysbits)
-
focusedWidget
The widget holding keyboard focus, ornullwhen nothing does. -
requestFocus
Moves keyboard focus towidget, or clears it whennull. Ignored for a widget that is not focusable, visible and enabled, and for one outside the topmost modal overlay. UI thread only. -
focusTraverse
public void focusTraverse(boolean backward) Moves focus to the next/previous focusable widget in layout (DFS) order. -
addTicker
Registers an animation ticker on scene time; frames keep coming while any is active. Safe to call from inside another ticker'stick(e.g. one animation kicking off another); such a ticker joins on the next frame.Its
dtis clamped toMAX_TICK_SECONDSand multiplied bytimeScale(), sosetPaused(boolean)freezes it. That is the right default for anything the app animates. UseaddRealTimeTicker(limn.scene.Scene.Ticker)for an animation whose completion frees a resource or settles state; freezing one of those hangs it. -
addRealTimeTicker
Registers a ticker on wall time:setTimeScale(double)andsetPaused(boolean)do not reach it. Itsdtis still clamped toMAX_TICK_SECONDS: that guard is about a stalled clock, not about intent.For animations that are not content: the toolkit's own window / dialog / menu fades, widget transitions, scrollbar fades, progress sweeps. Two reasons they belong here. The hard one: several of them own a lifecycle; the fade-out is what destroys the popup window, removes the overlay that is capturing input, or completes the dialog's future, so a pause that froze them would leave a half-closed dialog on screen and a future nobody ever completes. The soft one: shell feedback that stops responding is read as a hang, not as a pause; a paused app should still highlight the button under the pointer.
-
disposeLater
Schedules aGpuSurfacefor disposal at the next frame, when this scene's window/GL context is current. Widgets that own GPU resources (e.g. a 3D viewport) call this fromWidget.onDetached()instead of disposing inline, becausedispose()would run without a current context. -
disposeLater
LikedisposeLater(GpuSurface)for any GPU cleanup that must run with this scene's GL context current, e.g. a retained 3D scene'sScene3D.dispose(). Runs once, at the top of the next frame. -
setTimeScale
public void setTimeScale(double scale) Speed of scene time, the clock everyaddTicker(limn.scene.Scene.Ticker)ticker integrates. 1 is real time, 0.5 slow motion, 2 double speed, 0 frozen. Never negative: nothing in the toolkit's animation model runs backwards, and a negative dt would driveTransitionpast its start with no way back.What it does not touch. Wall time keeps running underneath: input, layout,
Ui.postDelayed(java.lang.Runnable, long)(so the caret keeps blinking), the frame metrics, the tooltip and modal-scrim fades, and everyaddRealTimeTicker(limn.scene.Scene.Ticker)ticker, which is where the toolkit puts its own shell animation, so a paused app still opens dialogs, closes menus and fades windows normally. Scene time is for what the app animates, not for the chrome around it.- Parameters:
scale- a finite factor ≥ 0
-
timeScale
public double timeScale()- Returns:
- the current scene-time scale (see
setTimeScale(double))
-
setPaused
public void setPaused(boolean newPaused) Freezes or resumes scene time, keeping thetimeScale()the app chose (so a paused slow-motion scene resumes in slow motion). Equivalent in effect to a scale of 0.No time is banked while paused: the paused seconds are never replayed. A scene-time ticker resumes measuring from the last frame that actually ran, which is the resume itself when the pause stopped the frame pump (so
dt == 0), or the last frame a real-time ticker kept alive. Either way what it receives is the time since that frame, not the length of the pause. A ticker registered while paused still gets its owndt == 0first frame, whenever it finally runs. -
isPaused
public boolean isPaused()- Returns:
- whether scene time is frozen (see
setPaused(boolean))
-
mouseMoved
public void mouseMoved(float x, float y) - Specified by:
mouseMovedin interfaceWindowInput
-
mouseDelta
public void mouseDelta(float dx, float dy) Description copied from interface:WindowInputRelative pointer motion while the window is inPointerMode.RELATIVE: unbounded deltas in logical points (raw/unaccelerated where the platform supports it), replacingWindowInput.mouseMoved(float, float)for the duration of the capture. The receiver may coalesce by summing. Default: ignored.- Specified by:
mouseDeltain interfaceWindowInput
-
mouseButton
public void mouseButton(int button, boolean pressed, int modifiers, float x, float y) Description copied from interface:WindowInputButton press/release at the given cursor position.- Specified by:
mouseButtonin interfaceWindowInput
-
scrolled
public void scrolled(float deltaX, float deltaY, float x, float y) Description copied from interface:WindowInputWheel/trackpad scroll; deltas in native notches (positive = up/left).- Specified by:
scrolledin interfaceWindowInput
-
keyEvent
public void keyEvent(int key, boolean pressed, boolean repeat, int modifiers) - Specified by:
keyEventin interfaceWindowInput
-
charTyped
public void charTyped(int codepoint) Description copied from interface:WindowInputCommitted text input, one code point (IME/layout-aware, after key events).- Specified by:
charTypedin interfaceWindowInput
-
preeditChanged
Description copied from interface:WindowInputIn-progress IME composition ("preedit") for the focused text input: the still-composing text plus its styled blocks and caret, shown inline but not yet committed (the commit arrives later viaWindowInput.charTyped(int)). An emptytextclears the composition. Default: ignored.- Specified by:
preeditChangedin interfaceWindowInput- Parameters:
text- the composing text (""clears it)blockSizes- code-point length of each styled block (tilestext)focusedBlock- index of the block being converted, or-1caret- caret position withintext, in code points
-
pointerEntered
public void pointerEntered(boolean entered) Description copied from interface:WindowInputCursor entered (true) or left the window's content area.- Specified by:
pointerEnteredin interfaceWindowInput
-
filesDropped
Description copied from interface:WindowInputFiles dragged from the OS and dropped on the window. The drop lands at the current pointer position (the platform moves the cursor onto the window before dropping). Default: ignored.- Specified by:
filesDroppedin interfaceWindowInput
-
windowFocusChanged
public void windowFocusChanged(boolean focused) Description copied from interface:WindowInputThe window gained or lost OS input focus. On loss the scene cancels any in-flight press/drag/hover; the matching RELEASE will never arrive (it happens in another app), so it is synthesized. Default: ignored.- Specified by:
windowFocusChangedin interfaceWindowInput
-
windowResized
public void windowResized(float logicalWidth, float logicalHeight) Description copied from interface:WindowInputWindow content resized (logical points). May be coalesced by the receiver.- Specified by:
windowResizedin interfaceWindowInput
-
inputBatchEnded
public void inputBatchEnded()Description copied from interface:WindowInputEnd of one native poll: dispatch the accumulated batch now.- Specified by:
inputBatchEndedin interfaceWindowInput
-
addShortcutHandler
Registers a scene-wide keyboard handler for chords the focused widget did not want: menu accelerators, and anything else that must work without being focused.The ordering is the contract. A key event is offered, in this order, to:
- the focused widget, bubbling to the root, so
Ctrl+Cinside a text field with a selection is the field's copy and never a menu's; - every handler registered here, oldest first, until one returns
true; - Tab traversal, if the key is Tab and still nobody took it.
Returning
trueconsumes the event and stops both the remaining handlers and the Tab fallback. Handlers see presses, auto-repeats and releases alike: a shortcut whose trigger is a modifier let go of (a bare Alt reaching for the menu bar) has nothing else to key on.Handlers are not consulted while a modal overlay owns the scene: an open dialog or an in-scene menu has the keyboard, and a shortcut belonging to what it covers must not fire behind it.
Registering or unregistering from inside a handler is legal and takes effect on the next event, never on the one being dispatched. Call the returned
Runnableto unregister; it is idempotent. UI thread only. - the focused widget, bubbling to the root, so
-
renderFrame
Full frame: animation tick → layout (if dirty) → paint (root then overlays) → modal scrim. -
renderFrame
- Parameters:
rePresent-truewhen re-drawing the same already-settled frame only to converge the double buffers (the backend's double-present anti-flicker): identical pixels, but excluded frommetrics()so FPS/frame-time count only frames rendered for content.
-
renderFrame
-
installTooltipStyle
Installs the themed tooltip appearance (the components layer calls this once). -
fadeWindowIn
public void fadeWindowIn(double seconds) Snaps the window opacity to0immediately, then eases it to1overseconds, the flash-free "appear" sequence. Call it right beforeNativeWindow.show()so the window is already transparent when it first maps. -
fadeWindowOut
Eases the window opacity to0, then runsonGone(typically a close). -
fadeWindow
Animates this scene's native-window opacity towardtarget([0..1]) overseconds, runningonArriveonce it settles. A newer call supersedes an in-flight fade without stacking tickers. Zero duration, an already-current value, or a headless scene (no window) jumps straight totargetand runsonArrivenow. UI thread only. -
windowClosed
public void windowClosed()The bound window was destroyed. A window-fade that had not yet reached its target will never tick again (a closed window renders no frames), so run its pending arrival callback now; otherwise a dialog fading out when its owner closes would leak an uncompleted result future. Runs at most once: bumping the generation kills the abandoned ticker, and clearing the field makes a later normal completion a no-op (and the callbacks it drives, likeCompletableFuture.complete, are themselves idempotent).Focus is cleared first: widgets gate self-rescheduling work (e.g. the text-caret blink chain) on
isFocused(), and a widget that stays "focused" in a dead scene would re-arm on the global UI queue forever, waking the event loop, forcing frames on every live window, and pinning this scene against GC. Clearing throughsetFocus(limn.scene.Widget)also runs the normal focus-lost path (composition dropped, IME state torn down).- Specified by:
windowClosedin interfaceWindowInput
-
observeWindowClosed
Runsobserverwhen the bound window is destroyed, the hook for completion work driven by tickers/frames, which stop forever at that point (an abandoned fade would otherwise leak an uncompleted future). Observers run once and are dropped; the returned handle unregisters earlier (call it when the normal path completed first). -
layoutPass
public void layoutPass(float newWidth, float newHeight) Runs measure/layout when dirty or resized (public for headless tests/embedding).
-