Lysa UI  0.0
Lysa UI —UI components for the Lysa Engine
Architecture overview

Overview

Lysa UI is organized as a set of C++23 modules (.ixx interfaces paired with .cpp implementations) that live in the lysa::ui namespace. The single top-level module lysa.ui re-exports every public widget type and serves as the only import needed by application code.

The library sits on top of the Lysa Engine and relies on the engine context (lysa::ctx()) for rendering, resource management, and event dispatch.

Entry Point : WindowManager

lysa::ui::WindowManager is the root of every UI setup. It attaches to a RenderingWindow and owns a list of virtual lysa::ui::Window objects. The WindowManager:

  • Is constructed with the RenderingWindow it draws into.
  • Owns the default Style, font and font scale inherited by every Window that does not set its own. The default font is embedded in the library, so no font resource has to be loaded to display text.
  • Subscribes to the engine's PROCESS and INPUT events to drive per-frame rendering and input routing.
  • Routes mouse and keyboard events to the currently focused Window.
  • Handles user-driven Window resizing via configurable border deltas.
  • Exposes drawFrame() and onInput() for explicit frame-by-frame control when needed.
lysa::ui::WindowManager windowManager(renderingWindow);
windowManager.setDefaultFontScale(0.25f);
windowManager.getDefaultStyle().setOption("color_text", "0.9,0.9,0.9,1.0");

Widget Base Class

lysa::ui::Widget is the base of every UI element. Key responsibilities:

Concern Details
Identity Inherits UniqueResource; each widget has a unique id used for event subscription
Layout Rect for position and size; Alignment enum controls placement within the parent
Hierarchy add<T>() / create<T>() templates; remove() / removeAll()
Appearance setDrawBackground(), setTransparency(), setPadding(), setHBorder(), setVBorder()
Input Virtual eventMouseDown, eventMouseUp, eventMouseMove, eventKeyDown, eventKeyUp, eventTextInput hooks
Focus setFocus(), isFocused(), allowFocus flag; tab-order traversal via setNextFocus()
State show() / isVisible(), enable() / isEnabled(), setFreezed() / isFreezed()
User data setUserData() / getUserData() and setGroupIndex() / getGroupIndex() for application tagging
Attachment isAttached(), and getWindow(), getStyle(), getWindowManager() returning the objects inherited from the tree the widget is attached to

Widget Hierarchy

Widget
├── Panel
│ ├── Box
│ │ └── Button
│ ├── Frame
│ ├── Grid
│ ├── TabContainer
│ └── Selection
├── Popup
│ └── Tooltip
├── Menu
│ ├── MenuPanel
│ └── MenuBox
├── ScrollContainer
├── FoldableContainer
├── CheckWidget
│ ├── ToggleButton
│ ├── CheckBox
│ └── RadioButton
├── CheckMark
├── CrossMark
├── Arrow
├── Line
│ ├── HLine
│ └── VLine
├── Text
├── TextEdit
├── RichTextArea
├── Image
├── Icon
│ ├── IconImage
│ └── IconSVG
├── ValueSelect
│ ├── ScrollBar
│ │ ├── HScrollBar
│ │ └── VScrollBar
│ ├── ProgressBar
│ │ ├── HProgressBar
│ │ └── VProgressBar
│ ├── Slider
│ │ ├── HSlider
│ │ ├── VSlider
│ │ └── ColorSlider
│ │ ├── HColorSlider
│ │ └── VColorSlider
│ ├── SpinBox
│ └── Knob
├── SpinButtons
├── List
│ └── ListBox
├── DropDownList
├── ColorWheel
├── ColorPicker
├── ColorPickerButton
├── DialogBox
├── GridCell
├── Table
├── TabBar
└── TreeView

Window has its own hierarchy for the dialogs displayed in a dedicated window :

Window
└── ModalDialog
├── ColorModalDialog
└── TextInputDialog

Menu, TabBar, TabContainer, FoldableContainer, TreeView, ListBox, ScrollContainer, ScrollBar, CheckBox, Slider, ColorSlider, SpinBox, SpinButtons, ColorPicker, ColorPickerButton, DropDownList, RichTextArea and DialogBox are composite widgets : they build their own children (rows, marks, scroll bars, grips, highlights). Their sub-widget resource strings are assigned by the style through a setResources() call in StyleBase::addResource(), so the application never has to call it.

Window

lysa::ui::Window is a virtual panel displayed inside a RenderingWindow. It holds a root lysa::ui::Widget that covers its entire client area and acts as the parent for all widgets in that window. Key features:

  • setWidget() sets the root widget and its resource string.
  • create<T>() / add<T>() template helpers delegate to the root widget.
  • setStyle() installs a style for this window only; getStyle() returns it, or the WindowManager default style when none was set. setStyle(nullptr) goes back to that default.
  • setFont() / setFontScale() override the WindowManager defaults for this window only.
  • setResizeableBorders() controls which edges the user can drag to resize the window.
  • Visibility changes (show(), hide(), setVisible()) take effect at the start of the next frame.
  • Virtual callbacks onAttach, onDetach, onShow, onHide, onResize, onMove, onKeyDown, onKeyUp, onMouseDown, onMouseUp, onMouseMove, onGotFocus, onLostFocus are intended to be overridden by application-level subclasses.

Styling

lysa::ui::Style is the abstract drawing backend. It defines three responsibilities:

Method Description
draw() Renders a widget before (when=true) or after (when=false) its children are drawn
resize() Adjusts a widget's size rectangle to style-specific constraints
addResource() Parses a resource string and attaches a UIResource to a widget

lysa::ui::Style itself holds everything the styles have in common : the name-value option map, the color palette and the widget sizes read from it. lysa::ui::StyleBase adds everything that does not depend on the look : the Widget::Type dispatching of the three methods above, the resource strings handed to the composite widgets, and the drawing of the widgets whose appearance comes from their content (texts, icons, tree view branches, grid lines, color slider & wheel, marks, arrows).

Two looks are built on top of it :

Style Style::create() name Look
lysa::ui::StyleClassic vector Classic 3D beveled vector look
lysa::ui::StyleMaterial material Material Design inspired flat look

Both read the three appearance modes of a lysa::ui::StyleResource, each giving them its own meaning:

Mode StyleClassic StyleMaterial
FLAT No 3D effect Plain surface, no outline
RAISED Beveled raised appearance Filled (elevated) container
LOWERED Beveled lowered (sunken) appearance Outlined container

Style options are set via setOption() / getOption(). Every style reads the colors color_focus, color_selected, color_highlight, color_foreground_up, color_foreground_down, color_button, color_shadow_dark, color_shadow_bright, color_cursor, color_tree_lines, color_grid_lines and color_text, and the sizes size_scroll_bar, size_radio_button, size_check_box, size_tree_indent, size_sort_arrow and size_knob. StyleMaterial adds color_primary, color_on_primary, color_surface, color_surface_container, color_outline, color_outline_variant, size_corner_radius and size_corner_radius_small.

Style::getTextColor() returns the default color of the texts, read from the color_text option : the color belongs to the style, not to the Window. A Text or a RichTextArea that was not given an explicit color picks it up when it is attached to a tree.

The WindowManager instantiates the default StyleClassic and hands it out with getDefaultStyle(). Another built-in style is installed with window->setStyle(lysa::ui::Style::create("material")). A custom style is created by subclassing StyleBase (to keep the widget dispatching) or Style (to redo it) and passing an instance to Window::setStyle().

Event System

All widget events are dispatched through the engine's EventManager. Widgets fire events using UIEvent signal-name constants:

Signal Payload struct Description
UIEvent::OnClick UIEventClick Button or ToggleButton clicked
UIEvent::OnClick UIEventLink [url] link of a RichTextArea clicked; target holds the tag value or the link text
UIEvent::OnStateChange UIEventState CheckWidget state changed
UIEvent::OnValueChange UIEventValue ValueSelect value changed
UIEvent::OnRangeChange UIEventRange ValueSelect range changed
UIEvent::OnColorChange UIEventColor Color of a ColorWheel, ColorPicker or ColorPickerButton changed
UIEvent::OnUserDataChange UIEvent User data attached to a widget changed
UIEvent::OnTextChange UIEventText Text widget content changed
UIEvent::OnTextInput UIEventText User text input in a TextEdit
UIEvent::OnKeyDown UIEventKeyb Key pressed with widget focused
UIEvent::OnKeyUp UIEventKeyb Key released with widget focused
UIEvent::OnMouseDown UIEventMouseButton Mouse button pressed over widget
UIEvent::OnMouseUp UIEventMouseButton Mouse button released over widget
UIEvent::OnMouseMove UIEventMouseMove Mouse moved over widget
UIEvent::OnMouseLeave UIEvent Mouse cursor left the widget
UIEvent::OnGotFocus UIEvent Widget acquired keyboard focus
UIEvent::OnLostFocus UIEvent Widget lost keyboard focus
UIEvent::OnShow UIEvent Widget became visible
UIEvent::OnHide UIEvent Widget became hidden
UIEvent::OnEnable UIEvent Widget enabled
UIEvent::OnDisable UIEvent Widget disabled
UIEvent::OnResize UIEvent Window resized
UIEvent::OnMove UIEvent Window moved
UIEvent::OnInsertItem UIEventItem Item inserted into a List / ListBox
UIEvent::OnRemoveItem UIEventItem Item removed from a List / ListBox
UIEvent::OnSelectItem UIEventItem Selected item changed in a List / ListBox / DropDownList / TreeView / Menu / TabBar / TabContainer
UIEvent::OnExpandItem UIEventExpand Item of a TreeView expanded or collapsed, or content of a FoldableContainer folded or unfolded
UIEvent::OnDoubleClickItem UIEventItem Item of a TreeView double-clicked

Subscribe using lysa::ctx().events.subscribe(UIEvent::OnClick, widget->id, handler). Events are queued and delivered on the next frame; the payload is read with std::any_cast<UIEventItem>(event.payload).

Lua Scripting

When the engine is compiled with LUA_BINDINGS=ON, every widget type inherits from LuaScript and exposes its full public API to Lua via LuaBridge. All UIEvent signal constants and Alignment, CheckWidget::State, Line::LineStyle, ScrollBar::Type, Slider::Type, ProgressBar::Orientation, ProgressBar::Display, Arrow::Orientation, TreeView::SelectionMode, TabPlacement, ColorPicker::Component, ColorPicker::Mode, SVGBounds and Window::ResizeableBorder enums are available in the lysa.ui Lua namespace. Every widget type (ListBox, ProgressBar, ScrollBox, Menu, TabBar, TabContainer, Slider, SpinBox, SpinButtons, ColorWheel, ColorPicker, ColorPickerButton, DropDownList, IconImage, IconSVG, RichTextArea, and their variants) is fully accessible with the same create_* factory pattern, in snake_case : create_tab_container(), create_radio_button(), create_check_box(), create_color_picker(), create_drop_down_list(), create_icon_svg(), create_rich_text_area(), …

Style is exposed too, with set_option(), get_option() and the read-only text_color property; it is reached through WindowManager.default_style or Window.style.

The complete Lua surface is described in Lua API.