Skip to main content

Tabs

private readonly Tabs _tabs;
private int _view;

_tabs = new Tabs(options.Keymap)
{
Titles = [() => Loc(LocString.Installed), () => Loc(LocString.Available)],
OnSelected = index => _view = index,
};

Widgets are built in the view's constructor, so options is the ArlecchinoOptions the container hands it, and region is the region the view draws the widget into.

MemberMeaning
TitlesOne Func<string> per tab, so the strip follows the language
SelectedIndexThe index currently shown
OnSelectedFires only when the selection actually changes
IsFocusedSet by the focus ring

←→ switch, a click picks the tab under the cursor.

Drawing what is behind them

Tabs owns one row and hands the rest of the region back, which is the point of Draw returning a region:

private readonly Surface _surface;
private readonly ListBox<Mod> _installed;
private readonly ListBox<Mod> _available;

public void Draw()
{
var rest = _tabs.Draw(_surface.Content);

if (_tabs.SelectedIndex == 0)
{
_installed.Draw(rest);
}
else
{
_available.Draw(rest);
}
}

Nothing counts rows, and the same code works whatever the terminal size is.

Tabs and focus

Put the strip and the pane behind it in one ring and Tab moves between them, while ←→ stay with whichever has the cursor:

private readonly FocusRing _focus;

_focus.Add(_tabs);
_focus.Add(_installed);

OnSelected is the place to swap what the second element of the ring is, if the tabs show genuinely different widgets rather than different rows of the same one.

A worked example

samples/Arlecchino.Sample puts its widget gallery behind tabs, one page each:

dotnet run --project samples/Arlecchino.Sample -- --frame widgets 100x24