Skip to content

UI widgets

The MicroEngine.UI namespace provides a small set of pixel-art widgets that games compose inside their own scenes: a Typewriter for revealing text, a paged DialogueBox, a keyboard-driven SelectionList, a UiTheme record that lets each game keep its own look, and the achievement banners and list screen. Widgets read input through actions and draw with the built-in BitmapFont; there is no layout system or retained widget tree.

Overview

Type Purpose
UiTheme Panel style plus text, shadow, accent and muted colours shared by the widgets
Typewriter Reveals a string character by character; raises Revealed for blip sounds
DialogueBox Queue of pages in a panel with a typewriter and an optional speaker tag
SelectionList Vertical menu navigated with up/down and chosen with confirm
AchievementToasts Unlock banners drawn above every scene (Achievements)
AchievementsScene Overlay listing a game’s achievements (Achievements)

Source: src/MicroEngine/UI/Widgets.cs, src/MicroEngine/UI/AchievementViews.cs

UiTheme

public sealed record UiTheme(PanelStyle Panel, Color Text, Color TextShadow, Color Accent, Color Muted)
{
public static readonly UiTheme Default = new(
new PanelStyle(Color.Hex("1c1b2e"), Color.Hex("f4f1de"), Color.Hex("3a3a5c"), Color.Hex("00000080")),
Color.Hex("f4f1de"), Color.Hex("0a0a14"), Color.Hex("f2cc60"), Color.Hex("8a8aa8"));
}

Source: src/MicroEngine/UI/Widgets.cs

PanelStyle(Fill, Border, Highlight, Shadow, InnerBorder) is drawn by the r.Panel(rect, style) renderer extension (Rendering). Each sample game defines one theme and passes it to its widgets and to UseAchievements.

Typewriter

Member Behaviour
Start(text) Resets and starts revealing text
Update(dt) Reveals CharactersPerSecond characters per second (default 45)
Finish() Reveals everything at once
Visible, VisibleCount, Complete Current state
Revealed event Raised for each newly revealed non-whitespace character

DialogueBox

Show(speaker, params pages) queues pages (and opens the box if it was closed). Update(dt, input, confirm = "confirm") advances the typewriter; confirm first completes the current page, then moves to the next. It returns true while the box is active, so a scene can skip its own input handling. Draw(r, font, area) wraps text to the area, shows up to Lines lines (default 3), a speaker name tag and a blinking arrow when the page is complete. Events: Blip (per revealed character) and Closed (after the last page).

Courtyard Key wraps the box in an overlay scene that freezes the world:

public sealed class DialogueScene : Scene
{
private readonly DialogueBox _box = new() { Theme = UiStyle.Theme, Lines = 3 };
// ...
public override bool IsOverlay => true;
public override void Enter()
{
_box.Blip += _ => { if (_blips++ % 2 == 0) Audio.Play("blip", 0.6f, 1f, 0.04f); };
_box.Closed += () => _done = true;
_box.Show(_speaker, _pages);
}
public override void Update(float dt)
{
if (_done) return;
_box.Update(dt, Input);
if (_done)
{
Game.Scenes.Pop();
_closed?.Invoke();
}
}
public override void Draw(IRenderer r)
{
var area = new RectF(10, 128, 300, 46);
_box.Draw(r, Font, area);
}
}

Source: games/CourtyardKey/Scenes/DialogueScene.cs

Text longer than Lines lines is cut, not scrolled: split long text into several pages.

SelectionList

new SelectionList(params options) needs at least one option. Update(input, up, down, confirm) moves the highlight (wrapping when Wrap is true, the default) and returns the chosen index on confirm, otherwise null. SetEnabled(index, false) greys an option out and makes it unselectable; SetOptions replaces the list. The Moved event fires when the highlight changes. Draw(r, font, position, theme, time, lineHeight = 12) draws the list with a bobbing cursor.

private readonly SelectionList _menu = new("RESUME", "QUIT TO TITLE");
// ...
public override void Enter() => _menu.Moved += () => game.Sfx("menu-move", 0.6f);
public override void Update(float dt)
{
if (Input.Pressed("pause")) { Close(); return; }
if (_menu.Update(Input) is { } choice)
{
game.Sfx("menu-select", 0.7f);
if (choice == 0) Close();
else Game.Scenes.Reset(new TitleScene(game), Transition.Fade(0.6f));
}
}

Source: games/NeonRift/Scenes/PlayScene.cs

Games can keep the list for its logic and draw it their own way: Neon Rift passes _menu to its own Ui.Menu helper instead of calling SelectionList.Draw.

Achievement views

AchievementToasts and AchievementsScene are documented with the rest of the system in Achievements.

Limitations

  • Keyboard and gamepad navigation only; widgets do not respond to mouse or touch.
  • Text uses the single built-in font; there is no rich text, right-to-left text or localisation support.
  • Layout is manual (rectangles and positions in virtual pixels).