You can display menus, dropdown lists, and context menus that extend outside the application window using popup windows. A popup is a small OS window owned by another window— it stays above its owner, leaves keyboard focus with the owner when clicked, and dismisses the way a native menu does.

For quick confirmations and alerts, native modal message boxes display standard OS dialogs that block until the user responds.

## Creating a Popup

Call `Window::CreatePopup()` with an owner window, position coordinates, and dimensions:

```cpp
#include <AppCore/Window.h>

using namespace ultralight;

class MyApp {
 public:
  void ShowContextMenu(double x, double y) {
    ///
    /// Show a context menu at (x, y) within the owner's content area.
    ///
    menu_ = Window::CreatePopup(window_, x, y, 220, 320);
    menu_->SetBackdrop(BackdropMaterial::Popup);

    ///
    /// The popup is see-through, so its page needs a transparent View (and
    /// a transparent page background).
    ///
    ViewConfig config;
    config.is_transparent = true;
    menu_->AddPanel({}, config)->view()->LoadURL("file:///menu.html");
  }

 private:
  RefPtr<Window> window_;
  RefPtr<Window> menu_;
};
```

Popups are borderless, transparent windows that stay above their owner window without appearing in the OS taskbar. When the owner window hides, the popup hides with it.

### Positioning Popups

Popup coordinates use logical pixels measured from the top-left of the owner window's content area— calls to `Window::MoveTo()`, `Window::x()`, and `Window::y()` use this same coordinate space.

When a panel fills the owner window, page coordinates match the owner's content area directly. Coordinates from `getBoundingClientRect()` pass straight into popup placement without conversion.

If a panel occupies only part of the window (such as below a toolbar), add the panel's offset to the page coordinates before positioning the popup.

### Styling Popups

Call `Window::SetBackdrop(BackdropMaterial::Popup)` to apply the host OS's native menu material. The material shows through transparent areas, so the popup's `View` requires `ViewConfig::is_transparent = true` and the page needs a transparent background.

A popup never becomes the active window, so its material displays the active look by default.

Popups display with rounded corners and a drop shadow by default. Call `Window::SetCornerStyle(CornerStyle::Square)` for sharp corners without a shadow (see [Native Window Styling](/docs/2.0/native-window-styling)).

## Showing and Reusing Popups

Popups appear immediately when created unless passed `WindowFlags::Hidden`.

For menus that open repeatedly, create the popup once with `WindowFlags::Hidden`. When opening the menu, call `Window::MoveTo()` to set its position and `Window::Show()` to display it.

Dismissing a popup hides the window without destroying it, preserving the instance for subsequent opens.

> 📘 Choosing between popups and panels
>
> Use a popup when UI content must extend outside the main window, such as a context menu or dropdown list. For content that stays within window boundaries (such as toast notifications or in-window dialogs), use a floating panel instead. See [Floating Panels](/docs/2.0/floating-panels).

## Dismissing Popups

Popups dismiss automatically the way native desktop menus do.

### Automatic Dismissal

By default, popups use `Dismiss::Auto`. The library hides the popup whenever the OS would dismiss a native menu:

- A press in the owner window outside the popup
- A press on the owner window's title bar or border
- The owner window moving, resizing, scrolling, or becoming inactive
- Pressing Esc while the owner window has focus

The dismissal check runs before the window receives input callbacks. Consuming an input event by returning `false` cannot keep a popup open.

### Keeping Popups Open

Call `Window::SetDismiss(Dismiss::Manual)` to disable automatic dismissal. The popup remains visible until you hide it— useful for tool palettes and flyout panels.

### Reacting to Dismissals

Implement `WindowListener::OnDismiss()` on the popup and assign it with `Window::set_listener()`.

The callback fires whenever the library hides the popup automatically, including when its owner window is hidden. It never fires when you hide the popup directly with `Window::Hide()`.

> 🚧 Menu button clicks
>
> The press that dismisses a popup still reaches the owner window's page. Clicking a menu button while its popup is already open dismisses the popup and immediately reopens it. You should track dismissal times and ignore clicks that arrive right after `WindowListener::OnDismiss()` (see [Native Look and Feel](/docs/2.0/native-look-and-feel)).

### Hiding Child Popups

Hiding any window automatically hides every popup it owns, so hiding a parent menu hides all of its submenus as well.

The library fires `WindowListener::OnDismiss()` for each child popup hidden this way, but never for the window you hid directly.

## Managing Keyboard Focus

Clicking inside a popup leaves keyboard focus with the owner window, and keystrokes continue to go to the owner. This matches standard desktop menu behavior.

Call `Window::SetAcceptsKeyInput(true)` for popups that include editable text fields. When enabled under `Dismiss::Auto`, pressing Esc while the popup has focus dismisses the popup.

## Lifetime and Window Limits

When an owner window closes, all of its popups close with it, firing `WindowListener::OnClose()` for each popup. You still hold valid `RefPtr<Window>` handles, but calling `Window::Show()` or `Window::MoveTo()` on an orphaned popup only logs a warning. Reuse popups on a window that outlives them.

Calls to `Window::Maximize()`, `Window::Minimize()`, `Window::Restore()`, `Window::SetWindowButtons()`, and `Window::MoveToCenter()` are ignored on popups with a warning.

## Showing Native Message Boxes

Call `ShowMessageBox()` from `<AppCore/Dialogs.h>` to display a modal OS dialog that blocks until the user responds:

```cpp
#include <AppCore/Dialogs.h>

using namespace ultralight;

///
/// Ask before discarding the user's edits.
///
bool ConfirmDiscard() {
  ButtonResult result = ShowMessageBox("Discard changes?",
                                       "Your unsaved edits will be lost.",
                                       DialogIcon::Question, ButtonType::YesNo);
  return result == ButtonResult::Yes;
}
```

The call returns a `ButtonResult` indicating which button the user selected (`ButtonResult::OK`, `ButtonResult::Cancel`, `ButtonResult::Yes`, or `ButtonResult::No`).

Pass optional icon and button configuration values to customize the dialog:

| Parameter | Default | Values |
|---|---|---|
| `icon` | `DialogIcon::Info` | `DialogIcon::Info`, `DialogIcon::Warning`, `DialogIcon::Error`, `DialogIcon::Question` |
| `buttons` | `ButtonType::OK` | `ButtonType::OK`, `ButtonType::OKCancel`, `ButtonType::YesNo` |

To build custom modal dialogs using HTML and CSS instead of native OS dialogs, create a floating panel configured with `FocusPolicy::Grab` (see [Floating Panels](/docs/2.0/floating-panels)).
