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:
Applicationas the runtime and terminal ownerStackfor common vertical and horizontal compositionsScrollAreaandViewportfor clipped, scrollable contentSectionsandButtonsfor common structured interaction areasbuilt-in surfaces such as
Panel,TextBox,Button,ScrollingBufferView,HeaderLine, andFooterLineChoicefor small non-opaque modal promptsActionobjects and surface schedulers for responsive interactionthemes for shared colors, block shapes, and padding rules across built-in and custom controls
local surface visibility, so layouts and painting can keep inactive surfaces in the tree without allocating space for them
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:
AbstractScrollAreais a base for custom-painted scrollable surfaces. It owns scroll state, common scroll methods, and optional scroll bars, but subclasses paint their own content inonPaintArea().ScrollAreais a ready-to-use layout surface. It owns aViewport, a horizontal scroll bar, a vertical scroll bar, and the scroll corner. Applications assign one content surface withsetContentSurface().
Both layers use scrollOffset terminology and provide directional,
page, edge, and scrollIntoView() helpers. Scroll bars are configured
per orientation with ScrollBarMode.