Class Dialog

java.lang.Object
limn.components.Dialog

public final class Dialog extends Object
A modal (or non-modal) dialog that resolves a CompletionStage with the chosen button's result: never a nested event loop, so show* returns immediately and the stage completes (on the UI thread) later.

Two display modes (setDisplayMode(limn.components.DisplayMode)):

  • DisplayMode.NATIVE_WINDOW (default): a separate floating native window centered over the owner, with a WindowStyle and optional always-on-top. Modality is enforced by the backend (locked windows dim and ignore input).
  • DisplayMode.IN_SCENE: an internal overlay drawn inside the owner window: a scrim that dims/blocks the content behind plus a centered card that fades in. No extra OS window; modality is scoped to that scene.

Three modality scopes (native window mode):

  • show(Scene) is window-modal: locks only the owner window (and its owned popups); other windows stay interactive.
  • showToolkitModal(Scene) is toolkit-modal: locks every window in the application.
  • showNonModal(Scene) is non-modal: locks nothing; a floating panel alongside a fully interactive owner.
In IN_SCENE mode the overlay always captures its own scene's input; the owner stays interactive (it hosts the overlay) but its sibling windows are locked like a native modal: show locks the owner's owned popups, showToolkitModal locks every window. showNonModal is unsupported in-scene (use a native window for a non-modal dialog).

Stacking

Dialogs stack in either mode and in any combination: the newest is the one the user answers, everything under it is frozen until it closes, and answering the top one hands control back to the one below.

One exception, and it changes the display mode: an IN_SCENE dialog raised over a window that a modal has already locked is presented as a native window instead, with a warning naming the dialog. An overlay is drawn inside its host window, so it can only come forward by bringing that window forward, which would hide the dialog already floating over it. keepInScene() declines the promotion.

Size

A dialog is a container of sized components rather than a sized component: it has no size knob of its own, it is a ControlSize scope that its content inherits. Its three gutters and its dialogMaxWidth measure cap all follow the resolved step.

Because every show* takes a Scene, the step is inherited from the owner scene's root, not from the control that opened the dialog, so a dialog raised from a SMALL toolbar inside a MEDIUM scene renders MEDIUM. Declare it with setControlSize(limn.scene.ControlSize), or pass the opening widget to show(Widget), to get anything else.

A dialog is also bounded, and never grows past what it can be seen in: a native window is capped at the work area of the display it opens on (the monitor less the OS chrome), and an in-scene overlay at the window hosting it, both inset by one gutter so a capped card reads as capped rather than as clipped. Content that does not fit scrolls, and the button row is deliberately outside the scrolling part, so the way out of a dialog can never be scrolled away. A dialog that fits is unaffected: it is exactly as tall as it asks to be, and has nothing to scroll.

A Dialog is single-use: it presents once and resolves once. Calling any show* method a second time (including after the dialog closed) throws IllegalStateException; build a new instance per presentation.


 new Dialog("Delete?", "This action cannot be undone.")
     .addButton("Cancel", "cancel")
     .addPrimaryButton("Delete", "delete")
     .show(scene)
     .thenAccept(result -> { if ("delete".equals(result)) ...; });
 
  • Constructor Details

    • Dialog

      public Dialog(String title, String message)
      A dialog with fixed text; see the I18nString constructor for localized text.
    • Dialog

      public Dialog(I18nString title, I18nString message)
      A dialog whose title and message follow the UI language; see I18nString.
  • Method Details

    • setContent

      public Dialog setContent(Widget widget)
      Puts a widget between the message and the buttons: a form, a preview, a picker. A dialog is the toolkit's one modal surface, and until now the only thing it could say was a sentence; anything that needs an answer richer than a button had to build its own overlay and re-solve focus, dismissal and the scrim.

      The widget inherits the card's ControlSize like everything else in it. Passing null removes the current one. UI thread only.

    • addButton

      public Dialog addButton(String text, String resultValue)
      Adds a secondary (outlined) button that resolves the dialog with result.
    • addButton

      public Dialog addButton(I18nString text, String resultValue)
      A secondary button whose caption follows the UI language.
    • addPrimaryButton

      public Dialog addPrimaryButton(String text, String resultValue)
      Adds a primary (filled) button that resolves the dialog with result.

      The first one added is also the card's default button: Return resolves the dialog with its result whenever the focused widget did not want the key itself, so Return in a text field answers the dialog, while Return on a focused button presses that button and Return in a TextArea still inserts a newline.

    • addPrimaryButton

      public Dialog addPrimaryButton(I18nString text, String resultValue)
      A primary button whose caption follows the UI language.
    • setCancelResult

      public Dialog setCancelResult(String value)
      Sets the result delivered on ESC / scrim dismiss (default null).
    • setControlSize

      public Dialog setControlSize(ControlSize size)
      Declares the size step for the whole card, overriding the inherited one: the only size knob a dialog has, and a forwarder rather than an override because a Dialog is not a Widget: it lands on the card's root, which is what every control inside the dialog then inherits, in both display modes.

      null restores inheritance from the owner scene. UI thread only.

    • controlSize

      public ControlSize controlSize()
      Returns:
      the step the card currently resolves to (declared, inherited or default)
    • setDisplayMode

      public Dialog setDisplayMode(DisplayMode mode)
      Selects native-window vs. internal (in-scene) presentation (default DisplayMode.NATIVE_WINDOW).

      A preference, not a guarantee. An IN_SCENE dialog raised while a modal is already open over its window is presented as a native one instead; an overlay can only be brought to the front by raising its host, which would hide the dialog already there. displayMode() answers what actually happened; keepInScene() insists.

    • displayMode

      public DisplayMode displayMode()
      How this dialog is actually presented: the requested mode until show(limn.scene.Scene) decides otherwise, and the effective one afterwards.

      Worth asking after show by anything that styles or positions the card on the assumption that it is an overlay: a promoted dialog is a real window, so setStyle starts meaning something and an in-scene drag affordance stops. Until this existed the promotion was announced only in a log line, which no application reads.

    • keepInScene

      public Dialog keepInScene()
      Insists on DisplayMode.IN_SCENE even when a modal window is already open over the host, the case this dialog would otherwise be promoted out of, silently and with a warning.

      Say this when you have looked at the stacking and are content with it: the overlay will be answerable, but bringing its host window forward hides any dialog floating over it, so the earlier one appears to vanish until this one closes. The honest reason to accept that is not needing an extra OS window at all: an embedded or kiosk surface, or a host that cannot create one.

      No boolean: promotion is the default and this is an assertion, not a property. keepInScene(false) would be a way to write "promote me, probably", which is just the default spelled less clearly.

    • setAlwaysOnTop

      public Dialog setAlwaysOnTop(boolean value)
      Whether the native dialog window floats above other windows (default true). Only applies to DisplayMode.NATIVE_WINDOW.
    • setDismissOnScrim

      public Dialog setDismissOnScrim(boolean value)
      Whether clicking the scrim dismisses the dialog (default false). Only applies to DisplayMode.IN_SCENE. When false the dialog is truly modal: a scrim click is ignored and answered with the same alert feedback (beep) a native modal gives; when true it closes with the cancel result (a dismissable overlay).
    • setStyle

      public Dialog setStyle(WindowStyle newStyle)
      Sets the native window framing (default WindowStyle.UNDECORATED_TRANSLUCENT): DECORATED uses the OS title bar/border (opaque); UNDECORATED_OPAQUE is a borderless solid card; the default is a borderless glassy panel with rounded, see-through corners. Ignored in DisplayMode.IN_SCENE (the internal card is always the glassy panel).
    • dismiss

      public void dismiss(String result)
      Programmatically closes the dialog, resolving with result. UI thread only.
    • result

      public CompletionStage<String> result()
      Returns:
      the stage that completes when the dialog closes (same as show(limn.scene.Scene))
    • fadeLevel

      public float fadeLevel()
      Returns:
      the current fade level in [0..1] (tests/screenshots)
    • modalWindow

      public NativeWindow modalWindow()
      Returns:
      the modal's native window while open (screenshots/tests), else null
    • show

      public CompletionStage<String> show(Scene owner)
      Shows the dialog window-modal over owner (locks only its window), or, in DisplayMode.IN_SCENE, a scene-modal internal overlay.
      Throws:
      IllegalStateException - if this dialog was already shown (dialogs are single-use)
    • showToolkitModal

      public CompletionStage<String> showToolkitModal(Scene owner)
      Shows the dialog toolkit-modal over owner (locks every window). In DisplayMode.IN_SCENE an overlay cannot lock other native windows, so it presents the same scene-modal internal overlay as show(limn.scene.Scene).
      Throws:
      IllegalStateException - if this dialog was already shown (dialogs are single-use)
    • showNonModal

      public CompletionStage<String> showNonModal(Scene owner)
      Shows the dialog non-modal over owner: a floating window that locks nothing, so the owner (and every other window) stays fully interactive. Only valid in DisplayMode.NATIVE_WINDOW; an in-scene overlay always captures its scene's input.
      Throws:
      IllegalStateException - if the display mode is DisplayMode.IN_SCENE, or if this dialog was already shown (dialogs are single-use)
    • show

      public CompletionStage<String> show(Widget owner)
      show(Scene) over owner's scene, but inheriting the size step from owner itself rather than from the scene root, the overload to reach for when a SMALL toolbar (or a LARGE settings pane) raises the dialog and the card should match the surface it came from.
      Throws:
      IllegalStateException - if owner is not in a scene, or the dialog was already shown
    • showToolkitModal

      public CompletionStage<String> showToolkitModal(Widget owner)
      showToolkitModal(Scene), inheriting the size step from owner.
    • showNonModal

      public CompletionStage<String> showNonModal(Widget owner)
      showNonModal(Scene), inheriting the size step from owner.