UI Framework

The erbsland::cterm::ui module lets you build full-screen terminal applications from a structured surface tree instead of manually repainting one big buffer. Pages, layouts, and surfaces describe the UI declaratively, while Application takes care of layout passes, paint invalidation, key routing, and timed actions.

At a glance, the UI framework gives you:

Important

The UI Framework is currently in beta, and parts of the API may change in future releases.

Quick Start

The umbrella include <erbsland/cterm/ui/all.hpp> re-exports the layout and surface namespaces into ui. This lets you write compact code such as ui::Stack or ui::TextBox directly.

For very small programs, create an Application instance directly, build one page, assign it with Application::setMainPage(), and call Application::run().

using namespace erbsland::cterm;
using namespace erbsland::cterm::ui;

auto page = Page::create();
auto root = Stack::create(Orientation::Vertical);
auto title = TextBox::create("Library Status", Alignment::Center);
auto body = Panel::create();

title->editLayoutMetrics().setFixedHeight(1);
body->addSurface(TextBox::create("Everything is running.", Alignment::Center));

root->addSurface(title);
root->addSurface(body);
page->addSurface(root);

auto app = Application{};
app.setTheme(theme::Theme::dark());
app.setMainPage(page);
return app.run();

Applications with command line handling, data loading, or reusable UI setup usually derive from Application. Override Application::setupUi() to build the surface tree, and override Application::processCommandLineArguments() when arguments select data or options for that tree.

class DemoApp final : public Application {
public:
    DemoApp(const int argc, char *argv[]) : Application(argc, argv) {}

protected:
    void setupUi() override {
        _page = Page::create();
        _body = TextBox::create("", Alignment::Center);
        _page->addSurface(_body);
        setMainPage(_page);
    }

    auto processCommandLineArguments(const CommandLineArgs &args) -> ExitCode override {
        _body->setText(args.size() > 1 ? String{args[1]} : String{"Ready"});
        return cExitCodeContinue;
    }

private:
    PagePtr _page;
    TextBoxPtr _body;
};

auto main(const int argc, char *argv[]) -> int {
    return DemoApp{argc, argv}.run();
}

The startup sequence is:

  • setupUi() builds the page and surface structure.

  • initializeTerminal() prepares the terminal.

  • processCommandLineArguments() parses arguments and can assign data to the prepared UI surfaces.

  • initialize() creates the display, event driver, and scheduler infrastructure.

  • runEventLoop() processes input, scheduled actions, layout, and painting until the app quits.

Painting Model

Each surface paints only its own content in local coordinates. The display walks the surface tree, skips hidden surfaces, clips child surfaces to their parents, and repaints only visible dirty regions. Custom surfaces should use context.surfaceRect() for their full local bounds, context.visibleRect() for the ancestor-clipped area, and context.dirtyRect() for the current repaint region. For themed painting, use context.theme() to get a ThemeAccessor. From there, forPart(), style(), color(), block(), and contentRect() resolve values for the current surface. These helpers use the current ThemeContext for the surface being painted.

Hidden surfaces remain part of the tree, keep their last assigned rectangle, and still own their actions and scheduled callbacks. They are ignored by layout, paint traversal, and focus routing until shown again.

Scrollable Content

The UI framework has two scroll layers:

  • AbstractScrollArea is a base for custom-painted scrollable surfaces. It owns scroll state, common scroll methods, and optional scroll bars, but subclasses paint their own content in onPaintArea().

  • ScrollArea is a ready-to-use layout surface. It owns a Viewport, a horizontal scroll bar, a vertical scroll bar, and the scroll corner. Applications assign one content surface with setContentSurface().

Both layers use scrollOffset terminology and provide directional, page, edge, and scrollIntoView() helpers. Scroll bars are configured per orientation with ScrollBarMode.