NAME

Term::Fabulous::Widget::Tabs - Pages behind a row of tabs

SYNOPSIS

use Term::Fabulous::Widget::Tabs;
use Term::Fabulous::Widget::Tabs::Page;
use Term::Fabulous::Widget::Text;

my $settings = Term::Fabulous::Widget::Tabs->new( id => 'settings' );
foreach my $section ( [ General => 'Language, time zone' ], [ Network => 'Hostname, ports' ], [ Users => 'Accounts and groups' ] ) {
	my $page = Term::Fabulous::Widget::Tabs::Page->new( title => $section->[0] );
	$page->add_child( Term::Fabulous::Widget::Text->new( text => $section->[1], text_color => '#c8cdd7' ) );
	$settings->add_child($page);
}

$settings->on( Select => sub ($event) {
	$status->text( 'Showing ' . $event->item->title );
	return;
} );

$settings->select(1);    # from the program: fires nothing

Four Tabs widgets: tabs along the top with the Network page shown and a disabled Licenses tab, a tab bar at the bottom in the Heavy style, tabs along the left side, and tabs with vertical labels without a page border

DESCRIPTION

The picture shows the Tabs widget in four forms: tabs along the top with its second page shown and a disabled tab, a bar at the bottom in another line style, tabs along the left side, and tabs with downward labels without a border around the page. The program is examples/widgets/tabs.pl.

A Tabs widget shows one of several pages at a time, chosen with the tabs of its bar. The bar is drawn as a row of boxes that join the border of the page: the active tab is open toward the page, so that it and the page are one shape, while the others are closed by the page's line and look as if they stood behind it (with active_padding, the active tab has that many more cells of padding on its page side and rises that many cells out of the row):

 ╭─────────╮ ╭─────────╮ ╭───────╮
 │ General │ │ Network │ │ Users │
╭╯         ╰─┴─────────┴─┴───────┴───────╮
│                                        │
│  Language   en                         │
│  Time zone  UTC                        │
│                                        │
╰────────────────────────────────────────╯

The bar can be on any side of the page (side), and the labels can be written from left to right or downwards (orientation), on any side; the tabs can sit at the start, in the center or at the end of the bar. The border around the page can be left out (page_border).

The user switches pages with a click on a tab, with Left, Right, Up, Down, Home and End while the active tab has the focus, and with Ctrl+PageUp and Ctrl+PageDown from anywhere inside the Tabs. Only the active tab takes the focus, so Tab walks from it into the page and on. A page can be disabled, and the Tabs fires Term::Fabulous::Event::Select for every change the user makes.

Each page is a Term::Fabulous::Widget::Tabs::Page: a box with a title and an optional icon for its tab, holding the widgets you add to it. Only the active page is part of the widget tree, so the other pages take no space and their widgets cannot take the focus. The Tabs makes a Term::Fabulous::Widget::Tabs::Button for every page in its Term::Fabulous::Widget::Tabs::Bar; the look of the tabs (side, orientation, line style, colors) is set once, on the Tabs.

A Tabs is a Term::Fabulous::Widget::Box that grows in both directions unless the layout says otherwise; add_child accepts only pages. Its children are the bar and a box that holds the shown page, so use "pages" rather than children to get at the pages.

CONSTRUCTOR

new

my $tabs = Term::Fabulous::Widget::Tabs->new(%parameters);

Accepts the parameters of "CONSTRUCTOR" in Term::Fabulous::Widget::Box (id, layout, background_color, ...), the look parameters of "CONSTRUCTOR" in Term::Fabulous::Widget::Tabs::Bar, which it passes to its bar, and page_border. All are optional; unknown parameters die. The look parameters are:

side

top (the default), right, bottom or left: the side the bar is on.

orientation

horizontal (the default) or vertical: whether the tab labels are written from left to right, or downwards.

tab_alignment

start (the default), center or end: where the tabs sit along the bar.

tab_gap
tab_margin
tab_padding

Non-negative integers, all 1 by default: the cells between two tabs, between the ends of the bar and the outer tabs, and on each side of a label along its writing direction.

active_padding

A non-negative integer. Default: 0. The cells of padding the active tab has on its page side beyond the other tabs, so that it stands that many cells taller (wider on a bar at the left or right) than the others, rising out of the row; 0 keeps all tabs the same size. See "TABS" in Term::Fabulous::Manual::Layout.

line_style

A Term::Fabulous::Enum::BorderStyle item with grid joints (Ascii, Dashed, Double, Heavy, Round or Solid), or the name of one. Default: Round. The style of the tabs' borders, of the line that closes them and of the page's border.

line_color

The color of those lines. Default: [90, 96, 110, 255], a gray.

text_color
active_text_color

The colors of the labels of the inactive tabs and of the active one. Defaults: [150, 160, 180, 255] and [220, 223, 228, 255].

active_bold

A boolean. Default: 0. Whether the active tab's label is bold.

hover_background_color

The background of an inactive tab under the pointer. Default: [40, 45, 58, 255].

focus_border_color

The color of the active tab's outline while it has the focus, or undef for none. Default: [97, 175, 239, 255], a blue.

disabled_color

The color of a disabled tab. Default: [108, 112, 120, 255].

page_border

A boolean. Default: 1. Whether the page has a border on the three sides away from the bar, in line_style and line_color; on the bar's side the bar's line closes it. Without it, only the bar's line separates the tabs from the page.

METHODS

The methods of Term::Fabulous::Widget, plus:

add_child

$tabs->add_child( $page, $other_page );

Appends pages. Dies for anything but a Term::Fabulous::Widget::Tabs::Page, and for a page that is part of a Tabs already. The first enabled page added becomes the active one, unless a page asks for it with active => 1. Returns the Tabs.

remove_child, remove_child_with_id, remove_children_with, clear_children

As in Term::Fabulous::Widget, for the pages (also those not shown); remove_child dies for anything but a widget and ignores widgets that are not pages of this Tabs. When the active page is removed, the first enabled page left becomes the active one. Fires nothing. children and has_child see the bar and the box that holds the shown page, not the pages.

pages

my @pages = $tabs->pages;

The pages, in the order of their tabs.

page

my $page = $tabs->page(2);

The page at an index, from 0. Dies for an index outside the pages.

index_of

my $index = $tabs->index_of($page);

The index of a page, or undef.

active

my $page = $tabs->active;    # or undef

The page shown now.

active_index

my $index = $tabs->active_index;    # or undef

Its index.

select

$tabs->select(1);        # by index
$tabs->select($page);    # or the page
$tabs->select(undef);    # no page

Shows a page from the program. Fires nothing. A tab that had the focus passes it on to the new active tab, and a focused widget on the page that leaves hands it to the active tab (see "focus_heir"). Dies for an index outside the pages or a page of another Tabs. Returns the Tabs.

choose

$tabs->choose($index);

Chooses a page as the user does: shows it, focuses its tab and fires Select when the active page changed. A disabled page changes nothing. Returns the Tabs.

focus_heir

my $tab = $tabs->focus_heir;

The active tab (a Term::Fabulous::Widget::Tabs::Button), or undef without pages. When the widget with the focus leaves the Tabs (its page is no longer shown, or it is removed), the focus goes to this tab when it can take the focus (else to the nearest widget above the Tabs that can), so the keys keep reaching the Tabs (see "When the focused widget goes away" in Term::Fabulous::Manual::Events and "Focus heirs" in Clay::UI::Interaction). You do not call it yourself.

bar

my $bar = $tabs->bar;

The Term::Fabulous::Widget::Tabs::Bar, for its tabs ($tabs->bar->button($index)) and their events. Change the active tab and the look through the Tabs, which keeps the pages in step.

side

$tabs->side('left');

Accessor for the side parameter. Changing it lays the Tabs out again; a tab that had the focus keeps it.

orientation

$tabs->orientation('vertical');

Accessor for the orientation parameter.

tab_alignment

$tabs->tab_alignment('center');

Accessor for the tab_alignment parameter.

tab_gap

$tabs->tab_gap(0);

Accessor for the tab_gap parameter.

tab_margin

$tabs->tab_margin(2);

Accessor for the tab_margin parameter.

tab_padding

$tabs->tab_padding(2);

Accessor for the tab_padding parameter.

active_padding

$tabs->active_padding(1);

Accessor for the active_padding parameter.

line_style

$tabs->line_style('Heavy');

Accessor for the line_style parameter; the reader returns the style item. The borders of the pages follow it.

line_color

$tabs->line_color('#5a606e');

Accessor for the line_color parameter. The reader returns [r, g, b, a]; an invalid color dies and leaves the old one, and so does undef. The borders of the pages follow it. The bar keeps the colors and the line style; $tabs->reset_look('line_color') returns them to the theme (see "reset_look" in Term::Fabulous::Widget).

text_color

$tabs->text_color('#96a0b4');

Accessor for the text_color parameter; works like "line_color".

active_text_color

$tabs->active_text_color('#ffffff');

Accessor for the active_text_color parameter; works like "line_color".

active_bold

$tabs->active_bold(1);

Accessor for the active_bold parameter. Returns 1 or 0.

hover_background_color

$tabs->hover_background_color( [ 40, 45, 58 ] );

Accessor for the hover_background_color parameter; works like "line_color".

focus_border_color

$tabs->focus_border_color(undef);

Accessor for the focus_border_color parameter; undef switches the focus look off.

disabled_color

$tabs->disabled_color('#6c7078');

Accessor for the disabled_color parameter; works like "line_color".

page_border

$tabs->page_border(0);

Accessor for the page_border parameter. Returns 1 or 0. Restyles the pages as well.

Every writer passes the value to the bar (see "METHODS" in Term::Fabulous::Widget::Tabs::Bar) and shows its change in the next frame.

search_texts, search_revision, highlight_text, clear_text_highlights, reveal_text

my @entries = $tabs->search_texts;    # ( [ [ $index, $key ], $text ], ... )
my ($text) = @{ $tabs->reveal_text( $entries[0][0], 0 ) };

The methods of Term::Fabulous::Role::Searchable, from Term::Fabulous::Role::SearchContainer: a Tabs answers for the searchable widgets of all its pages, the pages not shown included, with keys of the widget's index among them and the widget's own key (see "Nested keys" in Term::Fabulous::Role::Searchable). search_serial comes from "search_serial" in Term::Fabulous::Role::Searchable. reveal_text selects the page of the widget, without firing Select, and returns what the widget returns. The indices change when pages or their widgets are added or removed; search_revision then changes too (it names the widget at the index), so a search that compares revisions runs again.

searchable_parts, reveal_part

my @widgets = $tabs->searchable_parts;

For Term::Fabulous::Role::SearchContainer: the searchable widgets of all pages, in order, and the step that selects the page holding one of them before it reveals a text. You do not call them yourself.

KEYS

While the active tab has the focus, Left and Up choose the previous enabled tab, Right and Down the next one, wrapping around, and Home and End the first and the last; see "KEYS" in Term::Fabulous::Widget::Tabs::Bar. From anywhere inside the Tabs, also from a widget on the page:

Ctrl+PageUp, Ctrl+PageDown

Choose the previous or the next enabled page, wrapping around.

Tab and Shift+Tab move through the active tab and the widgets of the shown page in order, as everywhere. All other keys bubble to the ancestors.

MOUSE

A click on a tab shows its page and focuses the tab. An inactive tab under the pointer is painted on hover_background_color.

EVENTS

Select

Term::Fabulous::Event::Select when the user chooses another tab (or "choose" is called and the active page changes); $event->item is the page now shown and $event->index its position. Programmatic changes fire nothing. The bar's own Select, which names the tab, does not bubble past the Tabs; listen on $tabs->bar for it.

The Activate of the tabs and the events of the widgets on the pages bubble through the Tabs as well.

KDL PROPERTIES

The properties of "KDL PROPERTIES" in Term::Fabulous::Widget::Box, plus side, orientation, tab_alignment, tab_gap, tab_margin, tab_padding, active_padding and line_style (strings and numbers), active_bold and page_border (#true / #false), focus_border_color (a color string, or #null for no focus look) and the colors line_color, text_color, active_text_color, hover_background_color and disabled_color. The pages are Page child nodes (see "KDL PROPERTIES" in Term::Fabulous::Widget::Tabs::Page):

use Term::Fabulous::Widget::Tabs as Tabs
use Term::Fabulous::Widget::Tabs::Page as Page
use Term::Fabulous::Widget::Text as Text

Tabs "settings" {
	side "left"
	line_style "Solid"
	Page "general" {
		title "General"
		Text { text "Language, time zone"; }
	}
	Page "network" {
		title "Network"
		active #true
		Text { text "Hostname, ports"; }
	}
}

EXAMPLES

A sidebar of tabs

my $tabs = Term::Fabulous::Widget::Tabs->new( side => 'left', tab_alignment => 'start' );

Tabs without a frame around the page

my $tabs = Term::Fabulous::Widget::Tabs->new( page_border => 0, active_bold => 1 );

Remember the shown page

$settings->on( Select => sub ($event) {
	$config->{page} = $event->item->id;
	return;
} );

SEE ALSO

Term::Fabulous::Widget::Tabs::Page, Term::Fabulous::Widget::Tabs::Bar, Term::Fabulous::Widget::Tabs::Button, Term::Fabulous::Event::Select, Term::Fabulous::Widget::Accordion, "TABS" in Term::Fabulous::Manual::Layout.