A `Window` is a native OS window that displays web content across panels. You can create and position windows across connected monitors and handle window events— the library manages display scaling automatically.

To create your first window step by step, see [Writing Your First App](/docs/2.0/writing-your-first-app).

## Creating Windows

You can create native desktop windows and configure how they appear and integrate with the OS.

### Creating a Window

Call `Window::Create()` to create a native OS window:

```cpp
///
/// A resizable 1024 by 768 window, scaled for the main monitor.
///
RefPtr<Window> window = Window::Create(App::instance()->main_monitor(),
    1024, 768, false,
    WindowFlags::Titled | WindowFlags::Resizable | WindowFlags::Maximizable);
```

The monitor parameter sets the window's initial display scale— it doesn't place a windowed window on that display. Window dimensions use logical pixels, and you can only set `fullscreen` when creating the window. The window appears immediately unless you pass `WindowFlags::Hidden`.

To fill the window with panels, see [Laying Out Panels](/docs/2.0/laying-out-panels).

#### Choosing Window Flags

Combine window flags with the bitwise OR operator (`|`) to configure window appearance and behavior:

| Flag | Description |
|---|---|
| `WindowFlags::Borderless` | Removes the system frame and title bar. |
| `WindowFlags::Titled` | Displays the system title bar. |
| `WindowFlags::Resizable` | Lets the user resize the window by dragging its edges. |
| `WindowFlags::Maximizable` | Requests minimize and maximize buttons (behavior varies across platforms). |
| `WindowFlags::Hidden` | Keeps the window hidden until you call `Window::Show()`. |
| `WindowFlags::Transparent` | Enables per-pixel transparency. See [Native Window Styling](/docs/2.0/native-window-styling). |
| `WindowFlags::CustomChrome` | Lets the page render the title bar and caption controls. See [Native Window Styling](/docs/2.0/native-window-styling). |

### Setting the Window Title

Call `Window::SetTitle()` to update the window's title string. Custom-chrome windows don't display a system title bar— the OS still displays the title in taskbars and window switchers.

### Setting the Window Icon

Call `Window::SetIcon()` to set the icon displayed in the window title bar and taskbar. The method accepts a 32-bit BGRA `Bitmap` with straight (unpremultiplied) alpha— passing `nullptr` restores the default icon (`Settings::app_icon` if configured, or the platform's default icon).

### Native Window Handle

Call `Window::native_handle()` to retrieve the underlying OS window handle when integrating with platform-specific native code. The returned pointer type depends on the OS.

## Pixels and Monitors

The library manages display scaling across monitors automatically and lets you query connected screens.

### Logical and Device Pixels

Sizes and coordinates across the API use logical pixels that remain independent of the display scale. Accessors with `device_` in their name report physical device pixels instead.

Call `Window::scale()` to retrieve the scale factor relating the two coordinate spaces:

```text
device_pixels = round(logical_pixels * scale)
```

Call `Window::LogicalToDevice()` or `Window::DeviceToLogical()` to convert values between logical and device pixels.

#### Handling Scale Changes

Each panel's `View` renders at the window's scale automatically. When a window moves to a monitor with a different display scale, the window and its views re-render at the new scale— you don't need to handle the transition.

> 👍 Overriding Display Scale
>
> Set `Settings::device_scale_override` to lock a fixed scale for every window. This lets you test display scales that connected monitors do not support. For details, see [App Lifecycle and Settings](/docs/2.0/app-lifecycle-and-settings).

### Inspecting Monitors

A `Monitor` represents a connected display.

You can inspect a monitor's dimensions in logical and physical device pixels, read its display scale with `Monitor::scale()`, and query its nominal refresh rate in Hz with `Monitor::refresh_rate()`.

#### Listing Monitors

Call `App::main_monitor()` to retrieve the primary display— this call never returns null.

To iterate through all connected displays, call `App::monitor_count()` alongside `App::monitor()`. Index `0` returns the main monitor, while an out-of-range index returns null.

Passing a monitor to `Window::Create()` supplies the window's initial display scale— the call does not position the window on that display.

#### Tracking Monitor Lifetimes

The `App` instance owns every `Monitor`— their pointers remain valid for the life of the app.

When a display disconnects, the app removes it from the enumerated monitor list. Any `Monitor` pointer you already hold remains safe to dereference, returning fallback values for its properties.

## Window Geometry

You can place windows across your displays and read their current size.

### Positioning a Window

Call `Window::MoveTo()` to place a window using global screen coordinates— a single logical space spanning all connected monitors. Call `Window::x()` and `Window::y()` to read back the current position.

Call `Window::MoveToCenter()` to center the window on the screen.

### Reading the Window's Size

Call `Window::width()` and `Window::height()` to get the window's dimensions in logical pixels. Call `Window::device_width()` and `Window::device_height()` to read physical device pixels.

Panels follow the window's size automatically. You only need to handle `OnResize()` (covered in the window events section below) if you maintain native state that tracks the window's dimensions.

## Visibility and State

You can control when windows appear on screen and manage their state throughout their lifecycle.

### Showing and Hiding a Window

Call `Window::Show()` to display a hidden window, or `Window::Hide()` to hide it. Call `Window::is_visible()` to check whether the window is currently visible.

Hiding a window also hides every popup it owns.

#### Showing When Ready

Call `Window::ShowWhenReady()` on a hidden window to avoid displaying a blank or partially loaded first frame:

```cpp
RefPtr<Window> window = Window::Create(app->main_monitor(), 1024, 768,
    false, WindowFlags::Titled | WindowFlags::Resizable |
        WindowFlags::Hidden);
window->AddPanel()->view()->LoadURL("file:///app.html");

// Appears once the page settles (or after 0.5 seconds at most).
window->ShowWhenReady();
```

The window appears once every page in a visible panel has settled. For details on how pages settle, see [Handling View Events](/docs/2.0/handling-view-events).

You can pass an optional timeout in seconds— if the pages take longer to settle, the window displays anyway once the timeout elapses.

##### Responding When Shown

`WindowListener::OnShow()` fires whenever the window appears, including when a `Window::ShowWhenReady()` wait ends:

```cpp
///
/// Start the live updates once the window is on screen.
///
void OnShow(Window* window) override {
  StartLiveUpdates();
}
```

> 🚧 Continuous Layout Updates
>
> Pages that modify their layout every frame (such as streaming live data or updating the DOM continuously) never settle— `Window::ShowWhenReady()` waits for the full timeout before displaying the window. Start those updates in `OnShow()` instead.

### Closing a Window

Call `Window::Close()` to close a window.

Closing a window fires `WindowListener::OnClose()`, closes every popup the window owns, and stops its panels and layout handles from working— calling `Window::Close()` again does nothing.

When the last window closes, call `App::Quit()` from `OnClose()` to end the main loop. For details on managing the main loop, see [App Lifecycle and Settings](/docs/2.0/app-lifecycle-and-settings).

### Managing Window State

Call `Window::Maximize()` to maximize the window, `Window::Minimize()` to minimize it, or `Window::Restore()` to return it to normal size. You can inspect the current state with `Window::is_maximized()` and `Window::is_minimized()` (or check `Window::is_fullscreen()` to see if it was created fullscreen).

## Window Events

You can listen for window changes and user input, and manage the mouse cursor.

### Handling Window Events

Call `Window::set_listener()` to receive callbacks for window events.

The window does not take ownership of the listener— you must keep the instance alive for as long as the window receives events.

Every callback receives a pointer to the triggering `Window`— you can use a single listener to manage multiple windows.

The interface covers window lifecycle events and user input. For the full list of methods, see the [WindowListener](/api/cpp/2_0_0/classultralight_1_1_window_listener.html) reference.

| Callback | Description |
|---|---|
| `OnClose()` | Fires when the window closes. |
| `OnResize()` | Fires when the window resizes, passing the new dimensions in logical pixels. |
| `OnWindowStateChanged()` | Fires when the window transitions between normal, minimized, and maximized states. |
| `OnActivationChanged()` | Fires when the window becomes active or inactive. |
| `OnShow()` | Fires when a hidden window appears. |
| `OnHide()` | Fires when a visible window is hidden. |
| `OnKeyEvent()` | Intercepts keyboard input before any panel receives it. |
| `OnMouseEvent()` | Intercepts mouse input before any panel receives it. |
| `OnScrollEvent()` | Intercepts scroll input before any panel receives it. |

#### Intercepting Input Events

Input callbacks run before any panel receives the event. Return `false` to consume the event and prevent panels from seeing it, or `true` to let it propagate:

```cpp
///
/// Open the help window on F1 before any page sees the key.
///
bool OnKeyEvent(Window* window, const KeyEvent& evt) override {
  if (evt.type == KeyEvent::kType_RawKeyDown &&
      evt.virtual_key_code == KeyCodes::GK_F1) {
    ShowHelp();
    return false;
  }
  return true;
}
```

This is useful for application-wide keyboard shortcuts. For more on event handling, see [Keyboard Input](/docs/2.0/keyboard-input) and [Mouse and Scroll Input](/docs/2.0/mouse-and-scroll-input).

#### Responding to State Changes

`OnWindowStateChanged()` fires whenever the window transitions between normal, minimized, and maximized states— whether triggered by API calls, title bar buttons, keyboard shortcuts, or OS window-snapping gestures. The callback fires after `OnResize()`, ensuring the window's updated dimensions are already set.

#### Tracking Window Activation

`OnActivationChanged()` fires whenever the window becomes or stops being the active window— whether the user switches applications or moves between windows in the same app.

Opening a popup owned by the window doesn't make the window inactive— the window keeps its active appearance while menus are open.

Windows that draw custom chrome can use state and activation events to update caption buttons and restyle inactive frames. For details, see [Custom Window Chrome](/docs/2.0/custom-window-chrome).

### Setting Cursors

The library updates the mouse cursor over web content automatically, showing the link hand over links, the I-beam over text, and shapes declared by CSS `cursor` rules.

Call `Window::SetCursor()` only when managing areas you draw yourself. Any cursor set manually persists until the pointer next moves across a web page.

## Managing Multiple Windows

To prevent visual repositioning when opening multiple windows, create each window with `WindowFlags::Hidden`, move it to its target coordinates, and then call `Window::Show()`. A single `WindowListener` can manage every window.

The C equivalent is available in the tab below:

<!-- tabs:start -->
```cpp
class MyApp : public WindowListener {
 public:
  MyApp() {
    app_ = App::Create();
    editor_ = OpenWindow("Editor", "file:///editor.html", 40, 40);
    preview_ = OpenWindow("Preview", "file:///preview.html", 880, 40);
  }

  ///
  /// Create each window hidden, place it, then show it-- every window
  /// shares this listener.
  ///
  RefPtr<Window> OpenWindow(const char* title, const char* url, double x,
                            double y) {
    RefPtr<Window> window = Window::Create(app_->main_monitor(), 800, 600,
        false, WindowFlags::Titled | WindowFlags::Resizable |
            WindowFlags::Hidden);
    window->MoveTo(x, y);
    window->SetTitle(title);
    window->set_listener(this);
    window->AddPanel()->view()->LoadURL(url);
    window->Show();
    return window;
  }

  ///
  /// Quit when either window closes.
  ///
  void OnClose(Window* window) override { app_->Quit(); }

 private:
  RefPtr<App> app_;
  RefPtr<Window> editor_;
  RefPtr<Window> preview_;
};
```
```c
#include <AppCore/CAPI.h>

static ULApp app = NULL;
static ULWindow editor = NULL;
static ULWindow preview = NULL;

///
/// Quit when either window closes.
///
static void OnClose(void* user_data, ULWindow window) {
  ulAppQuit(app);
}

///
/// Create each window hidden, place it, then show it-- every window
/// shares the close callback.
///
static ULWindow OpenWindow(const char* title, const char* url_string,
                           double x, double y) {
  ULWindow window = ulCreateWindow(ulAppGetMainMonitor(app), 800, 600,
      false, kWindowFlags_Titled | kWindowFlags_Resizable |
          kWindowFlags_Hidden);
  ulWindowMoveTo(window, x, y);
  ulWindowSetTitle(window, title);
  ulWindowSetCloseCallback(window, OnClose, NULL, NULL);

  ULPanel panel = ulWindowAddPanel(window, NULL, NULL);
  ULView view = ulPanelGetView(panel);
  ULString url = ulCreateString(url_string);
  ulViewLoadURL(view, url);
  ulDestroyString(url);
  ulDestroyView(view);
  ulDestroyPanel(panel);

  ulWindowShow(window);
  return window;
}

void OpenBothWindows(void) {
  editor = OpenWindow("Editor", "file:///editor.html", 40, 40);
  preview = OpenWindow("Preview", "file:///preview.html", 880, 40);
}
```
<!-- tabs:end -->

## Platform Differences

Several window features and behaviors vary across desktop platforms:

| Behavior | Windows | macOS | Linux |
|---|---|---|---|
| Native handle type (`Window::native_handle()`) | `HWND` | `NSWindow*` | `GLFWwindow*` |
| Window icon (`Window::SetIcon()`) | Appears in the title bar and taskbar. | Ignored (the Dock icon comes from `Settings::app_icon` or the app bundle). | Sets the window icon. |
| `WindowFlags::Maximizable` | Displays minimize and maximize caption buttons. | Enables the minimize button (the zoom button follows `WindowFlags::Resizable`). | Handled by the window manager. |
| `Maximize()` on a non-resizable window | Resizes the window. | Fires `OnWindowStateChanged()` without resizing the window. | Handled by the window manager. |
| Fullscreen target display | Primary display. | Monitor passed to `Window::Create()`. | Monitor passed to `Window::Create()`. |
