On Wed Sep 25 02:46:13 2013, TEAM wrote:
Show quoted text> Maybe I'm missing something but it seems that there's no way to listen
> for notifications when button states are toggled - if that's the case,
> an on_change or similar callback would be very useful for reacting to
> state changes.
Ah yes. Easy enough to add.
Attached is a patch to add 'on_toggle' to CheckButton, 'on_activate' to RadioButton, and 'on_changed' to RadioButton::Group.
--
Paul Evans
=== modified file 'lib/Tickit/Widget/CheckButton.pm'
--- lib/Tickit/Widget/CheckButton.pm 2013-09-27 12:56:35 +0000
+++ lib/Tickit/Widget/CheckButton.pm 2013-09-27 13:02:58 +0000
@@ -129,6 +129,10 @@
The label text to display alongside this button.
+=item on_toggle => CODE
+
+Optional. Callback function to invoke when the check state is changed.
+
=back
=cut
@@ -140,7 +144,8 @@
my $self = $class->SUPER::new( %args );
- $self->{label} = $args{label};
+ $self->set_label( $args{label} ) if defined $args{label};
+ $self->set_on_toggle( $args{on_toggle} ) if $args{on_toggle};
return $self;
}
@@ -185,6 +190,31 @@
$self->redraw;
}
+=head2 $on_toggle = $checkbutton->on_toggle
+
+=cut
+
+sub on_toggle
+{
+ my $self = shift;
+ return $self->{on_toggle};
+}
+
+=head2 $checkbutton->set_on_toggle( $on_toggle )
+
+Return or set the CODE reference to be called when the button state is
+changed.
+
+ $on_toggle->( $checkbutton, $active )
+
+=cut
+
+sub set_on_toggle
+{
+ my $self = shift;
+ ( $self->{on_toggle} ) = @_;
+}
+
=head1 METHODS
=cut
@@ -200,6 +230,7 @@
my $self = shift;
$self->{active} = 1;
$self->set_style_tag( active => 1 );
+ $self->{on_toggle}->( $self, 1 ) if $self->{on_toggle};
}
=head2 $checkbutton->deactivate
@@ -213,6 +244,7 @@
my $self = shift;
$self->{active} = 0;
$self->set_style_tag( active => 0 );
+ $self->{on_toggle}->( $self, 0 ) if $self->{on_toggle};
}
*key_toggle = \&toggle;
=== modified file 'lib/Tickit/Widget/RadioButton.pm'
--- lib/Tickit/Widget/RadioButton.pm 2013-08-30 23:19:22 +0000
+++ lib/Tickit/Widget/RadioButton.pm 2013-09-28 13:03:41 +0000
@@ -88,6 +88,16 @@
=back
+The following style actions are used:
+
+=over 4
+
+=item activate
+
+The main action to activate the C<on_click> handler.
+
+=back
+
=cut
style_definition base =>
@@ -130,6 +140,11 @@
supplied, a new group will be constructed that can be accessed using the
C<group> accessor.
+=item value => SCALAR
+
+Optional. If supplied, used to set the button's identification value, which
+is passed to the group's C<on_changed> callback.
+
=back
=cut
@@ -141,7 +156,10 @@
my $self = $class->SUPER::new( %args );
- $self->{label} = $args{label};
+ $self->set_label( $args{label} ) if defined $args{label};
+ $self->set_on_toggle( $args{on_toggle} ) if $args{on_toggle};
+ $self->set_value( $args{value} ) if defined $args{value};
+
$self->{group} = $args{group} || Tickit::Widget::RadioButton::Group->new;
return $self;
@@ -199,6 +217,58 @@
$self->redraw;
}
+=head2 $on_toggle = $radiobutton->on_toggle
+
+=cut
+
+sub on_toggle
+{
+ my $self = shift;
+ return $self->{on_toggle};
+}
+
+=head2 $radiobutton->set_on_toggle( $on_toggle )
+
+Return or set the CODE reference to be called when the button state is
+changed.
+
+ $on_toggle->( $radiobutton, $active )
+
+When the radio tick mark moves from one button to another, the old button is
+marked unactive before the new one is marked active.
+
+=cut
+
+sub set_on_toggle
+{
+ my $self = shift;
+ ( $self->{on_toggle} ) = @_;
+}
+
+=head2 $value = $radiobutton->value
+
+=cut
+
+sub value
+{
+ my $self = shift;
+ return $self->{value};
+}
+
+=head2 $radiobutton->set_value( $value )
+
+Return or set the scalar value used to identify the radio button to the
+group's C<on_changed> callback. This can be any scalar value; it is simply
+stored by the button and not otherwise used.
+
+=cut
+
+sub set_value
+{
+ my $self = shift;
+ ( $self->{value} ) = @_;
+}
+
=head1 METHODS
=cut
@@ -218,11 +288,14 @@
if( my $old = $group->active ) {
$old->set_style_tag( active => 0 );
+ $old->{on_toggle}->( $old, 0 ) if $old->{on_toggle};
}
$group->set_active( $self );
$self->set_style_tag( active => 1 );
+ $self->{on_toggle}->( $self, 1 ) if $self->{on_toggle};
+
return 1;
}
@@ -301,7 +374,7 @@
sub new
{
my $class = shift;
- return bless \(my $self), $class;
+ return bless [ undef, undef ], $class;
}
=head2 $radiobutton = $group->active
@@ -313,13 +386,42 @@
sub active
{
my $self = shift;
- return $$self;
+ return $self->[0];
}
sub set_active
{
my $self = shift;
- ( $$self ) = @_;
+ ( $self->[0] ) = @_;
+ $self->[1]->( $self->active, $self->active->value ) if $self->[1];
+}
+
+=head2 $on_changed = $group->on_changed
+
+=cut
+
+sub on_changed
+{
+ my $self = shift;
+ return $self->[1];
+}
+
+=head2 $group->set_on_changed( $on_changed )
+
+Return or set the CODE reference to be called when the active member of the
+group changes. This may be more convenient than setting the C<on_toggle>
+callback of each button in the group.
+
+The callback is passed the currently-active button, and its C<value>.
+
+ $on_changed->( $active, $value )
+
+=cut
+
+sub set_on_changed
+{
+ my $self = shift;
+ ( $self->[1] ) = @_;
}
=head1 AUTHOR
=== modified file 't/11widget-radiobutton.t'
--- t/11widget-radiobutton.t 2013-05-11 19:29:25 +0000
+++ t/11widget-radiobutton.t 2013-09-28 13:03:58 +0000
@@ -14,13 +14,25 @@
push my @buttons, Tickit::Widget::RadioButton->new(
label => "Radio 1",
+ value => 1,
);
push @buttons, Tickit::Widget::RadioButton->new(
label => "Radio $_",
+ value => $_,
group => $buttons[0]->group
) for 2 .. 4;
+my $radio2_active;
+$buttons[1]->set_on_toggle(
+ sub { ( undef, $radio2_active ) = @_ }
+);
+
+my $active_value;
+$buttons[0]->group->set_on_changed(
+ sub { ( undef, $active_value ) = @_ }
+);
+
is( $buttons[2]->label, "Radio 3", '$button->label' );
my $vbox = Tickit::Widget::VBox->new;
@@ -36,6 +48,8 @@
[TEXT("( )",fg=>15,b=>1), BLANK(2), TEXT("Radio 4")] ],
'Display initially' );
+ok( !$radio2_active, '$radio2_active false initially' );
+
pressmouse( press => 1, 1, 10 );
flush_tickit;
@@ -47,6 +61,8 @@
'Display after click 2' );
ok( $buttons[1]->is_active, 'Radio 2 is active' );
+ok( $radio2_active, '$radio2_active after click 2' );
+is( $active_value, 2, 'active ->value is 2 after click 2' );
pressmouse( press => 1, 3, 10 );
@@ -60,6 +76,8 @@
ok( !$buttons[1]->is_active, 'Radio 2 no longer active' );
ok( $buttons[3]->is_active, 'Radio 4 is active' );
+ok( !$radio2_active, '$radio2_active false after click 4' );
+is( $active_value, 4, 'active ->value is 4 after click 4' );
$buttons[0]->set_label( "First radio" );
=== modified file 't/12widget-checkbutton.t'
--- t/12widget-checkbutton.t 2013-05-11 19:29:25 +0000
+++ t/12widget-checkbutton.t 2013-09-27 13:03:47 +0000
@@ -11,8 +11,10 @@
my $root = mk_window;
+my $active;
my $button = Tickit::Widget::CheckButton->new(
label => "Check button",
+ on_toggle => sub { ( undef, $active ) = @_ },
);
is( $button->label, "Check button", '$button->label' );
@@ -33,6 +35,7 @@
'Display after click' );
ok( $button->is_active, '$button->is_active true after click' );
+ok( $active, 'on_toggle invoked after click' );
pressmouse( press => 1, 0, 7 );
@@ -42,5 +45,6 @@
'Display after second click' );
ok( !$button->is_active, '$button->is_active true after second click' );
+ok( !$active, 'on_toggle invoked after second click' );
done_testing;