[ Widgets ]
Tabs
koto::Tabs renders a horizontal strip of tab titles from either a mutable koto::TabsState state struct or a std::vector<std::string> (Header: koto/tabs.hpp).
Quick example
Section titled “Quick example”TabsState tabs({"Overview", "Metrics", "Settings"}, 0);
Tabs(tabs) .style(TabsStyle::Bar) .active_fg(Color::Black) .active_bg(Color::Cyan) .draw(frame, tabs_area); Overview │ Metrics │ SettingsBehavior
Section titled “Behavior”- Automatic hit-buffer tagging:
Tabs::draw()setseffective_id = !m_id.is_none() ? m_id : (m_state ? Id(m_state) : Id::none())and callsframe.tag_area(area, effective_id)wheneffective_idis notId::none()(src/tabs.cpp:293-297). - Three visual styles (
TabsStyle):TabsStyle::Bar: Pads each title with" "on each side (" Title ") and drawsm_separator("│"by default) inm_separator_fg/m_inactive_bgbetween tabs.TabsStyle::Pill: Wraps the active tab in"[ Title ]"and inactive tabs in" Title ", separated by a single" "space.TabsStyle::Underline: Pads each title as" Title "oninner.rowseparated by" ", and ifinner.height >= 2, draws an underline bar oninner.row + 1usingU'━'inm_active_bgunder the active tab andU'─'inm_separator_fgunder all other columns (src/tabs.cpp:431-445).
- Sub-tab mouse geometry (
rendered_tabs): When bound to aTabsState,Tabs::draw()populatesm_state->rendered_tabswith aTabRect{start_col, end_col, inner.row, inner.bottom()}for each visible tab soTabsState::handle_mouse()can determine which tab was clicked (src/tabs.cpp:396-404).
Keys and mouse
Section titled “Keys and mouse”Handled by TabsState::handle_event(const KeyEvent&) and TabsState::handle_mouse(const MouseEvent&):
| Input | Condition | Action |
|---|---|---|
key.is(Key::Right) or key.ch == 'l' | focused && !titles.empty() | Calls select_next() (wraps circularly to 0 after last tab) and returns true. |
key.is(Key::Left) or key.ch == 'h' | focused && !titles.empty() | Calls select_prev() (wraps circularly to titles.size() - 1 from 0) and returns true. |
key.is(Key::Home) | focused && !titles.empty() | Calls select_first() (selected = 0) and returns true. |
key.is(Key::End) | focused && !titles.empty() | Calls select_last() (selected = titles.size() - 1) and returns true. |
'1' .. '9' (key.ch >= '1' && key.ch <= '9') | focused && !titles.empty() && !key.ctrl && !key.alt and (key.ch - '1') < titles.size() | Calls set_selected(static_cast<size_t>(key.ch - '1')) and returns true. |
MouseButton::WheelUp / MouseButton::WheelDown | !titles.empty() | Calls select_prev() / select_next() and returns true. |
MouseAction::Press + MouseButton::Left | mouse.row and mouse.col fall inside rendered_tabs[i] | Calls set_selected(i) and returns true. |
koto::TabsState satisfies FocusableState and HasMouseHandler:
| Member | Type / Signature | Description | Default |
|---|---|---|---|
titles | std::vector<std::string> | Tab label strings. | {} |
selected | size_t | 0-based active tab index. | 0 |
focused | bool | Whether handle_event processes keyboard input and Tabs draws a focused border. | true |
on_change | std::function<void(size_t, const std::string&)> | Callback fired whenever select_next, select_prev, select_first, select_last, or set_selected changes or sets selection. | nullptr |
rendered_tabs | std::vector<TabRect> | Per-tab hit rectangles populated by Tabs::draw(). | {} |
TabsState() / TabsState(titles, selected) | Constructors | Constructs empty or initialized state (selected clamped to titles.size() - 1). | initial_selected = 0 |
select_next() / select_prev() / select_first() / select_last() / set_selected(index) | void | Updates selected (set_selected ignores index >= titles.size()) and invokes on_change. | None |
selected_title() | std::optional<std::string_view> selected_title() const | Returns active tab title view, or std::nullopt if empty. | std::nullopt |
selected_title_ptr() | const std::string* selected_title_ptr() const | Returns pointer to active tab title string, or nullptr if empty. | nullptr |
handle_event(key) / handle_mouse(mouse) | bool | Handles keyboard or mouse input and returns true if consumed. | None |
Methods
Section titled “Methods”| Method | Description | Default |
|---|---|---|
Tabs() | Default-constructs a stateless Tabs widget. | m_state = nullptr |
Tabs(TabsState& state) | Explicit constructor binding Tabs to state. | None |
Tabs(std::vector<std::string> titles) | Explicit constructor for stateless rendering with titles. | m_selected = 0 |
selected(size_t index) | Sets active tab index (and calls m_state->set_selected(index) if bound to a TabsState). | 0 |
style(TabsStyle s) | Sets visual style (TabsStyle::Bar, TabsStyle::Pill, TabsStyle::Underline). | TabsStyle::Bar |
separator(std::string_view sep) | Sets separator string drawn between tabs in TabsStyle::Bar. | "│" |
separator_fg(Color color) | Sets foreground color of Bar separators and Underline inactive rule (─). | Color::DarkGray |
active_fg(Color color) | Sets foreground color of the active tab label. | Color::Black |
active_bg(Color color) | Sets background color of the active tab label (and foreground of the active ━ underline in TabsStyle::Underline). | Color::Cyan |
inactive_fg(Color color) | Sets foreground color of inactive tab labels. | Color::White |
inactive_bg(Color color) | Sets background color of inactive tab labels, separators, and underline row. | COLOR_DEFAULT |
border_style(BorderStyle border) | Sets optional outer border style (None draws no border). | BorderStyle::None |
title(std::string_view title) | Sets outer box border title (used when border_style != BorderStyle::None). | "" |
title_align(TitleAlign align) | Sets outer box border title alignment. | TitleAlign::Left |
focused(bool is_focused) | Overrides m_state->focused for border highlighting. | Uses m_state->focused |
focused_border_fg(Color color) | Sets outer border color when focused. | Color::Cyan |
id(Id tag_id) | Overrides automatic Id(m_state) hit-buffer tag. | Id::none() |
draw(Frame& frame, Area area) | Renders the tab bar into frame within area. | None |
Gotchas
Section titled “Gotchas”TabsStyle::Underlinerequires at least 2 inner rows: The underline bar (━/─) is drawn oninner.row + 1only wheninner.height >= 2(src/tabs.cpp:431). If you also enable.border_style(...), the widget needsarea.height >= 4(2border rows +2inner rows).- In
TabsStyle::Underline, the active underline color comes fromactive_bg: On row 1 ofTabsStyle::Underline, the active tab’sU'━'glyph is drawn with foregroundm_active_bg(Color::Cyanby default), while the active tab text on row 0 usesm_active_fg(Color::Blackby default) on backgroundm_active_bg(src/tabs.cpp:367-368, 438).