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_coloralso takes a packed0xRRGGBB), stored as[r, g, b, a].undefdies 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
undeffor none (a look the widget then leaves out, such as a focus border). In a layout it is a'scalar'property, so#nullgivesundef. border_style,grid_border_style-
A Term::Fabulous::Enum::BorderStyle item or its name ("border_style" in Term::Fabulous::Check);
grid_border_styleonly a style with joints.undefdies 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).
undefdies 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 ),undefincluded; 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.