Skip to main content

Overview

LibGUI is SerenityOS’s native GUI toolkit providing widgets, windows, layouts, and event handling for building graphical applications. It offers a rich set of controls from basic buttons to complex table views, all with a clean object-oriented API.
LibGUI applications automatically integrate with the SerenityOS WindowServer for window management, theming, and input handling.

Getting Started

Application

Every GUI application starts with a GUI::Application instance.
#include <LibGUI/Application.h>
#include <LibGUI/Window.h>
#include <LibGUI/Button.h>
#include <LibMain/Main.h>

ErrorOr<int> serenity_main(Main::Arguments arguments)
{
    auto app = TRY(GUI::Application::create(arguments));
    
    auto window = GUI::Window::construct();
    window->set_title("My Application");
    window->resize(300, 200);
    
    auto button = window->set_main_widget<GUI::Button>("Click me!");
    button->on_click = [&](auto) {
        outln("Button clicked!");
    };
    
    window->show();
    return app->exec();
}
  1. Create Application: GUI::Application::create()
  2. Build UI: Create windows and widgets
  3. Show Windows: Call window->show()
  4. Run Event Loop: Call app->exec()
  5. Cleanup: Automatic when app exits

Windows

Window

Top-level window container.
#include <LibGUI/Window.h>

auto window = GUI::Window::construct();

// Basic properties
window->set_title("My Window");
window->set_rect(100, 100, 640, 480); // x, y, width, height
window->resize(800, 600);
window->center_on_screen();

// Window modes
window->set_resizable(true);
window->set_minimizable(true);
window->set_closeable(true);
window->set_frameless(false);

// Window states
window->set_maximized(true);
window->set_minimized(false);
window->set_fullscreen(false);

// Set main widget
auto main_widget = window->set_main_widget<GUI::Widget>();

// Show/hide
window->show();
window->hide();
auto window = GUI::Window::construct();
window->set_title("Hello");
window->resize(400, 300);
window->set_main_widget<GUI::Label>("Hello, World!");
window->show();
Key Properties:
title
String
Window title displayed in title bar
resizable
bool
Whether window can be resized by user
modal
bool
If true, blocks interaction with other windows
fullscreen
bool
Fullscreen mode (hides title bar and decorations)

Core Widgets

Widget

Base class for all GUI widgets.
#include <LibGUI/Widget.h>

auto widget = GUI::Widget::construct();

// Size constraints
widget->set_min_size(100, 50);
widget->set_max_size(500, 300);
widget->set_fixed_size(200, 100);
widget->set_fixed_width(150);
widget->set_fixed_height(75);

// Visibility
widget->set_visible(true);
widget->set_enabled(true);

// Focus
widget->set_focus_policy(GUI::FocusPolicy::StrongFocus);
widget->set_focus(true);

// Tooltips
widget->set_tooltip("This is a helpful tooltip"sv);

// Background
widget->set_fill_with_background_color(true);
widget->set_background_role(Gfx::ColorRole::Base);

Button

Clickable button widget.
#include <LibGUI/Button.h>

auto button = GUI::Button::construct("Click me!");

// Handle clicks
button->on_click = [](auto) {
    dbgln("Button was clicked!");
};

// Checkable button
button->set_checkable(true);
button->set_checked(false);
button->on_checked = [](bool checked) {
    dbgln("Button checked: {}", checked);
};

// Icon
button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/app.png").release_value_but_fixme_should_propagate_errors());

Label

Text display widget.
#include <LibGUI/Label.h>

auto label = GUI::Label::construct("Hello, World!");

// Text alignment
label->set_text_alignment(Gfx::TextAlignment::Center);

// Word wrap
label->set_word_wrap(true);

// Update text
label->set_text("New text"sv);

TextBox & TextEditor

Single-line and multi-line text input.
#include <LibGUI/TextBox.h>
#include <LibGUI/TextEditor.h>

// Single-line input
auto textbox = GUI::TextBox::construct();
textbox->set_placeholder("Enter text..."sv);
textbox->on_change = [&]() {
    dbgln("Text: {}", textbox->text());
};

// Multi-line editor
auto editor = GUI::TextEditor::construct();
editor->set_text("Initial content"sv);
editor->set_mode(GUI::TextEditor::Mode::MultiLine);
editor->set_wrapping_mode(GUI::TextEditor::WrappingMode::WrapAtWords);

CheckBox & RadioButton

Toggle controls.
#include <LibGUI/CheckBox.h>
#include <LibGUI/RadioButton.h>

auto checkbox = GUI::CheckBox::construct("Enable feature");
checkbox->set_checked(true);
checkbox->on_checked = [](bool checked) {
    dbgln("Checked: {}", checked);
};

auto radio1 = GUI::RadioButton::construct("Option 1");
auto radio2 = GUI::RadioButton::construct("Option 2");
// Radio buttons in same parent auto-group

ComboBox

Dropdown selection widget.
#include <LibGUI/ComboBox.h>

Vector<String> options = { "Red"_string, "Green"_string, "Blue"_string };
auto combobox = GUI::ComboBox::construct();
combobox->set_model(*GUI::ItemListModel<String>::create(options));
combobox->set_selected_index(0);

combobox->on_change = [&](auto& text, auto& index) {
    dbgln("Selected: {} (index {})", text, index.row());
};

Slider & SpinBox

Numeric input controls.
#include <LibGUI/Slider.h>
#include <LibGUI/SpinBox.h>

auto slider = GUI::HorizontalSlider::construct();
slider->set_range(0, 100);
slider->set_value(50);
slider->on_change = [](int value) {
    dbgln("Slider: {}", value);
};

auto spinbox = GUI::SpinBox::construct();
spinbox->set_range(0, 100);
spinbox->set_value(10);

Container Widgets

GroupBox

Visual grouping with optional title.
#include <LibGUI/GroupBox.h>

auto groupbox = GUI::GroupBox::construct("Options");
auto& layout = groupbox->set_layout<GUI::VerticalBoxLayout>();
layout.set_margins(10);

groupbox->add<GUI::CheckBox>("Option 1");
groupbox->add<GUI::CheckBox>("Option 2");

Frame

Decorative frame container.
#include <LibGUI/Frame.h>

auto frame = GUI::Frame::construct();
frame->set_frame_style(Gfx::FrameStyle::SunkenContainer);
frame->set_thickness(2);

TabWidget

Multi-page tabbed interface.
#include <LibGUI/TabWidget.h>

auto tabs = GUI::TabWidget::construct();

auto& page1 = tabs->add_tab<GUI::Widget>("Page 1");
page1.set_layout<GUI::VerticalBoxLayout>();
page1.add<GUI::Label>("Content 1");

auto& page2 = tabs->add_tab<GUI::Widget>("Page 2");
page2.set_layout<GUI::VerticalBoxLayout>();
page2.add<GUI::Label>("Content 2");

SplitterWidget

Resizable split view.
#include <LibGUI/Splitter.h>

auto splitter = GUI::HorizontalSplitter::construct();

auto& left = splitter->add<GUI::Widget>();
left.set_layout<GUI::VerticalBoxLayout>();
// ... add widgets to left ...

auto& right = splitter->add<GUI::Widget>();
right.set_layout<GUI::VerticalBoxLayout>();
// ... add widgets to right ...

Layouts

VerticalBoxLayout & HorizontalBoxLayout

Stack widgets vertically or horizontally.
#include <LibGUI/BoxLayout.h>

auto widget = GUI::Widget::construct();
auto& layout = widget->set_layout<GUI::VerticalBoxLayout>();

// Layout properties
layout.set_spacing(5);
layout.set_margins({ 10, 10, 10, 10 }); // top, right, bottom, left

// Add widgets
widget->add<GUI::Label>("Top");
widget->add<GUI::Button>("Middle");
widget->add<GUI::Label>("Bottom");

// Spacers
layout.add_spacer(); // Flexible space
layout.add_layout<GUI::HorizontalBoxLayout>(); // Nested layout
auto container = GUI::Widget::construct();
auto& layout = container->set_layout<GUI::VerticalBoxLayout>();
layout.set_margins(10);
layout.set_spacing(5);

container->add<GUI::Label>("Header");
container->add<GUI::TextBox>();
container->add<GUI::Button>("Submit");

Advanced Widgets

TableView & ListView

Display tabular and list data.
#include <LibGUI/TableView.h>
#include <LibGUI/Model.h>

class MyModel : public GUI::Model {
public:
    virtual int row_count(GUI::ModelIndex const&) const override {
        return m_data.size();
    }
    
    virtual int column_count(GUI::ModelIndex const&) const override {
        return 2; // Name and Age columns
    }
    
    virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override {
        if (role == GUI::ModelRole::Display) {
            auto& row = m_data[index.row()];
            return index.column() == 0 ? row.name : String::number(row.age);
        }
        return {};
    }
    
private:
    struct Person { String name; int age; };
    Vector<Person> m_data;
};

auto table = GUI::TableView::construct();
table->set_model(adopt_ref(*new MyModel()));
table->set_column_headers_visible(true);

TreeView

Hierarchical tree display.
#include <LibGUI/TreeView.h>

auto tree = GUI::TreeView::construct();
tree->set_model(/* your tree model */);
tree->set_should_fill_selected_rows(true);

tree->on_selection_change = [&]() {
    auto index = tree->selection().first();
    dbgln("Selected row {}", index.row());
};

Dialogs

MessageBox

Simple message dialogs.
#include <LibGUI/MessageBox.h>

GUI::MessageBox::show(window, 
    "File saved successfully!",
    "Success",
    GUI::MessageBox::Type::Information);

auto result = GUI::MessageBox::show(window,
    "Delete this file?",
    "Confirm",
    GUI::MessageBox::Type::Warning,
    GUI::MessageBox::InputType::YesNo);
    
if (result == GUI::MessageBox::ExecResult::Yes) {
    // Delete file
}

FilePicker

File selection dialogs.
#include <LibGUI/FilePicker.h>

auto result = GUI::FilePicker::get_open_filepath(window,
    "Select file",
    "/home/anon",
    false); // allow_multiple

if (result.has_value()) {
    auto path = result.value();
    dbgln("Selected: {}", path);
}

// Save dialog
auto save_path = GUI::FilePicker::get_save_filepath(window,
    "Save as",
    "untitled.txt",
    "/home/anon");

Actions & Menus

Action

Reusable command with icon, text, and shortcut.
#include <LibGUI/Action.h>

auto open_action = GUI::Action::create(
    "&Open...",
    { Mod_Ctrl, Key_O },
    Gfx::Bitmap::load_from_file("/res/icons/16x16/open.png").release_value_but_fixme_should_propagate_errors(),
    [&](auto&) {
        // Open file dialog
    }
);

// Common actions
auto quit_action = GUI::CommonActions::make_quit_action([](auto&) {
    GUI::Application::the()->quit();
});
Application menus.
auto menubar = GUI::Menubar::construct();

auto& file_menu = menubar->add_menu("&File");
file_menu.add_action(open_action);
file_menu.add_action(save_action);
file_menu.add_separator();
file_menu.add_action(quit_action);

auto& edit_menu = menubar->add_menu("&Edit");
edit_menu.add_action(GUI::CommonActions::make_undo_action([](auto&) {}));
edit_menu.add_action(GUI::CommonActions::make_redo_action([](auto&) {}));

window->set_menubar(move(menubar));

Events

Event Handling

Override event handlers in custom widgets.
class MyWidget : public GUI::Widget {
    C_OBJECT(MyWidget)
public:
    virtual void paint_event(GUI::PaintEvent& event) override {
        GUI::Painter painter(*this);
        painter.fill_rect(rect(), Gfx::Color::White);
        // Custom painting...
    }
    
    virtual void mousedown_event(GUI::MouseEvent& event) override {
        dbgln("Clicked at {}, {}", event.x(), event.y());
    }
    
    virtual void keydown_event(GUI::KeyEvent& event) override {
        if (event.key() == Key_Return) {
            // Handle Enter key
        }
    }
    
    virtual void resize_event(GUI::ResizeEvent& event) override {
        dbgln("Resized to {}x{}", event.size().width(), event.size().height());
    }
};

Best Practices

  • Use layouts instead of manual positioning
  • Set size constraints (min/max) instead of fixed sizes when possible
  • Use spacers to control widget spacing
  • Nest layouts for complex UIs
// Widgets are reference counted
auto button = GUI::Button::construct(); // RefPtr<Button>

// Adding to parent transfers ownership
container->add_child(button); // container now owns button

// Use C_OBJECT macro in custom widgets
class MyWidget : public GUI::Widget {
    C_OBJECT(MyWidget) // Provides construct() and create()
private:
    MyWidget() = default; // Constructor must be private
};
  • Use semantic colors via Gfx::ColorRole
  • Don’t hardcode colors - respect system theme
  • Use standard icons from /res/icons/
  • Follow SerenityOS HIG (Human Interface Guidelines)

Build docs developers (and LLMs) love