docs

Windows, Monitors, and DPI

Create and position desktop windows, inspect connected monitors, and handle window events.

On this page

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.

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:

C++
///
/// 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.

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.
WindowFlags::CustomChrome Lets the page render the title bar and caption controls. See 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:

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.

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:

C++
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.

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:

C++
///
/// 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.

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 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:

C++
///
/// 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 and 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.

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:

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_;
};
#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);
}

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().