Tickit::Widget(3pm)
๐ Tickit::Widget โ from the Tickit::Widget(3pm) manual, bestie edition ๐
User Contributed Perl Documentation ยท the Tickit::Widget page ยท ok so sit down, grab a strawberry, we're doing this ๐
โจ NAME โจ
"Tickit::Widget" โ the abstract base class for on-screen widgets. it's giving "everybody's parent", no cap. ๐
๐ธ DESCRIPTION ๐ธ
okay so, real talk: this class is an abstract base class for on-screen widget objects. it's the scaffolding, the bones, the behind-the-scenes machinery that most or all widget types need. it does not do the cute stuff itself, it just makes the cute stuff possible. ๐
you literally cannot construct objects directly in this class. nope. instead, you make a subclass of this class that gives a suitable implementation of the render_to_rb method and the other provided methods, and then you construct instances in that class. that's the whole vibe.
peep the EXAMPLES section below for the receipts. ๐
the core Tickit distribution only ships a couple of simple widget classes โ that's it, that's the roster. but so many more widget types live on CPAN. and honestly? for basically any widget-based program you're gonna want to install the Tickit::Widgets distribution at minimum, because it hands you many of the basic UI types of widget. fr fr. ๐ซถ
๐ STYLE ๐
these are the style tags used on all widget classes that use Style:
:focusโ gets set when this widget has the input focus. the main character moment, lowkey. โจ
and these are the style actions in play:
focus_next_before(<Tab>)focus_next_after(<S-Tab>)
both of these request that the focus move to the next or previous focusable widget in display order. tabbing around like it's a group chat. ๐ฌ
๐ฆ CONSTRUCTOR ๐ฆ
new
$widget = Tickit::Widget->new( %args );
constructs a shiny new Tickit::Widget object. ๐ must be called on a subclass that implements the required methods โ go check the SUBCLASS METHODS section below, bestie, don't skip it.
any pen attributes present in %args get used to set the default values on the widget's pen object. except for these three, who have their own whole thing going on:
class=> STRINGclasses=> ARRAY of STRING
if present, gives the Tickit::Style class name (or names) applied to this widget. pick your aesthetic. ๐จ
style=> HASH
if present, gives a set of "direct applied" style to the Widget. this is treated as an extra set of style definitions that apply more directly than any of the style classes or the default definitions. it's the loudest voice in the room, basically.
the hash should contain style keys, optionally suffixed by style tags, giving values. like:
style => {
'fg' => 3,
'fg:active' => 5,
}
๐ METHODS ๐
style_classes
@classes = $widget->style_classes;
returns a list of the style class names this Widget has. that's it, that's the method. short and iconic. โญ
set_style_tag
$widget->set_style_tag( $tag, $value );
sets the (boolean) state of the named style tag. after you call this, the get_style_* methods may start returning different results ๐ โ so heads up.
no resizing or redrawing is necessarily performed though. but the widget can use style_reshape_keys, style_reshape_textwidth_keys or style_redraw_keys to declare which style keys should cause automatic reshaping or redrawing. plus it can override the on_style_changed_values method to inspect the changes and decide for itself. autonomy! we love to see it. ๐ซถ
get_style_values
@values = $widget->get_style_values( @keys );
$value = $widget->get_style_values( $key );
returns a list of values for the given keys of the currently-applied style. for more detail, go read the Tickit::Style documentation (it's the source of truth, not me ๐). and in scalar context? it returns just one value. one. single. value. โจ
get_style_pen
$pen = $widget->get_style_pen( $prefix );
a shortcut to calling get_style_values to collect up the pen attributes, and form a Tickit::Pen::Immutable object out of them. cute, efficient, we stan. ๐
if $prefix is supplied, it gets prefixed onto the pen attribute names with an underscore โ which, plot twist, would be read from the stylesheet file as a hyphen. iykyk.
note that the returned pen instance is immutable, and may be cached. so don't go trying to change it, bestie, it's not gonna work. ๐
get_style_text
$text = $widget->get_style_text;
a shortcut to calling get_style_values for a single key called text. that's the whole thing. simple. clean. iconic. ๐
set_style
$widget->set_style( %defs );
changes the widget's direct-applied style. glow-up incoming. โจ
%defs should contain style keys optionally suffixed with tags, in the same form as that given to the style key to the constructor. defined values will add to or replace values already stored by the widget. and keys mapping to undef? those get deleted from the stored style. poof. gone. ๐
ok important one: changing the direct applied style is moderately costly ๐ญ because it has to invalidate all of the cached style values and pens that depend on the changed keys. for normal runtime changes of style, consider using a tag if you possibly can โ because style caching takes tags into account, and simply changing applied style tags does not invalidate the caches. tags are the cheat code. ๐ฅ
set_window
$widget->set_window( $window );
sets the Tickit::Window for the widget to draw on. setting undef removes the window. bye window ๐
if a window is associated to the widget, that window's pen is set to the current widget pen. the widget is then drawn to the window by calling the render_to_rb method. but if a window is removed (by setting undef) then no cleanup of the window is performed โ the new owner of the window is expected to do that. it's their problem now, not yours. ๐
this method may invoke the window_gained and window_lost methods. ๐ฃ
window
$window = $widget->window;
returns the current window of the widget, if one has been set using set_window. if not... you get nothing, bestie. ๐ธ
set_parent
$widget->set_parent( $parent );
sets the parent widget; pass undef to remove the parent.
$parent, if defined, must be a subclass of Tickit::ContainerWidget. no random objects as parents, ok? we have standards. ๐
parent
$parent = $widget->parent;
returns the current container widget. your parent, literally. ๐ซถ
resized
$widget->resized;
provided for subclasses to call when their size requirements have or may have changed. it re-calculates the size requirements by calling lines and cols again, then calls set_requested_size. it's the "i changed, deal with it" method. ๐
set_requested_size
$widget->set_requested_size( $lines, $cols );
provided for subclasses to call when their size requirements have or may have changed. it informs the parent that the widget requires a differently-sized window if the dimensions are now different to last time. if they're the same, nothing happens. no drama. ๐
requested_size
( $lines, $cols ) = $widget->requested_size;
returns the requested size of the widget โ its preferred dimensions. ๐ this method calls lines and cols and caches the result until the next call to resized.
container widgets: use this method in preference to callinglinesandcolsdirectly. do not skip the middleman, the middleman is there for a reason. ๐
requested_lines
$lines = $widget->requested_lines;
requested_cols
$cols = $widget->requested_cols;
returns one or other of the requested dimensions. these are shortcuts for calling requested_size.
ngl these are temporary convenience methods to assist container widgets during the transition to the new sizing model. so use them, love them, but know they're on a journey. ๐ถโ๏ธโจ
redraw
$widget->redraw;
clears the widget's window, then invokes the render method. this should completely redraw the widget. full refresh, no crumbs. ๐ฅ
and here's the tea: this redraw doesn't happen immediately. the widget is marked as needing to redraw, and its parent is marked that it has a child needing redraw โ recursively all the way up to the root widget. ๐ญ these then get flushed out down the widget tree using an Tickit "later" call.
why tho? because it lets other widgets register a requirement to redraw, and have them all flushed in a fairly efficient manner. batch processing, but make it cute. ๐
pen
$pen = $widget->pen;
returns the widget's Tickit::Pen. that's the one. ๐๏ธโจ
take_focus
$widget->take_focus;
calls take_focus on the Widget's underlying Window, if present โ or, if there's no window yet, it stores that the window should take focus when one is eventually set by set_window. forward planning, we love her. ๐
โ ๏ธ this may only be called on Widget subclasses that override CAN_FOCUS to return a true value. otherwise, no focus for you. ๐ซ
...okay bestie don't go anywhere, there's more of this page coming ๐
๐ SUBCLASS METHODS ๐
Okay so bestie, real talk: this is an abstract class. You literally can't just make one of these and vibe. The constructor has to be called on a subclass, and that subclass has to actually implement the methods below. No methods, no widget, no slay. ๐ญ
๐จ render_to_rb
$widget->render_to_rb( $renderbuffer, $rect )
This one gets called when it's time to redraw the widget's content into the given Tickit::RenderBuffer.
You'll be handed the clipping rectangle region to be rendered as a Tickit::Rect. And here's the cute part โ you don't have to render anything outside of that region. Free pass. ๐ซถ
๐ฆ reshape
$widget->reshape;
Optional. This gets called after the window geometry changes. Super useful if you need to hand out new sizes to your child widgets. Like distributing snacks at a sleepover. ๐
๐ lines
$lines = $widget->lines;
๐ cols
$cols = $widget->cols;
These two get called to ask about the requested window for this widget. And ngl โ the actual window you get allocated might be bigger or smaller than what you asked for. It's giving "wishlist vs. what actually showed up." ๐
๐ธ window_gained
$widget->window_gained( $window );
Optional. Called by set_window when a window has actually been set for this widget. New window, who dis. โจ
๐ window_lost
$widget->window_lost( $window );
Optional. Called by set_window when undef has been set as the window for this widget. The old window object gets passed in so you can say your goodbyes. ๐ญ
โจ๏ธ on_key
$handled = $widget->on_key( $ev );
Optional. If you provide this method, it'll be set as the on_key callback for any window set on the widget. So basically โ write this method and your subclass can implement widgets that actually respond to user input. Main character behaviour. ๐
It receives the exact same event arguments structure as the underlying window's on_key event. No remixing, no substitutions.
๐ฑ๏ธ on_mouse
$handled = $widget->on_mouse( $ev );
Optional. Same deal as above, but for on_mouse. If you provide it, it becomes the on_mouse callback for any window set on the widget, and your subclass can react to user input. It receives the same event arguments structure as the underlying window's on_mouse event. ๐ญ
๐
on_style_changed_values
$widget->on_style_changed_values( %values );
Optional. If you provide this, set_style_tag will call it to let the widget know which style keys may have changed values as a result of the tag change. The style values come in as ARRAY references of two elements โ the old value and the new value.
Heads up bestie: the %values hash may contain false positives in some cases โ like, if the old and new value are actually the same, but it still looks from the style definitions like certain keys changed. It's giving "you looked different but it was just the lighting." ๐
Most of the time you don't even need this method, because the style_reshape_keys, style_reshape_textwidth_keys, and style_redraw_keys declarations should suffice for most purposes. So don't overthink it. โจ
๐ฏ CAN_FOCUS
$widget->CAN_FOCUS;
Optional, and normally false. But if this constant method returns a true value, the widget is allowed to take focus using the take_focus method. AND โ it will also take focus automatically if it receives a mouse button 1 press event. Iconic. Click it and it's on. ๐ฅ
๐ฅ KEYPRESSES_FROM_STYLE
$widget->KEYPRESSES_FROM_STYLE;
Optional, and normally false. If this constant method returns a true value, the widget uses style information to invoke named methods on keypresses. Here's the whole flow, so stay with me:
When the window's on_key event fires, the widget will first attempt to look up a style key with the name of the pressed key โ including its modifier key prefixes โ surrounded by <angle brackets>. If that gives you the name of a method prefixed by key_, then that method gets invoked as a special-purpose on_key handler. And if that doesn't exist, or doesn't return true, then the widget's regular on_key handler gets invoked instead โ if it's present. Clean chain, no drama. ๐
As a special case: space is given the key name <Space> instead of being notated by a literal space character in brackets, purely for the neatness of the style information. Aesthetic choices matter. ๐
๐งฐ UTILITY FUNCTIONS ๐งฐ
Okay so these next ones are functions, not methods โ and they're not exported by this package. They're meant to be called by widget subclasses, fully-qualified. So like, don't just call them and expect them to show up, bestie. Address them properly.
๐ split_widget_opts
( $widget, %opts ) = split_widget_opts( $arg );
Here's the tea:
- If
$argis an object reference derived fromTickit::Widget, return it directly with no extra options. Done. Easy. - If
$argis a reference to an unblessed hash, return thewidgetvalue from it โ which must exist, no exceptions โ followed by any other keys and values in no particular order.
This function is intended for methods that add an entire collection of child widgets in a single call. For example, Tickit::Widget::LinearBox's add_children โ you know, the case where the caller wants to override options on specific ones. One call, many widgets, chaotic but chic. ๐ฆ
โจ EXAMPLES โจ
๐ A Trivial "Hello, World" Widget
This is about the smallest possible Tickit::Widget implementation. The absolute bare minimum. It displays the fixed string "Hello, world" at the top left corner of its window.
package HelloWorldWidget;
use base 'Tickit::Widget';
sub lines { 1 }
sub cols { 12 }
sub render_to_rb
{
my $self = shift;
my ( $rb, $rect ) = @_;
$rb->eraserect( $rect );
$rb->text_at( 0, 0, "Hello, world" );
}
1;
So the lines and cols methods tell the container of the widget what its minimum size requirements are, and the render_to_rb method actually draws it to the render buffer. That's the whole job. ๐ธ
A slight improvement on this would be to obtain the size of the window, and position the text in the centre rather than the top left corner. Centred text? Obsessed.
sub render_to_rb
{
my $self = shift;
my ( $rb, $rect ) = @_;
my $win = $self->window;
$rb->eraserect( $rect );
$rb->text_at( $win->lines - 1 ) / 2, ( $win->cols - 12 ) / 2,
"Hello, world"
);
}
๐ฎ Reacting To User Input
If a widget subclass provides an on_key method, then it'll receive keypress events โ but only if the widget's window has the focus. This example uses it to change the pen foreground colour.
package ColourWidget;
use base 'Tickit::Widget';
my $text = "Press 0 to 7 to change the colour of this text";
sub lines { 1 }
sub cols { length $text }
sub render_to_rb
{
my $self = shift;
my ( $rb, $rect ) = @_;
my $win = $self->window;
$rb->eraserect( $rect );
$rb->text_at( $win->lines - 1 ) / 2, ( $win->cols - 12 ) / 2,
"Hello, world"
);
$win->focus( 0, 0 );
}
sub on_key
{
my $self = shift;
my ( $args ) = @_;
if( $args->type eq "text" and $args->str =~ m/[0-7]/ ) {
$self->set_style( fg => $args->str );
return 1;
}
return 0;
}
1;
The render_to_rb method sets the focus at the window's top left corner to make sure the window always has focus, so the widget will actually receive keypress events. (A real widget implementation would likely pick a more sensible place to put the cursor, ngl. But for a demo? It eats. ๐
)
The on_key method then gets invoked for keypresses. It returns a true value to indicate the keys it handles, and returns false for the others, so parent widgets or the main Tickit object can handle those instead. Sharing the load. We love teamwork. ๐ซถ
Similarly, by providing an on_mouse method, the widget subclass will receive mouse events within the window of the widget. This example saves a list of the last 10 mouse clicks and renders them with an "X".
package ClickerWidget;
use base 'Tickit::Widget';
# In a real Widget this would be stored in an attribute of $self
my @points;
sub lines { 1 }
sub cols { 1 }
sub render_to_rb
{
my $self = shift;
my ( $rb, $rect ) = @_;
$rb->eraserect( $rect );
foreach my $point ( @points ) {
$rb->text_at( $point->[0], $point->[1], "X" );
}
}
sub on_mouse
{
my $self = shift;
my ( $args ) = @_;
return unless $args->type eq "press" and $args->button == 1;
push @points, [ $args->line, $args->col ];
shift @points while @points > 10;
$self->redraw;
}
1;
This time there's no need to set the window focus, because mouse events don't need to follow the window that's in focus โ they always affect the window at the location of the mouse cursor. The mouse knows where it is. It's confident like that. ๐ญโจ
The on_mouse method then gets invoked whenever a mouse event happens within the window occupied by the widget. In this particular case, the method filters only for pressing button 1. It then stores the position of the mouse click in the @points array, for the render method to use. Ten clicks max, then the oldest one gets yeeted out of the array. Bye bestie. ๐
๐ AUTHOR ๐
Paul Evans <leonerd@leonerd.org.uk>
And that's a wrap on this page, gorgeous. ๐ This was Tickit::Widget(3pm) from perl v5.40.0, dated 2024-09-16 โ go build something cute with it, mwah. ๐โจ
๐ฆ where this page comes from
- ubuntu resolute: libtickit-widgets-perl 0.42-1 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ubuntu stonking: libtickit-widgets-perl 0.42-1 ยท Artistic-1.0-Perl OR GPL-1.0-or-later
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms