use Set::Object;
use strict;
-my $delay = 5;
+my $delay = 15;
sub new {
my ($proto, $kernel, $session, $stage) = @_;
'kernel' => $kernel,
'session' => $session,
'stage' => $stage,
+ 'moving' => 0,
};
$self->{'blocks'} = Set::Object->new();
+ @{ $self->{'new'} } = ();
+ @{ $self->{'paths'} } = ();
+ @{ $self->{'pending_move'} } = ();
bless ($self, $class);
- $self->init();
+
+ $self->{'kernel'}->state('notifications_add', $self, 'add');
+ $self->{'kernel'}->state('notifications_expire', $self, 'expire');
+
return $self;
}
sub add {
- my ($self, $kernel, $notification, $logo) = @_[OBJECT, KERNEL, ARG0, ARG1];
+ my ($self, $kernel, $notification, $logo_file) = @_[OBJECT, KERNEL, ARG0, ARG1];
+
+ my $text = undef;
+ if ($notification->isa("Clutter::Label")) {
+ $text = $notification;
+ } else {
+ $text = Clutter::Label->new('Sans 20', $notification);
+ $text->set_color(Clutter::Color->parse('White'));
+ $text->set_ellipsize('end');
+ }
my $block = Clutter::Group->new();
$block->set_opacity(0);
$bg->set_width($self->{'stage'}->get_width() - 20);
$bg->set_opacity(100);
- $block->add($bg);
- $block->add($notification);
+ $bg->set_height($text->get_height() + 10);
- $bg->set_height($notification->get_height() + 10);
- $bg->set_anchor_point(1, $bg->get_height());
- $bg->set_position(10, $self->{'stage'}->get_height() - 10);
+ $text->set_position(5, 5);
- $notification->set_anchor_point(1, $notification->get_height());
+ $block->set_height($bg->get_height());
+ $block->set_anchor_point(1, $block->get_height());
+ $block->set_position(10, $self->{'stage'}->get_height() - 10);
- if (defined $logo) {
+ $block->add($bg);
+ $block->add($text);
+
+ if (defined $logo_file) {
+ my $logo = Clutter::Texture->new($logo_file);
$block->add($logo);
$logo->set('keep-aspect-ratio' => 1);
$logo->set('sync-size' => 1);
- $logo->set_height($notification->get_height());
+ $logo->set_height($text->get_height());
+ $logo->set_position(5, 5);
- $logo->set_position(20, $self->{'stage'}->get_height() - $bg->get_height - 20);
- $notification->set_width($bg->get_width() - $logo->get_width() - 20 - 20);
- $notification->set_position(20 + $logo->get_width() + 20, $self->{'stage'}->get_height() - 20);
+ $text->set_width($bg->get_width() - $logo->get_width() - 20 - 20);
+ $text->set_x(20 + $logo->get_width() + 20);
} else {
- $notification->set_position(20, $self->{'stage'}->get_height() - 20);
- $notification->set_width($bg->get_width() - 20);
+ $text->set_width($bg->get_width() - 20);
+ $text->set_x(20);
}
- my $expire = time() + $delay;
-
- $self->{'blocks'}->insert( {
- 'expire' => $expire,
- 'block' => $block
- });
+ if ($self->{'blocks'}->size() > 0) {
+ if (! $self->{'moving'}) {
+ push @{ $self->{'new'} }, $block;
- $self->{'stage'}->add($block);
- my $effect = Clutter::EffectTemplate->new_for_duration(1000, 'main::smoothstep_inc');
- my $timeline = Clutter::Effect->fade($effect, $block, 255);
-
- $kernel->delay_add('notifications_expire', $delay);
+ $self->move_blocks();
+ } else {
+ push @{ $self->{'pending_move'} }, $block;
+ }
+ } else {
+ push @{ $self->{'new'} }, $block;
- $timeline->start();
+ fade_in(undef, $self);
+ }
}
sub expire {
for my $block ($self->{'blocks'}->members()) {
if ($block->{'expire'} <= time()) {
- my $old_effect = Clutter::EffectTemplate->new_for_duration(1000, 'main::smoothstep_inc' );
- my $old = Clutter::Effect->fade($old_effect, $block->{'block'}, 0, $self->can('post_fade_out'), $self);
+ my $old_effect =
+ Clutter::EffectTemplate->new_for_duration(1000,
+ 'main::smoothstep_inc' );
+ my $old =
+ Clutter::Effect->fade($old_effect, $block->{'block'}, 0,
+ $self->can('post_fade_out'), $self);
$old->start();
}
}
+
+ # What a hack! It seems that multiple alarm_ardds aren't being
+ # honoured. So, we check and see when the next expire should run, and
+ # call ourselves again then.
+ while (scalar(@{ $self->{'expire_times'} }) > 0
+ && $self->{'expire_times'}[0] <= time()) {
+ my $old = shift @{ $self->{'expire_times'} };
+ }
+
+ if (defined $self->{'expire_times'}[0]) {
+ my $timer = shift @{ $self->{'expire_times'} };
+ $self->{'kernel'}->alarm_add('notifications_expire', $timer);
+ }
+
+ return 1;
}
sub post_fade_out {
for my $block ($self->{'blocks'}->members()) {
if ($block->{'expire'} <= time()) {
- $self->{'stage'}->remove($block->{'block'});
$self->{'blocks'}->remove($block);
$block->{'block'}->remove_all()
}
}
-sub init {
- my $self = shift;
+sub fade_in {
+ my ($score, $self) = @_;
- $self->{'kernel'}->state('notifications_add', $self, 'add');
- $self->{'kernel'}->state('notifications_expire', $self, 'expire');
+ # Clean out no longer required state.
+ $self->{'paths'} = ();
+
+ while (my $block = shift @{ $self->{'new'} }) {
+ if (defined $block) {
+ $self->{'stage'}->add($block);
+ my $effect = Clutter::EffectTemplate->new_for_duration(1000, 'main::smoothstep_inc');
+ my $timeline = Clutter::Effect->fade($effect, $block, 255);
+
+ my $expire = time() + $delay;
+
+ $self->{'blocks'}->insert( {
+ 'expire' => $expire,
+ 'block' => $block
+ });
+
+ $self->{'kernel'}->delay_add('notifications_expire', $delay);
+ push @{ $self->{'expire_times'} }, $expire;
+
+ $timeline->start();
+ $self->{'moving'} = 0;
+ }
+ }
+
+ # Check and see if there are any blocks that have been added since we
+ # started moving, and start the process of showing them.
+ if (scalar(@{ $self->{'pending_move'} }) > 0) {
+ while (my $block = shift @{ $self->{'pending_move'} }) {
+ push @{ $self->{'new'} }, $block;
+ }
+
+ $self->move_blocks();
+ }
+}
+
+sub move_blocks {
+ my ($self) = @_;
+
+ # We're in the process of moving blocks.
+ $self->{'moving'} = 1;
+
+ my $score = Clutter::Score->new();
+ $score->signal_connect('completed' => $self->can('fade_in'), $self);
+
+ # Work out the total height
+ my $height = 10;
+ for my $new (@{ $self->{'new'} }) {
+ $height += $new->get_height();
+ }
+
+ for my $obj ($self->{'blocks'}->members()) {
+ my $block = $obj->{'block'};
+ my $timeline = Clutter::Timeline->new_for_duration(1000);
+ my $alpha = Clutter::Alpha->new($timeline, 'main::smoothstep_inc');
+
+ my $path = Clutter::Behaviour::Path->new($alpha,
+ [ $block->get_position() ],
+ [ $block->get_x(), $block->get_y() - $height ]);
+ $path->apply($block);
+
+ push @{ $self->{'paths'} }, $path;
+
+ $score->append(undef, $timeline);
+ }
+
+ $score->start();
+ $self->{'score'} = $score;
}
1;