Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Overlays & Windows

Modals, floating windows, and popups are ordinary NanoUI actions that take a Var Bool for their open state.

Call overlays unconditionally

modal  :: Var Bool -> Text -> NanoUI () -> NanoUI ()
window :: Var Bool -> Text -> NanoUI () -> NanoUI ()
popup  :: Var Bool -> PopupConfig -> NanoUI () -> NanoUI ()

Caution

Always call an overlay on every frame. Wrapping it in when isShown (modal ...) shifts widget ids for all subsequent siblings when it opens or closes, which moves focus, carets, and per-row state. Overlays draw nothing while their Var Bool is False.

confirmDelete = do
  showConfirm <- useVar False

  whenM (button "Delete File...") (writeVar showConfirm True)

  modal showConfirm "Confirm Deletion" $ do
    label "Are you sure you want to permanently delete this file?"
    row $ do
      whenM (button "Cancel") (writeVar showConfirm False)
      whenM (styled destructive (button "Delete")) $ do
        deleteFile
        writeVar showConfirm False

Escape, a click outside, and the close button write False to the Var, so the dialog closes in the same frame. A modal blocks interaction with the content behind it and is drawn above it.

Modals

modal renders a titled panel with a close button, centered in the window and limited to the window’s bounds. The body is not scrolled automatically; wrap it in scrollWith for long content, and give the panel a size with modalWith when the body needs to fill it:

settingsDialog = do
  open <- useVar False
  modalWith (fixedWH 600 400) open "Settings" $
    columnWith (fillW . fillH) settingsBody

modal' returns (Response, Maybe a) and reports a close request as a click. When the open flag is derived from other state, pass constVar and write the real flag from the response:

notice = do
  open <- useVar False
  isOpen <- readVar open
  (resp, _) <- modal' (constVar isOpen) "Important" $ do
    label "This dialog is shown while `open` is True."
    whenM (button "OK") (writeVar open False)
  when (respClicked resp) (writeVar open False)

For a modal without a title bar or close button, use modalPanel / modalPanel'; the panel surface and backdrop remain, and Escape or an outside click still writes False.

Floating windows

window draws a draggable, resizable in-app window with a scrolling body. Other windows and the page remain interactive outside its bounds:

inspector = do
  inspectorOpen <- useVar False
  window inspectorOpen "Inspector" $ do
    inspectorBody

window' returns the body result and a response. Use window for panels that coexist with the main content, modal when the user must answer first.

Popups

popup is a floating panel placed by a PopupConfig:

filterPopup = do
  filterOpen <- useVar False
  showFruit <- useVar True
  showVeg <- useVar True
  filterR <- button' "Filter"
  when (respClicked filterR) (modifyVar filterOpen not)

  popup filterOpen (defaultPopupConfig (AnchorRect (respRect filterR))) $ do
    checkbox "Fruit" showFruit
    checkbox "Vegetables" showVeg

A press on the anchor counts as inside the popup, so the button that toggles the popup does not immediately reopen it.

Popup placement:

  • AnchorRect (respRect r) anchors to a widget; AnchorPoint pos anchors to a coordinate.
  • cfgPlacement chooses PlacementAuto, PlacementBelow, PlacementAbove, PlacementAtCursor, and the other placements.
  • cfgOffset is the gap from the anchor.
  • cfgDismissable = False disables Escape/outside-click closing; act on the response from popup' instead.

An anchored popup positions against the previous frame’s respRect, so warm a frame before relying on it in tests. For long popup bodies, cap the height with popupWith (maxH ...) and scroll the body.

Tooltips

Tooltips are stateless and attach to any widget with a Response:

tooltips = do
  save <- button' "Save"
  tooltip save "Write the file to disk"

  more <- button' "More"
  tooltipConfigured defaultTooltipConfig
    {tooltipPlacement = PlacementAtCursor} more "Follows the pointer"

The default delay is half a second; TooltipConfig sets the delay and placement. withTooltip attaches a rich tooltip widget instead of text.

Rules

  • Modals stack. Escape closes the innermost first; side-by-side modals close in declaration order.
  • Allocate the open flag in the view that opens and closes the overlay, outside any child that may be skipped.
  • Read focus after the overlay call.
  • A scrollArea, panel, or card inside a dialog body draws a box in a box; strip its chrome (see Double borders).

Build a Notepad wires a modal to the window’s close request and builds a menu bar from popup'.