Skip to content

Widgets Overview

github-actions[bot] edited this page Oct 13, 2026 · 8 revisions

Widgets are Gondwana’s system for interactive, reusable interface elements built on top of DirectDrawing.

They are intended for interface elements rendered inside the game itself rather than platform-native UI. This includes things like:

  • buttons, check boxes, radio buttons, and text boxes
  • labels, lists, combo boxes, and progress indicators
  • panels and layout containers
  • dialogs and conversation boxes
  • menus
  • health bars and name tags
  • splash screens, notifications, popups, and other overlays

Widgets use the same Gondwana rendering pipeline as the rest of the game and can participate in either screen-space or world-space rendering.

Desktop video has an optional companion package: Gondwana.Video.Widgets. VideoWidget wraps the DirectVideo primitive with normal Widget input, focus, opacity/fades, lifecycle and composite movement. Set IsDragEnabled = true to opt into inherited dragging; its default is false. Pointer/keyboard events belong to game-specific behavior, with no default play/pause command. Show()/Hide() do not change playback. Both View and SceneLayer targets and URI/stream/GAF sources are supported. Neither Widgets nor Video depends on the other; the bridge references both and retains Video's native desktop LibVLC requirements.


Built on DirectDrawing

A widget uses DirectDrawing for its visual representation, but adds the lifecycle, composition, focus, and input behavior expected from a user-interface element.

Depending on how it is associated, a widget can appear as:

  • a SceneLayer-bound world-space element
  • a View-bound screen-space interface element

That means the same widget system can support both in-world interaction and traditional HUD, menu, dialog, and overlay interfaces.

As a general rule:

  • Use a View when the widget should remain screen-relative, such as menus, HUD text, settings panels, dialogs, and most conventional UI.
  • Use a SceneLayer when the widget should exist in world coordinates and move through the scene with that layer.
  • HealthBarWidget and NameTagWidget are world-space widgets designed to follow a Sprite.
  • ToastWidget and SplashScreen are view-level overlays.

WidgetBase

All widgets ultimately derive from WidgetBase, which provides the common widget lifecycle and interaction model.

This includes:

  • showing and hiding
  • activation and cancellation
  • focus participation
  • pointer hit testing
  • pointer enter and leave events
  • pointer down, up, and click events
  • keyboard input
  • optional drag behavior

Specialized widget base classes add behavior where it is useful.

For example, container widgets add ownership and lifecycle propagation for child widgets, while draggable widgets build on the shared pointer pipeline rather than implementing platform-specific mouse or touch handling.

Application code normally works with the concrete widget classes rather than deriving directly from WidgetBase.


WidgetInputRouter

Interactive widget input is coordinated by WidgetInputRouter.

It:

  • tracks widgets associated with a render surface
  • determines which widget is under the pointer
  • routes mouse or touch input to that widget
  • manages pointer capture during clicks and drags
  • manages keyboard focus
  • routes keyboard input to the focused widget, then offers unhandled keys to eligible menu bars and tab controls
  • provides a platform-agnostic input path

The platform hosts translate native input into Gondwana input events. Widgets themselves do not need to know whether they are running under WinForms, Avalonia, or Blazor.

The normal GameHostBase initialization path creates and starts the widget input router automatically, so most Gondwana games do not need to create one manually.

If you are implementing a custom host, you can create the router using whatever input pollers that host provides:

using Gondwana.Widgets;

using var widgetInputRouter = new WidgetInputRouter(
    host,
    keyboardEventPoller,
    mouseEventPoller,
    touchEventPoller);

widgetInputRouter.Start();

An input poller can be null when that input source is not available.

Mouse clicks and touch taps on gameplay

Subscribe to WidgetInputRouter.UnhandledPrimaryAction when a game needs the same board/sprite selection behavior for a left mouse press and a single-finger tap. This event only fires inside a view when no widget has claimed the input. Widget hit testing and the original touch ownership are handled centrally: a widget that hides on touch-down does not leak that tap into the game. A mouse action fires on press; a touch action fires only after a completed recognized tap. Drags, cancellations, swipes, and multi-touch do not count.

private WidgetInputRouter? _gameInput;

protected override void OnSceneGraphCreated()
{
    _gameInput = WidgetInputRouter;
    if (_gameInput is not null)
        _gameInput.UnhandledPrimaryAction += OnPrimaryAction;
}

protected override void OnTouchAdapterInitialized()
{
    // GameHostBase starts mouse monitoring automatically, but touch monitoring
    // must be started explicitly for touch taps to reach UnhandledPrimaryAction.
    Engine.Input.TouchEventPoller?.StartMonitoringTouch();
}

private void OnPrimaryAction(PrimaryPointerActionEventArgs e)
{
    // e.View, e.ScreenPositionPx, e.IsTouch, e.PointerId
    var grid = e.View.ScreenPxToGrid(mySceneLayer, e.ScreenPositionPx);
    SelectGameCell(grid); // Game-specific behavior
}

protected override void UnhookEvents()
{
    if (_gameInput is not null)
        _gameInput.UnhandledPrimaryAction -= OnPrimaryAction;
    _gameInput = null;
}

Keep the router reference for cleanup: GameHostBase.Dispose() disposes its router before calling UnhookEvents(). Note that GameHostBase.InitializeWidgetInput starts mouse monitoring only; hosts targeting touch devices must also override OnTouchAdapterInitialized() and call StartMonitoringTouch() on TouchEventPoller so that touch events are polled. The event does not automatically select sprites; games use the logical position and reported view to hit-test tiles, sprites, or objects. Game-specific rules such as turn ownership or modal state remain the game's responsibility. WinForms only receives mouse input unless an additional touch adapter is supplied.


Why Widgets Exist

DirectDrawing can render almost any custom visual, but rendering alone does not provide the lifecycle and input behavior expected from a user-interface control.

Widgets provide that higher-level structure while remaining:

  • engine-native
  • code-first
  • platform-agnostic
  • compatible with Gondwana's existing View and SceneLayer rendering models

A useful mental model is:

Direct drawings are engine-managed custom visuals. Widgets are interactive or reusable direct-drawing components with a shared lifecycle and input model.

Not every widget is interactive. Display-oriented widgets such as labels, progress bars, health bars, name tags, and popups still benefit from the same composition and lifecycle model without needing direct user input.


Available Widgets

The following pages cover the current widgets available in Gondwana.Widgets.

Menus

Callback-based menu bars with stable item keys, check/radio commands, recursive submenus, icons, typed keyboard shortcuts, and explicit mnemonics. Closed menus receive unhandled accelerators after the focused widget has its opportunity to handle input.

Controls

A clickable text button for commands and actions. Supports pointer activation as well as keyboard activation with Enter or Space.

A two-state control for independent Boolean options such as enabling music, showing diagnostics, or toggling gameplay settings.

A compact single-selection control that expands into a ListBoxWidget. Useful for difficulty, resolution, character class, or other enumerated choices.

Interactive text that opens an external absolute URI through a platform-provided IExternalUriLauncher.

A retained-mode, non-interactive text widget for captions, headings, status displays, instructions, and other interface text.

A vertically scrolling list of strings with pointer and keyboard selection.

A rectangular container used to group other widgets. Supports solid or image backgrounds, borders, rounded corners, and child-widget positioning.

A generic horizontal or vertical progress indicator representing a normalized value from 0.0 through 1.0.

A single-choice control that can participate in a RadioButtonGroup so selecting one option automatically clears the others.

A single-line editable text control with caret movement, insertion, deletion, placeholder text, length limits, and submit behavior.

A tabbed container whose pages own ordinary widgets. Headers resize across configurable rows, pages can use Alt mnemonics, and optional pointer dragging can reorder tabs.


Layout

A lightweight layout container that arranges child widgets sequentially in a horizontal or vertical stack with configurable spacing.


Dialogs and Dialogue

A ready-to-use application information dialog with application name, version, optional description, copyright, logo, and external hyperlink.

The base class for custom Gondwana dialogs. Provides a panel, title bar, optional close button, dragging, keyboard accept/cancel behavior, and DialogResult semantics.

An NPC-style dialogue panel with speaker name, wrapped body text, continue indicator, and pointer or keyboard advance behavior.


HUD Widgets

A world-space health bar that automatically follows a Sprite and displays current health relative to a configurable maximum.

A world-space text label that automatically follows a Sprite, suitable for NPC names, player names, unit labels, or similar identifiers.

A view-level, scrollable runtime diagnostics HUD backed by Engine.Instance.Profiler. It owns telemetry collection only while shown, can include or suppress individual measurements and sources, can opt into Scene/View/backbuffer/configuration/MSAA context, supports application-defined extension lines, hover definitions, and a built-in Metrics selector for toggling individual diagnostics at runtime. The saved-scene Scene Viewer dogfoods this widget for its F3 diagnostics overlay.


Overlays

Short-lived text or image content that can move, accelerate, fade, and originate from view coordinates, world coordinates, a tile, or a grid cell.

Typical uses include damage numbers, healing values, XP gains, item pickups, and status effects.

A full-view splash image that fades in, holds for a minimum duration, optionally performs startup work, and then fades out.

A lightweight, non-interactive view overlay for contextual hover help. It positions near the pointer, clamps to the owning View, and does not intercept underlying input.

A temporary view-level notification that can slide or fade into place, remain visible for a configured duration, and dismiss automatically or on user input.


Input at a Glance

Interactive widgets such as buttons, check boxes, radio buttons, list boxes, combo boxes, text boxes, tab controls, conversation boxes, dialogs, hyperlinks, and dismissible toasts participate in normal widget focus, pointer, and keyboard routing.

Display and layout widgets such as:

  • LabelWidget
  • PanelWidget
  • ProgressBarWidget
  • HealthBarWidget
  • NameTagWidget
  • ProfilerWidget
  • TooltipWidget
  • PopupWidget
  • StackPanelWidget

do not normally require direct input.


Installation

Add the Widgets package to your game project:

dotnet add package Gondwana.Widgets

Then import the widget namespace or the namespace containing the widget family you are using:

using Gondwana.Widgets;
using Gondwana.Widgets.Controls;
using Gondwana.Widgets.Dialogs;
using Gondwana.Widgets.Dialogue;
using Gondwana.Widgets.Hud;
using Gondwana.Widgets.Layout;
using Gondwana.Widgets.Overlays;

Where to Read Next

Start with the individual widget pages above for normal game development.

For lower-level details about the shared widget system, see:

  • Gondwana.Widgets/WidgetBase.cs
  • Gondwana.Widgets/ContainerWidget.cs
  • Gondwana.Widgets/WidgetInputRouter.cs
  • Gondwana.Widgets/*

For the underlying rendering model, see the DirectDrawing documentation.


Widgets on wrapped layers

World-space widget children repeat with their SceneLayer; View-bound UI never wraps. Following widgets share their target's translations. Hit testing returns the canonical widget, while pointer capture retains the chosen image in WrappedOffsetWorldPx. See SceneLayer Wrapping for dragging and custom-control coordinate handling.

Gondwana Wiki

🔎 Search the Gondwana Wiki

Introduction

Core Concepts
Systems
Rendering
Widgets
Studio & Desktop Tools
Advanced Topics

Reference

Clone this wiki locally