NAME
Clay::UI::Role::Interaction::HasFocusOrder - role for containers that choose the focus order in their subtree
SYNOPSIS
use v5.22;
use warnings;
use feature 'signatures';
no warnings 'experimental::signatures';
use Object::Pad;
use Clay::UI;
use Clay::UI::Box;
use Clay::UI::Role::Interaction::Focusable;
use Clay::UI::Role::Interaction::HasFocusOrder;
class My::Input :strict(params)
:does(Clay::UI::Box)
:does(Clay::UI::Role::Interaction::Focusable)
{}
# A form whose first Tab goes to the toolbar, although it comes last.
class My::Form :strict(params)
:does(Clay::UI::Box)
:does(Clay::UI::Role::Interaction::HasFocusOrder)
{
field $toolbar :param :reader;
method get_next_focus () {
return $toolbar unless defined $self->ui->interaction->get_focused_widget;
return $self->default_next_focus;
}
method get_previous_focus () {
return $self->default_previous_focus;
}
}
my $toolbar = My::Input->new(id => 'toolbar');
my $form = My::Form->new(id => 'form', toolbar => $toolbar);
$form->add_child(My::Input->new(id => 'name'), My::Input->new(id => 'email'), $toolbar);
my $ui = Clay::UI->new(width => 400, height => 300, root => $form);
for (1 .. 4) {
$ui->interaction->focus_next;
say $ui->interaction->get_focused_widget->id; # toolbar, name, email, toolbar
}
DESCRIPTION
By default, focus_next and focus_previous (Clay::UI::Interaction) move the focus in the depth-first pre-order of the widget tree. A widget that composes this role decides the order inside its subtree instead. It does not need to take the focus itself: HasFocusOrder does not imply Clay::UI::Role::Interaction::Focusable.
Which widget decides:
With a focused widget: the nearest widget composing HasFocusOrder among the focused widget and its ancestors. When there is none, the default order applies.
With nothing focused: the root widget of the Clay::UI, if it composes HasFocusOrder; otherwise the default order applies.
The deciding widget's "get_next_focus" (for focus_next) or "get_previous_focus" (for focus_previous) is called without arguments and must return one of:
undef-
The focus stays where it is.
- a Focusable widget of the same Clay::UI
-
It gets the focus, unless its
can_focusis 0 at that moment; then the focus stays where it is.
Anything else is a bug in the composing class: a non-widget, a widget that does not compose Clay::UI::Role::Interaction::Focusable, or a widget that is not part of this Clay::UI. focus_next / focus_previous then die with Clay::UI::Interaction: <class> returned ..., naming the class and the problem.
The role composes Clay::UI::Role::Layout::HasParent (for ui).
REQUIRED METHODS
The composing class must implement both.
get_next_focus
method get_next_focus () { ... }
Returns the widget that should get the focus on focus_next, or undef to keep it. $self->ui->interaction->get_focused_widget tells where the focus is now (undef when nothing is focused).
get_previous_focus
method get_previous_focus () { ... }
Returns the widget that should get the focus on focus_previous, or undef; the mirror of "get_next_focus".
PROVIDED METHODS
default_next_focus
return $self->default_next_focus;
return $self->default_next_focus(within => $self);
Returns the widget the default order would focus next from the currently focused widget, ignoring every HasFocusOrder (see "default_next_focus" in Clay::UI::Interaction), or undef when no widget can take the focus or this widget does not belong to a Clay::UI. Use it for partial overrides: handle the special case, and return $self->default_next_focus for everything else.
The arguments go to the tracker as they are. With within => $widget (a focus scope, see "Focus scopes" in Clay::UI::Interaction) the order is limited to that subtree and wraps around inside it, which is how a modal dialog keeps Tab inside itself:
method get_next_focus () { return $self->default_next_focus(within => $self) }
method get_previous_focus () { return $self->default_previous_focus(within => $self) }
default_previous_focus
return $self->default_previous_focus;
return $self->default_previous_focus(within => $self);
The mirror of "default_next_focus".
SEE ALSO
"FOCUS" in Clay::UI::Interaction, "focus_next" in Clay::UI::Interaction, Clay::UI::Role::Interaction::Focusable.