NAME

Term::Fabulous::Role::Themed - How a widget reads its colors and borders from the theme

SYNOPSIS

use Object::Pad 0.825;

class My::Gauge :isa(Term::Fabulous::Widget::Display) :strict(params) {
	method theme_family :common () { return 'progress' }

	# fill_color: the theme's progress.color unless given; a color
	method themed_params :common () {
		return ( $class->SUPER::themed_params, fill_color => [ 'color', 'normal', 'cell_color' ] );
	}

	method fill_color (@new) {
		return @new ? $self->set_look( fill_color => $new[0] ) : $self->look_value('fill_color');
	}

	method paint () {
		my $fill = $self->color_attr( $self->fill_color );
		...
	}
}

my $gauge = My::Gauge->new( fill_color => '#ff8800' );    # explicit, wins over the theme
$gauge->reset_look('fill_color');                          # back to the theme

DESCRIPTION

Every Term::Fabulous widget composes this role (through Term::Fabulous::Widget or Term::Fabulous::Widget::Text). It holds the widget's explicit looks, the colors, border styles and borders the program set, and reads everything else from the Term::Fabulous::Theme of the UI the widget is in, for the widget's family and classes: the theme the UI's theme_for returns for the widget, which is the UI's theme unless a subclass of the UI says otherwise. A widget in no UI reads the default theme.

The looks are fetched once and kept until the theme changes ("generation, bump_generation" in Term::Fabulous::Theme) or the widget joins or leaves a tree, so reading a look while a frame is drawn costs one hash lookup.

A themed parameter is declared once, in "themed_params", with the slot it reads and its kind. The role takes a parameter of a kind from the constructor, checks every value given to "set_look" by the kind and makes it a layout property, so the widget writes only a one-line accessor. A widget that copies looks into parts it builds learns about every change in one hook, "looks_changed"; a widget whose looks live on its parts declares them in "forwarded_looks".

This page is for widget authors; "THEMES" in Term::Fabulous::Manual::Looks explains themes to users, and Term::Fabulous::Manual::CustomWidgets shows the role in a widget.

CLASS METHODS A WIDGET DEFINES

theme_family

method theme_family :common () { return 'input' }

The family whose slots the widget draws with, one of "Families, slots and states" in Term::Fabulous::Theme. A subclass inherits its parent's family unless it defines its own.

themed_params

method themed_params :common () {
	return (
		$class->SUPER::themed_params,
		accent_color           => [ 'accent',     'normal',  'cell_color' ],
		focus_background_color => [ 'background', 'focused', 'cell_color' ],
	);
}

The parameters whose value the theme supplies when the program gives none: name => [ slot, state, kind ]. The slot and state must exist in the family; the first use of the class checks that, and the kind, and dies otherwise. A subclass returns its parent's list plus its own.

A parameter with a kind may map to the slot undef: the family has no slot for it, so only an explicit value counts and "look_value" answers undef without one. Term::Fabulous::Widget declares bordered this way for the families that have no border.enabled:

my $slot = Term::Fabulous::Theme::has_slot( $class->theme_family, 'border.enabled' ) ? 'border.enabled' : undef;
...
bordered => [ $slot, 'normal', 'border_sides' ],

The kind says what a value is:

color, cell_color

A color ("color" in Term::Fabulous::Check; cell_color also takes a packed 0xRRGGBB), stored as [r, g, b, a]. undef dies with a message that names "reset_look", the way back to the theme. In a layout it is a 'color' property.

optional_color, optional_cell_color

The same, or undef for none (a look the widget then leaves out, such as a focus border). In a layout it is a 'scalar' property, so #null gives undef.

border_style, grid_border_style

A Term::Fabulous::Enum::BorderStyle item or its name ("border_style" in Term::Fabulous::Check); grid_border_style only a style with joints. undef dies as for a color. A 'scalar' layout property.

border_sides

The sides of a border ("border_sides" in Term::Fabulous::Check): one boolean for all four sides or a hash reference of booleans under the side names, stored as a hash of all four sides, 1 or 0. It maps to a boolean slot, whose 1 or 0 stands for all four sides, or to no slot (see above). undef dies as for a color. A 'border_sides' layout property.

a code reference

A check of your own, called like the functions of Term::Fabulous::Check as $check->( $widget, $name, $value ), undef included; it returns the value to keep or dies. A 'scalar' layout property.

The role takes a parameter of a kind out of the constructor's arguments and records it, checked, as an explicit value; a parameter the program left out stays with the theme. It does that before the class's own fields and ADJUST blocks run, so it calls neither the accessor nor "looks_changed": build your parts in ADJUST from "look_value". Do not declare such a parameter as a field: "not given" and "given as undef" (for a look undef switches off) stay apart this way. A layout entry of the same name in "layout_properties" in Term::Fabulous::Role::CanParseLayout replaces the one the kind gives.

Without a kind ([ slot, state ]) the class keeps and checks the value itself and the role leaves the constructor and the layout to it: Term::Fabulous::Widget keeps background_color and border_color in the Clay::UI roles, Term::Fabulous::Widget::Text its text_color. Its accessor checks the value before "set_look", and "look_reset" clears it.

forwarded_looks

method forwarded_looks :common () {
	return ( bar => [ 'Term::Fabulous::Widget::Tabs::Bar', qw(line_color text_color) ] );
}

Optional. The looks the widget keeps on parts it builds: method => [ part class, names ], where the method returns the parts (one or more) and every name is a themed parameter with a kind of the part class. Term::Fabulous::Widget::Tabs keeps its colors on its bar, Term::Fabulous::Widget::ScrollBox its scrollbar colors on both scrollbars. The role routes "set_look" (checked by the part's kind, in the widget's name, then given to every part), "look_value" and "has_look_override" (asking the first part) and "reset_look" (on every part) through the method, and makes the names layout properties of the part's kinds. The widget passes the constructor's values to the parts it builds, or calls "set_look" once they exist. The first use of the class dies for a name the part class has no kind for, a name that is also a themed parameter of the widget, or a name given twice.

METHODS A WIDGET DEFINES

look_state

method look_state () { return $self->is_focused ? 'focused' : 'normal' }

The state the widget shows now: normal, or a state its family's slots have. Term::Fabulous::Widget answers normal; a widget with states overrides it and decides the precedence (a disabled button is disabled, not focused).

look_reset

method look_reset ($name) { ... }

Called by "reset_look" for every parameter name, after the role dropped its explicit value. A class that keeps the explicit value of a parameter outside the role (Term::Fabulous::Widget keeps background_color and border_color in the Clay::UI roles) clears it here; the others do nothing.

classes

The widget's class names as an array reference; see "classes" in Term::Fabulous::Widget.

looks_changed

method looks_changed (@names) {
	$_close_button->text_color( $self->text_color );
	return;
}

Optional. Called with the names of the looks that may have changed: after "set_look" (the name), after "reset_look" (the names given), and with every look of the widget (its themed parameters and its forwarded looks) while the widget is in a UI: when the UI is created, when its theme is set to another one, when "forget_looks" runs (the widget's classes changed) and when the widget joins a tree that is in a UI (its tree_changed hook, see "tree_changed" in Term::Fabulous::Widget). Not called during construction (see "themed_params"), nor for a widget outside a UI, which reads its looks when it is drawn.

Most widgets need none, because they read their looks when a frame is drawn. A widget that copies looks into the parts it builds (Term::Fabulous::Widget::Table colors its cells, its pager and its scrollbar) colors them again here.

CLASS METHODS

themed_layout_properties

my %kind_of = My::Gauge->themed_layout_properties;    # ( fill_color => 'color' )

The layout properties the themed parameters of a kind and the forwarded looks give the class (see "themed_params"); Term::Fabulous::Role::CanParseLayout adds them to the class's own layout_properties.

METHODS

look

my $color = $self->look('accent');
my $color = $self->look( 'border.color', 'focused' );

The theme's value of a slot of the widget's family in a state (normal by default), for the widget's classes: [r, g, b, a], a Term::Fabulous::Enum::BorderStyle item, 'reverse' or undef for none, 1 or 0 for a boolean slot (border.enabled). A state the theme gives no value of its own looks like the normal state.

family_look

my $track = $self->family_look( scrollbar => 'track' );

Like "look", for a slot of another family, without the widget's classes: for a part the widget paints itself in the looks of that family, such as the scrollbar of a Term::Fabulous::Widget::TextArea. Read it while painting; a theme switch repaints a Term::Fabulous::Widget::Display by itself.

look_value

my $color = $self->look_value('accent_color');

What a themed parameter is worth: the explicit value when the program set one, else the theme's value of the slot and state the parameter maps to, or undef for a parameter that maps to no slot. A forwarded look is the first part's. Dies for a name that is neither in "themed_params" nor in "forwarded_looks".

themed_value

my $background = $self->themed_value( 'background', $state, $explicit // $self->look('background') );

The value of a slot in a state, given the value of the normal state: the explicit value of the parameter mapped to that state (such as focus_background_color), else the theme's own value for the state, else the normal value. This is how an explicit normal color stays in states the theme does not color differently.

set_look

$self->set_look( accent_color => '#61afef' );

Records an explicit value: checked by the parameter's kind (a parameter without a kind takes the value as given, checked by the caller), or, for a forwarded look, checked by the part's kind and given to every part. Marks the widget changed, calls "looks_changed" with the name and returns the value as recorded ([r, g, b, a] for a color). Dies for an unknown name.

has_look_override

if ( $self->has_look_override('accent_color') ) { ... }

Whether the program set the parameter explicitly (for a forwarded look: on the first part).

reset_look

$widget->reset_look('accent_color');
$widget->reset_look( 'border_color', 'background_color' );

Drops the explicit values of the named parameters, so the theme supplies them again (a forwarded look on every part), marks the widget changed and calls "looks_changed" with the names. Returns the widget. Dies for a name that is neither in "themed_params" nor in "forwarded_looks", before anything changes.

forget_looks

$widget->forget_looks;

Drops the fetched looks of the widget and of every widget below it; the next read fetches them from the theme of the UI the widget is in now. When the widget is in a UI, calls "looks_changed" with all its looks. Term::Fabulous calls it when a widget changes its classes. A widget that joins or leaves a tree forgets its own looks from its tree_changed hook instead, which Clay::UI calls on every widget of the moved subtree (see "tree_changed" in Term::Fabulous::Widget).

FUNCTIONS

forget_tree_looks

Term::Fabulous::Role::Themed::forget_tree_looks( $ui->root );

"forget_looks" for a node and every themed widget below it, in layout pre-order ("descendants" in Clay::UI::Role::Core::Element), also below nodes that are not themed (the rows of a grid). The UI calls it when it is created and when its theme changes.

SEE ALSO

Term::Fabulous::Theme, Term::Fabulous::Widget, Term::Fabulous::Manual::CustomWidgets.