Skip to content

Widgets

Generated from engine/ui/widget.h at 862e08b. The text under each declaration is the header's own comment, word for word. About the reference says how these pages are made.

Browse the ui module

#include "engine/ui/widget.h" · namespace labrador

class UiObject : public GameObject

A named, hideable thing in a user interface.

NO RESCALE. A scale_size_and_position() on every widget, with a container walking the tree to apply it, is a layout policy - and an engine base class is not where a game's answer to "what happens at 1280x720" belongs (T1). It is also destructive: the factor is recorded nowhere, so after the walk no authoritative geometry is left to write against, and any later absolute setter writes design-space units into a rescaled widget. A screen mixing the two gets bars 1.5x too wide for the box they sit in, because the box was shrunk and the bars were not.

A widget holds one geometry, in whatever space the game authored it in, and nothing ever rewrites it. The mapping to the screen is a Camera, and it is set on the draw list once for the whole menu rather than passed to every widget in turn (renderer.h, DrawList::set_camera).

UiObject() = default;
explicit UiObject(const std::string& name, bool hidden = false);
const std::string& name() const;
void set_hidden(bool hidden);
bool hidden() const;
void update(float dt) override = 0;
void draw(DrawList& draw_list) const override = 0;
mattmath::RectangleF bounds() const override = 0;
virtual void set_colour(const Colour& colour);

Painting a thing that has no colour is nothing, not an error, which is why this is a no-op here and pure one level down.

It is on this class at all so that a container can forward it to children it knows only as UiObjects. The two alternatives are both worse: a container that asked at runtime which of its children were colourable would be putting a cast on the path, and a container that could only hold colourable children would be a narrower container than anyone asked for.

class UiWidget : public UiObject

A UiObject that has a colour, which is what the focus machinery traffics in: FocusGroup::add takes one of these, because paint is how a menu shows where the cursor is (focus.h).

UiWidget() = default;
explicit UiWidget(const std::string& name, bool hidden = false);
~UiWidget() override = default;
void update(float dt) override = 0;
void draw(DrawList& draw_list) const override = 0;
mattmath::RectangleF bounds() const override = 0;
void set_colour(const Colour& colour) override = 0;

Pure again. UiObject's no-op is for a thing that is drawn and not coloured; a widget is the other kind, and being the other kind is the whole of what this class adds.

class UiContainer : public UiWidget

A widget that is its children.

IT IS A UiWidget AND NOT A UiObject, which is the difference between a compound widget being writable and not. FocusGroup::add takes a UiWidget*, so a container that was only a UiObject could not be focused - and a row that is a label and a value side by side, which is what every options screen is made of, is exactly a container the cursor lands on. A client without this recovers the label-to-value relationship by comparing the focused pointer against each label in turn, and cannot write a slider at all.

SO IT IS NOT final EITHER, and deriving from it is how a compound widget is written: build the parts in the constructor, hand them to add_child, and inherit update, draw, bounds and set_colour already written. set_colour reaches every child, so a focus group holding one of these paints the whole row.

Children are loans, and the container outlives nothing. A deriving class that owns its children holds them as members and calls add_child from its constructor body: members are built after the base, so there is nothing to add during the initialiser list, and destruction runs the other way - the members go first, and the container destroyed after them drops their pointers without reading any of them.

UiContainer · add_child · remove_child · remove_all_children · child_count · children

Section titled “UiContainer · add_child · remove_child · remove_all_children · child_count · children”
UiContainer() = default;
explicit UiContainer(const std::string& name);
void add_child(UiObject* child);
void remove_child(const std::string& name);
void remove_child(const UiObject* child);
void remove_all_children();
size_t child_count() const;
std::vector<std::pair<std::string, UiObject*>> children();
void update(float dt) override;
void draw(DrawList& draw_list) const override;
mattmath::RectangleF bounds() const override;
mattmath::RectangleF cull_bounds(float units_per_pixel) const override;
void set_colour(const Colour& colour) override;

Every child, in the order they were added, and whatever a child makes of it - a nested container passes it down, and a child with no colour ignores it.

class UiTexture : public UiWidget, public TextureObject

THE THREE LEAVES ARE NOT final, deliberately: PHILOSOPHY (UI) has every leaf open to derivation. A client wanting a text object with one extra behaviour - a line that builds itself from a list of parts, say - derives from UiText, where composing one would mean forwarding update, draw, bounds and set_colour to it by hand: four functions of boilerplate to add one.

Deriving costs nothing structural. The destructor is virtual from GameObject down, so these are safe to hold and delete as UiWidget*, and each leaf is that plus a render-side base that already does the drawing.

WHAT A DERIVING CLASS OWES: if it overrides draw(), it early-outs on hidden() itself. That check is written into each leaf's draw rather than into a wrapper around it, so an override replaces it rather than running after it.

UiTexture() = default;
UiTexture(const std::string& name,
const std::string& sheet_name,
const std::string& frame_name,
const mattmath::RectangleF& rectangle,
RenderResources* render_resources,
const Colour& color = Colour::white,
bool hidden = false,
float rotation = 0.0f,
SpriteFlip flip = SpriteFlip::none,
float layer_depth = 0.0f);
void update(float dt) override;
void draw(DrawList& draw_list) const override;
mattmath::RectangleF bounds() const override;
mattmath::RectangleF cull_bounds(float units_per_pixel) const override;

set_texture · set_sprite_frame · set_colour · set_position · set_position_at_center · set_width · set_height · set_size · set_position_from_top_right_origin

Section titled “set_texture · set_sprite_frame · set_colour · set_position · set_position_at_center · set_width · set_height · set_size · set_position_from_top_right_origin”
void set_texture(const std::string& sheet_name, const std::string& frame_name);
void set_sprite_frame(const std::string& frame_name);
void set_colour(const Colour& colour) override;
void set_position(const mattmath::Vector2F& position);
void set_position_at_center(const mattmath::Vector2F& position);
void set_width(float width);
void set_height(float height);
void set_size(const mattmath::Vector2F& size);
void set_position_from_top_right_origin(const mattmath::Vector2F& position);
const mattmath::RectangleF& rectangle() const;
class UiText : public UiWidget, public Text
UiText() = default;
UiText(const std::string& name,
const std::wstring& text,
const std::string& font_name,
const mattmath::Vector2F& position,
RenderResources* render_resources,
const Colour& color = Colour::white,
bool hidden = false,
float scale = 1.0f,
float rotation = 0.0f,
const mattmath::Vector2F& origin = mattmath::Vector2F::ZERO,
float layer_depth = 0.0f);
void update(float dt) override;
void draw(DrawList& draw_list) const override;
mattmath::RectangleF bounds() const override;
void set_colour(const Colour& colour) override;
UiTextDropShadow() = default;
UiTextDropShadow(const std::string& name,
const std::wstring& text,
const std::string& font_name,
const mattmath::Vector2F& position,
RenderResources* render_resources,
const Colour& color = Colour::white,
const Colour& shadow_color = Colour::black,
const mattmath::Vector2F& shadow_offset = { 2.0f, 2.0f },
bool hidden = false,
float scale = 1.0f,
float shadow_scale = 1.0f,
float rotation = 0.0f,
const mattmath::Vector2F& origin = mattmath::Vector2F::ZERO,
float layer_depth = 0.0f);
void update(float dt) override;
void draw(DrawList& draw_list) const override;
mattmath::RectangleF bounds() const override;
void set_colour(const Colour& colour) override;

Named in the public declarations above: Colour, DrawList, GameObject, RectangleF, RenderResources, SpriteFlip, Text, TextDropShadow, TextureObject, Vector2F.

The files that include this header directly. A file can also reach it through another header.

Development documentation (unreleased). Built from Labrador 862e08b of 2026-10-08.