Selkie.git | lib/Selkie/Widget/ | FocusableByDefault.rakumod


=begin pod

=head1 NAME

Selkie::Widget::FocusableByDefault - Mix-in role that defaults
C<focusable> to True at construction

=head1 SYNOPSIS

=begin code :lang<raku>

use Selkie::Widget;
use Selkie::Widget::FocusableByDefault;

unit class My::Toggle does Selkie::Widget does Selkie::Widget::FocusableByDefault;

# No `method new` override needed — composing the role causes
# `My::Toggle.new(...)` to default `focusable => True` unless the
# caller passes it explicitly.

=end code

=head1 DESCRIPTION

Most input widgets in Selkie are focusable by default — Buttons,
Checkboxes, TextInputs, ListViews, RadioGroups, and so on. Before this
role existed, each of those widgets carried the same three-line C<new>
override:

=begin code :lang<raku>

method new(*%args --> ::?CLASS) {
    %args<focusable> //= True;
    callwith(|%args);
}

=end code

That boilerplate is what this role consolidates. Compose it on any
widget that should default to focusable, and the role's C<new> takes
care of the C<focusable> default — callers that pass an explicit
C<:!focusable> or C<:focusable(False)> still win, because C<//=>
respects the caller's choice.

=head2 Why a role and not a base class?

Selkie's widget hierarchy is role-based (everything composes
C<Selkie::Widget>) rather than class-based, so a role mixin is the
natural shape. Composing this role doesn't add any state — only the
C<new> behaviour — so it's free of the usual diamond-inheritance
hazards that come with multi-class hierarchies.

=head2 What if my widget needs more constructor logic?

Implement C<submethod TWEAK> on your class — it runs after C<new> has
returned the new object, with all attributes initialized. The role's
C<new> doesn't interfere with C<TWEAK>; both compose cleanly.

=end pod

unit role Selkie::Widget::FocusableByDefault;

#| Constructor wrapper. Defaults C<focusable> to True before delegating
#| to the next C<new> candidate in MRO (typically C<Mu.new>), which
#| returns an instance of the composing class. An explicit
#| C<:focusable(False)> from the caller is preserved.
#|
#| The return type is intentionally unconstrained: a role-context
#| C<--> ::?CLASS> trips C<Pod::To::Markdown>'s signature renderer
#| (the placeholder has no C<.WHICH> before composition), and the
#| constraint would be redundant anyway since C<callwith> already
#| returns an instance of C<::?CLASS>.
method new(*%args) {
    %args<focusable> //= True;
    callwith(|%args);
}