The Notifications object now handles displaying multiple notifications.
authorAndrew Ruthven <andrew@etc.gen.nz>
Mon, 27 Oct 2008 09:13:16 +0000 (22:13 +1300)
committerAndrew Ruthven <andrew@etc.gen.nz>
Mon, 27 Oct 2008 09:13:16 +0000 (22:13 +1300)
When a new notification is submitted the existing ones are moved up the
screen, then the new one is faded in.  The blocks get expired after a
pre-set amount of time.

Also allows the submitted notification to just be a text string and
we'll take care of creating the Clutter::Label.

lib/Display/Notifications.pm
lib/Display/Plugins/MPD.pm
lib/Display/Plugins/NotifyTest.pm

index b4bb858..082c17e 100644 (file)
@@ -5,7 +5,7 @@ use POE::Session;
 use Set::Object;
 use strict;
 
-my $delay = 5;
+my $delay = 15;
 
 sub new {
   my ($proto, $kernel, $session, $stage) = @_;
@@ -15,17 +15,33 @@ sub new {
     '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);
@@ -34,43 +50,45 @@ sub add {
   $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 {
@@ -78,11 +96,30 @@ 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 {
@@ -90,7 +127,6 @@ 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()
@@ -98,11 +134,76 @@ sub post_fade_out {
   }
 }
 
-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;
index 7acb4ee..13d92df 100644 (file)
@@ -45,11 +45,9 @@ sub display {
     push @lines, $current->album()  if defined $current->album();
     push @lines, $current->artist() if defined $current->artist();
 
-    $self->{'status'}->set_text(join("\n", @lines));
-
     $self->{'file'} = $current->file();
 
-    $kernel->yield('notifications_add', $self->{'status'}, $self->{'logo'});
+    $kernel->post('display', 'notifications_add', join("\n", @lines), $self->{'logo'});
   }
 
   $self->delay();
@@ -60,10 +58,6 @@ sub init {
 
   $self->{'kernel'}->state('mpd_display', $self, 'display');
 
-  $self->{'status'} = Clutter::Label->new('Sans 20', "Song\nAlbum");
-  $self->{'status'}->set_color(Clutter::Color->parse('White'));
-  $self->{'status'}->set_ellipsize('end');
-
   $self->{'logo'} = "$Bin/share/MPD.png";
 
   $self->{'kernel'}->yield('mpd_display');
index 95660ad..213214d 100644 (file)
@@ -28,10 +28,10 @@ sub display {
   my ($self, $kernel) = @_[OBJECT, KERNEL];
 
   my $text = Clutter::Label->new('Sans 20', "Notify " . ++$self->{'count'});
-  $text->set_color(Clutter::Color->parse('White'));
+  $text->set_color(Clutter::Color->parse('Blue'));
   $text->set_ellipsize('end');
 
-  $kernel->yield('notifications_add', $text);
+  $kernel->post('display', 'notifications_add', $text);
 
   $self->delay();
 }