FrostUI is a small C++20 retained-mode GUI toolkit with a software renderer, platform backends for Linux/X11 and Windows/Win32, and a deliberately compact widget set.
This repository currently exposes enough to build small desktop tools and UI experiments. It does not yet provide a large widget catalog, theming system, scrolling, menus, or rich text/layout primitives.
Public API available from include/frost/frost.hpp:
Applicationfor the main window, event loop, focus, and renderingContainerwith flex-style layoutHBox(),VBox(), andCenter()helpersLabelfor text displayButtonwithon_clickTextInputfor single-line text entry withon_text_changedandon_submitCheckbox,RadioButton, andSliderPlatformWindowand platform initialization helpers- Core utility types such as
Result<T>,Signal, geometry structs, and colors
Current layout controls:
FlexDirection:Row,RowReverse,Column,ColumnReverseFlexJustify:Start,End,Center,SpaceBetween,SpaceAround,SpaceEvenlyFlexAlign:Start,End,Center,Stretch- Per-child flex settings through
Container::set_child_flex()
Current limitations worth knowing up front:
- widget catalog is still intentionally compact
TextInputis single-line onlyContainerStyle.wrapexists but is marked TODO and is not implemented- no built-in list, table, image, menu, or scroll container
- text measurement is approximate in some widgets
- default rendering path is software rendering; Vulkan is optional and conditional
- runtime renderer selection is available through
ApplicationConfig::renderer_backend
Font support notes:
- software renderer uses an embedded default bitmap font
- optional custom fonts can be loaded via
Application::load_font_from_file(...).psf(PSF1/PSF2) works without extra dependencies.ttf/.otfrequire FreeType support (-DFROST_USE_FREETYPE=ONand FreeType installed)
- fallback glyphs use
?when a character is not present
- C++20 compiler: GCC 11+, Clang 14+, or MSVC 2022+
- CMake 3.20+
- Linux: X11 development headers
- Windows: Visual Studio / Win32 SDK
Linux packages:
# Ubuntu / Debian
sudo apt install libx11-dev
# Arch
sudo pacman -S libx11
# Fedora
sudo dnf install libX11-develOptional Vulkan packages:
# Ubuntu / Debian
sudo apt install vulkan-sdk
# Arch
sudo pacman -S vulkan-devel
# Fedora
sudo dnf install vulkan-develOptional FreeType packages (for .ttf / .otf loading):
# Ubuntu / Debian
sudo apt install libfreetype6-dev
# Arch
sudo pacman -S freetype2
# Fedora
sudo dnf install freetype-develBuild with GPU backend support:
cmake -B build -DFROST_USE_VULKAN=ON
cmake --build buildBuild with TTF/OTF font loading support:
cmake -B build -DFROST_USE_FREETYPE=ON
cmake --build buildcmake -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir buildExample executables:
screenshots
./build/bin/frost_demo
./build/bin/frost_font_showcase
./build/bin/frost_calculator
./build/bin/frost_unit_converter.\build\bin\Release\frost_demo.exe
.\build\bin\Release\frost_font_showcase.exe
.\build\bin\Release\frost_calculator.exe
.\build\bin\Release\frost_unit_converter.exe#include <frost/frost.hpp>
#include <iostream>
using namespace frost;
int main() {
auto init_result = platform::initialize();
if (!init_result) {
std::cerr << init_result.error().message << "\n";
return 1;
}
platform::shutdown();
return 0;
}ApplicationConfig config;
config.window.title = "My FrostUI App";
config.window.width = 800;
config.window.height = 600;
config.window.show_minimize_button = true;
config.window.show_maximize_button = false;
config.renderer_backend = RendererBackend::Gpu;
config.allow_software_fallback = true;
auto app_result = Application::create(config);
if (!app_result) {
return 1;
}
auto& app = *app_result.value();Renderer selection behavior:
RendererBackend::Softwareis always availableRendererBackend::Gpuis available only when built with-DFROST_USE_VULKAN=ON- when GPU is requested but unavailable and
allow_software_fallbackistrue, app falls back to software
FrostUI now supports native window icons through raw RGBA pixel buffers in WindowConfig.
WindowConfig::Icon large_icon;
large_icon.width = 64;
large_icon.height = 64;
large_icon.rgba8 = make_my_64x64_rgba_pixels();
WindowConfig::Icon small_icon;
small_icon.width = 32;
small_icon.height = 32;
small_icon.rgba8 = make_my_32x32_rgba_pixels();
ApplicationConfig config;
config.window.title = "My FrostUI App";
config.window.icons.large = std::move(large_icon);
config.window.icons.small = std::move(small_icon);Notes:
- Windows uses the large and small icons for taskbar/title-bar integration.
- X11 publishes the configured icon sizes through
_NET_WM_ICON. - FrostUI does not yet decode
.pngor.icofiles for you, so you currently provide RGBA bytes directly. - Minimize/maximize button visibility is fully supported on Win32 and best-effort on X11.
FrostUI is retained-mode. You create widgets, connect signals, add them to containers, then set a single root widget on the app.
auto root = VBox(12.0f);
root->set_padding(Edges{20.0f, 20.0f, 20.0f, 20.0f});
auto title = Label::create("Hello, FrostUI");
title->set_font_size(24.0f);
title->set_text_align(TextAlign::Center);
root->add_child(std::move(title));
auto input = TextInput::create("Type your name");
auto* input_ptr = input.get();
root->add_child(std::move(input));
auto output = Label::create("Waiting for input...");
auto* output_ptr = output.get();
root->add_child(std::move(output));
auto button = Button::create("Update");
button->on_click.connect([input_ptr, output_ptr]() {
if (input_ptr->text().empty()) {
output_ptr->set_text("Please enter a name.");
} else {
output_ptr->set_text("Hello, " + input_ptr->text() + "!");
}
});
root->add_child(std::move(button));app.set_root(std::move(root));
app.run();
platform::shutdown();Most FrostUI apps follow the same pattern:
- Call
platform::initialize(). - Create an
Application. - Build a tree of
Container,Label,Button, andTextInput. - Store raw pointers before moving widgets into containers if you need to update them later.
- Connect signals like
Button::on_clickorTextInput::on_submit. - Call
app.set_root(...). - Enter
app.run(). - Call
platform::shutdown()before exit.
The common retained-mode pattern looks like this:
auto label = Label::create("0");
auto* label_ptr = label.get();
auto button = Button::create("Increment");
button->on_click.connect([label_ptr]() {
label_ptr->set_text("1");
});auto column = VBox(16.0f);
auto row = HBox(8.0f);
auto centered = Center();ContainerStyle style;
style.direction = FlexDirection::Row;
style.justify = FlexJustify::SpaceBetween;
style.align_items = FlexAlign::Center;
style.gap = 12.0f;
auto toolbar = Container::create(style);auto row = HBox(12.0f);
row->add_child(Label::create("Fixed"));
row->add_child(TextInput::create("Expands"));
row->set_child_flex(0, FlexItem{.grow = 0.0f, .shrink = 0.0f});
row->set_child_flex(1, FlexItem{.grow = 1.0f, .shrink = 1.0f});Use it for static or dynamically updated text.
auto label = Label::create("Status: ready");
label->set_font_size(18.0f);
label->set_text_color(Color{0.4f, 0.8f, 0.4f, 1.0f});
label->set_text_align(TextAlign::Center);
label->set_vertical_align(VerticalAlign::Middle);Use it for click actions.
auto button = Button::create("Save");
button->on_click.connect([]() {
// handle click
});Custom style:
ButtonStyle style;
style.background_color = Color{0.18f, 0.40f, 0.70f, 1.0f};
style.hover_color = Color{0.24f, 0.48f, 0.80f, 1.0f};
style.pressed_color = Color{0.12f, 0.30f, 0.58f, 1.0f};
auto primary = Button::create("Primary", style);Use it for single-line input.
auto input = TextInput::create("Search...");
input->on_text_changed.connect([](const String& text) {
// react to typing
});
input->on_submit.connect([](const String& text) {
// react to Enter
});auto checkbox = Checkbox::create("Enable feature");
checkbox->on_toggled.connect([](bool checked) {
// checked changed
});auto low = RadioButton::create("Low", "quality");
auto high = RadioButton::create("High", "quality");
high->set_selected(true);auto slider = Slider::create(0.0f, 100.0f, 50.0f);
slider->set_step(1.0f);
slider->on_value_changed.connect([](f32 value) {
// value changed
});Load a PSF bitmap font for software-rendered text:
auto load_result = app.load_font_from_file("/path/to/my-font.psf");
if (!load_result) {
std::cerr << load_result.error().message << "\n";
}
// Revert to embedded default font
app.reset_font();
## Example UIs Included In This Repo
### `frost_demo`
Located at [`examples/demo/main.cpp`](/home/fuad/Dev/FrostUI/examples/demo/main.cpp)
- counter controls
- text input and greeting action
- basic vertical and horizontal layout composition
### `frost_calculator`
Located at [`examples/calculator/main.cpp`](/home/fuad/Dev/FrostUI/examples/calculator/main.cpp)
- two text fields for operands
- add, subtract, multiply, divide actions
- result and validation messaging
### `frost_unit_converter`
Located at [`examples/unit_converter/main.cpp`](/home/fuad/Dev/FrostUI/examples/unit_converter/main.cpp)
- one input field
- length, weight, and temperature conversions
- grouped button rows that mimic small tool panels
## More UI Ideas You Can Build With The Current API
These are realistic with today’s widget set:
- a login form with username, password, status message, and submit button
- a settings panel with grouped labels, text inputs, and apply/reset actions
- a pomodoro timer UI driven by labels and buttons
- a color value converter for RGB, HEX, and HSL text fields
- a tiny note app with title field, body preview label, and save button
## Project Layout
```text
include/frost/
frost.hpp
core/
platform/
graphics/
ui/
examples/
demo/
calculator/
unit_converter/
src/
core/
graphics/
platform/
ui/
tests/
If you add a new widget or layout feature, update:
include/frost/frost.hppif it is publicREADME.mdso the available API stays accurateexamples/CMakeLists.txtif the change includes a runnable example
MIT


