Skip to content
Subha Sundar Das edited this page Aug 7, 2026 · 4 revisions

A lightweight, thread-safe window manager for retui terminal applications. It adds floating windows, modal dialogs, Z-ordering, and focus management on top of retui's component model — without retui needing to know anything about windows directly.

Table of Contents


Concepts

What is a Window?

A Window is a floating UI surface — think of it as a resizable, movable box that sits on top of your app's main screen. It has a title, a position, a size, and content to render. A window can be:

  • Non-modal — floats above the background but doesn't block interaction with it (like a status panel or a docked palette).
  • Modal — blocks all interaction with everything behind it until it's closed (like a confirmation dialog or an edit form).

Windows start out hidden. Nothing shows up on screen until you call .Show().

The WindowManager

Every window is tracked by a single global WindowManager. You never create one yourself — the package manages a globalManager instance for you, and most operations are exposed as both:

  • Methods on *WindowManager (if you fetch it via window.GetManager()), and
  • Free package-level functions that operate on the global instance (e.g. window.CloseAll(), window.GetFocused()).

The manager is responsible for:

  • Keeping a registry of all windows (map[string]*Window)
  • Maintaining Z-order — which window is drawn on top
  • Maintaining a separate modal stack — which modal is currently active
  • Tracking focus — which window receives keyboard input
  • Triggering re-renders when window state changes

Modal vs. Non-Modal

  Non-Modal Modal
Blocks background interaction ❌ No ✅ Yes
Appears in modalStack ❌ No ✅ Yes
Escape has default "close" behavior ❌ No (you wire it yourself) ✅ Yes (built-in fallback)
Tab cycles between it and other modals N/A ✅ Yes, if multiple modals stacked
Typical use case Status bar, docked panel, tooltip Edit form, confirmation dialog, alert

Gotchas & Best Practices

  • Always return true/false deliberately from OnKeyPress. Returning true when you didn't actually handle the key will swallow it silently for everyone else; returning false when you did handle it can let the key "leak" into the background screen.
  • Close() is final. Once closed, a window is unregistered and cannot be reshown. If you need a reusable dialog, keep it around and use Hide() / Show() instead of Close() / NewWindow().
  • Only one modal blocks input at a time — the topmost one. Lower modals in the stack are inert until the ones above them are closed.
  • Escape has default behavior; Tab does not (much). Tab only auto-cycles focus among open modals — it does nothing special for non-modal windows or when nothing is open.
  • Don't forget SetScreenSize. If you never call it, Center() falls back to DefaultScreenWidth/DefaultScreenHeight (140×40), which may not match your actual terminal.
  • All state is protected by mutexes, so it's safe to call window/manager methods from goroutines (e.g. background workers closing a progress dialog when a task finishes). Rendering re-triggers are dispatched via go wm.triggerRender(), so they won't block the caller.

FAQ

Q: I pressed Escape and it closed my modal and my whole screen. What happened? A: Almost certainly your OnKeyPress handler for Escape did something (like calling Close()) but returned false instead of true, so the key kept propagating past the window layer into your background screen's own key handling. Return true whenever you act on a key.

Q: How do I make a window that can't be closed by Escape? A: Add an OnKeyPress handler that intercepts KeyEscape and returns true without calling Close():

w.OnKeyPress(func(key retui.Key) bool {
    if key.Code == retui.KeyEscape {
        return true // swallow it — do nothing
    }
    return false
})

Q: Can I have two modals open with neither blocking the other? A: No — modal means "blocks everything behind it," including other modals lower in the stack. If you need two independent floating panels, use non-modal windows instead.

Q: How do I know which window currently has keyboard focus? A: window.GetFocused() returns its ID; pass that to window.GetWindowByID(id) to get the *Window.

Clone this wiki locally