[ API reference ]
App
Purpose and header
Section titled “Purpose and header”koto::App is the top-level runtime coordinator that owns a Terminal, a Frame, and an EventLoop, connects optional user callbacks and a FocusManager, and runs the 60 FPS event and rendering loop.
Header: koto/app.hpp (included by koto/koto.hpp).
Types and members
Section titled “Types and members”| Member | Signature | Description | Default |
|---|---|---|---|
App() | App() | Constructs m_term, m_frame(m_term), and m_loop without entering raw mode. | m_running = true, m_needs_draw = true |
~App() | ~App() = default | Destroys the application and restores terminal state via m_term.~Terminal(). | = default |
on_key() | App& on_key(std::function<void(const KeyEvent&)> handler) | Registers the callback invoked for key events not consumed by FocusManager. | nullptr |
on_paste() | App& on_paste(std::function<void(const std::string&)> handler) | Registers the callback invoked when a PasteEvent arrives. | nullptr |
on_mouse() | App& on_mouse(std::function<void(const MouseEvent&)> handler) | Registers the mouse callback and immediately calls m_term.enable_mouse_capture(). | nullptr |
on_tick() | App& on_tick(std::function<void()> handler) | Registers a parameterless periodic tick callback fired every 16 ms (~60 FPS). | nullptr |
on_tick(dt) | App& on_tick(std::function<void(double)> handler) | Registers a periodic tick callback receiving elapsed seconds dt as a double. | nullptr |
on_render() | App& on_render(std::function<void(Frame&)> handler) | Registers the callback that draws widgets into the frame back buffer. | nullptr |
use_focus() | App& use_focus(FocusManager& fm) | Attaches fm, calls m_term.enable_mouse_capture(), syncs geometry after each draw, and routes input events through fm. | nullptr |
quit() | void quit() | Sets m_running = false and calls m_loop.wakeup() to exit run(). | None |
request_redraw() | void request_redraw() noexcept | Sets m_needs_draw = true and wakes the event loop via m_loop.wakeup(). | None |
run() | auto run() -> std::expected<void, TerminalErrorInfo> | Enters raw mode, hides the cursor, runs the event/render loop until quit() or EOF, and restores the terminal. | None |
run(render_function) | auto run(std::function<void(Frame&)> render_function) -> std::expected<void, TerminalErrorInfo> | Calls on_render(std::move(render_function)) and then run(). | None |
area() | Area area() const noexcept | Returns m_frame.area() (Area{0, 0, cols, rows}). | {0, 0, 80, 24} fallback |
hit_at(row, col) | Id hit_at(int row, int col) const | Queries the hit buffer at (row, col) via m_frame.hit_at(row, col). | Id::none() |
hit_at(mouse) | Id hit_at(const MouseEvent& mouse) const | Queries m_frame.hit_at(mouse.row, mouse.col). | Id::none() |
draw_frame() | void draw_frame(bool flush = true) | Clears the back buffer, invokes on_render, calls fm.sync_geometry(m_frame), and calls m_frame.render(flush). | flush = true |
dispatch_event() | void dispatch_event(const Event& ev) | Routes ev (ResizeEvent, PasteEvent, MouseEvent, or KeyEvent) to FocusManager and registered callbacks. | None |
frame() | Frame& frame() noexcept / const Frame& frame() const noexcept | Returns a mutable or const reference to the internal frame. | None |
Event dispatch order in App::dispatch_event
Section titled “Event dispatch order in App::dispatch_event”| Event variant | Dispatch behavior |
|---|---|
ResizeEvent | Updates m_term.set_dimensions(rows, cols), calls m_frame.handle_resize(rows, cols), and sets m_needs_draw = true. Does not invoke user callbacks. |
PasteEvent | 1. If on_paste is set, calls m_paste_handler(paste->text) and returns. 2. Else if m_focus_manager && m_focus_manager->handle_paste(paste->text) returns true, returns. 3. Else iterates codepoints with next_codepoint, synthesizes a KeyEvent for each codepoint with cp >= 32 or cp == '\t' (setting shift = true for 'A'..'Z'), and routes each through m_focus_manager->handle_key(ke) then m_key_handler(ke). Never synthesizes Key::Enter. |
MouseEvent | Copies the event, populates m.target = m_frame.hit_at(m.row, m.col), invokes m_mouse_handler(m) if registered, and then invokes m_focus_manager->handle_mouse(m, m_frame) if use_focus was called. |
KeyEvent | Calls m_focus_manager->handle_key(*key) first if attached; only invokes m_key_handler(*key) if the FocusManager did not consume the key. |
Examples
Section titled “Examples”#include <koto/koto.hpp>#include <cstdio>
using namespace koto;
int main(){ App app; TextInputState name_input("koto"); FocusManager fm; fm.add(name_input); app.use_focus(fm);
app.on_key([&](const KeyEvent& key) { if (key.is(Key::Escape) || (key.ctrl && key.ch == 'C')) { app.quit(); } });
app.on_render([&](Frame& frame) { TextInput(name_input) .title("Project") .border_style(BorderStyle::Rounded) .draw(frame, Layout::center(frame.area(), 30, 3)); });
if (auto res = app.run(); !res) { std::fprintf(stderr, "Error: %.*s\n", static_cast<int>(res.error().message().size()), res.error().message().data()); return 1; } return 0;}Headless frame and event verification
Section titled “Headless frame and event verification”app.on_render([&](Frame& f) { Box().title("Headless").id(Id("panel")).draw(f, Area(0, 0, 20, 4));});app.draw_frame(false);Id hit = app.hit_at(1, 1);static_cast<void>(hit);Errors and edge cases
Section titled “Errors and edge cases”- Non-TTY execution:
App::run()checksm_term.is_tty()before entering raw mode. If standard input or standard output is piped or redirected,run()immediately returnsstd::unexpected(TerminalErrorInfo{TerminalError::NotATty, ENOTTY}). - No default quit key:
Appdoes not bindq,Escape, orCtrl+C(raw mode disablesISIG). You must callapp.quit()in anon_keyoron_mousehandler, or let standard input reach EOF. - No
on_resizecallback:ApphandlesResizeEventinternally by resizingm_termandm_frameand markingm_needs_draw = true, which triggerson_renderon the same loop iteration. - Cross-thread wakeups:
Appdoes not expose awakeup()method directly; callapp.request_redraw()from background worker threads to mark the frame dirty and wake the internalEventLoopself-pipe.