Beefy Boxes and Bandwidth Generously Provided by pair Networks
more useful options
 
PerlMonks  

Gtk2 Bubble notification windows

by zentara (Archbishop)
on Nov 29, 2008 at 19:12 UTC ( [id://726821]=sourcecode: print w/replies, xml ) Need Help??
Category: GUI Programming
Author/Contact Info zentara of perlmonks
Description: This package allows you to pop "cartoon-like bubble windows" centered on another window, or a StatusIcon, to send a notification message. It's based on the great work of Dirk van der Walt and his Gtk2 Study Guide

You can specify a window to pop on, or an x,y location.

#!/usr/bin/perl -w
use strict;
use Glib qw/TRUE FALSE/;

# this packaged based on work of Dirk van der Walt's
# package Gtk2::Ex::Notify;

package Gtk2::Ex::ZNotify;
use Gtk2;
use Glib qw/TRUE FALSE/;

use constant wbsize           => 16;    #window border size
use constant messageTextWidth => 300;

use Glib::Object::Subclass Gtk2::Window::
 ,    # parent class, derived from Glib::Object
 signals => {
   show               => \&on_show,
   size_allocate      => \&on_size_allocate,
   style_set          => \&on_style_set,
   expose_event       => \&on_expose_event,
   enter_notify_event => \&on_enter_notify_event,
   leave_notify_event => \&on_leave_notify_event,
 },
 properties => [

   Glib::ParamSpec->object(
      'parent_widget',                                   'parent_widge
+t',
      'The parent widget on which this popup will show', 'Gtk2::Widget
+',
      [ qw/readable writable/ ]
   ),
   Glib::ParamSpec->string(
      'message',
      'message',
      'The heading for this message',
      'This is sample heading',
      [ qw/readable writable/ ]
   ),

   Glib::ParamSpec->enum(
      'messageType',             'messageType',
      'Type of message this is', 'Gtk2::MessageType',
      'info', [ qw/readable writable/ ]
   ),

   Glib::ParamSpec->int(
      'timeout', 'timeout',
      'Timeout in milli-seconds before hide, 0 = never hide',
      0, 10000000, 0, [ qw/readable writable/ ]
   ),

   Glib::ParamSpec->int(
      'x', 'x',
      'x position for pointer',
      0, 64000, 0, [ qw/readable writable/ ]
   ),

   Glib::ParamSpec->int(
      'y', 'y',
      'y position for pointer',
      0, 64000, 0, [ qw/readable writable/ ]
   ),

 ];

sub INIT_INSTANCE {

   my $self = shift;

   $self->{ activeBackgroundColor } =
    new Gtk2::Gdk::Color( ( 249 * 257 ), ( 253 * 257 ), ( 202 * 257 ) 
+);
    #Remember to * with 257!!
 
   $self->{ inactiveBackgroundColor } =
    new Gtk2::Gdk::Color( ( 255 * 257 ), ( 255 * 257 ), ( 255 * 257 ) 
+);

#Initial setup
   $self->app_paintable( TRUE );
   $self->{ activebackground }   = undef;
   $self->{ inactivebackground } = undef;

#Create the layout of the window
   my $outBox = new Gtk2::HBox();
   $outBox->set_border_width( wbsize );

   $self->add( $outBox );

   my $closeBox = new Gtk2::VBox();
   $closeBox->set_border_width( 3 );

   $outBox->pack_end( $closeBox, TRUE, TRUE, 0 );

   my $eBox = new Gtk2::EventBox();

#Add event handler for the "close" event box
   $eBox->signal_connect(
      'button-press-event' => sub {
         $self->hide();
         $self->destroy();
      }
   );

   my $closeImg = new Gtk2::Image();
   $closeImg->set_from_stock( 'gtk-close', 'menu' );
   $eBox->add( $closeImg );

   $closeBox->pack_start( $eBox, FALSE, FALSE, 0 );

   my $padder = new Gtk2::Label( "" );
   $outBox->pack_start( $padder, FALSE, FALSE, 5 );

   my $vbox = new Gtk2::VBox();
   $vbox->set_border_width( 10 );
   $outBox->pack_start( $vbox, TRUE, TRUE, 0 );

   my $hbox = new Gtk2::HBox();
   $hbox->set_spacing( 5 );
   my $iconVBox = new Gtk2::VBox();
   my $msgImage = new Gtk2::Image();
   $self->{ msgImage } =
    $msgImage;    #Needed in order to configure after creation!
   $iconVBox->pack_start( $msgImage, FALSE, FALSE, 0 );
   $hbox->pack_start( $iconVBox,     FALSE, FALSE, 0 );
   $vbox->pack_start( $hbox,         FALSE, FALSE, 0 );
 
   my $messageVBox = new Gtk2::VBox();
   $hbox->pack_start( $messageVBox, TRUE, TRUE, 0 );

   my $l = new Gtk2::Label();
   $l->set_line_wrap( FALSE );
   $l->set_use_markup( TRUE );
   $l->set_selectable( FALSE );
   $l->set_alignment( 0, 0 );
   $l->set_line_wrap( TRUE );
   $l->{ width_request } = messageTextWidth;
   $self->{ lblHeading } = $l;    #Needed in order to configure after 
+creation!
   $messageVBox->pack_start( $l, FALSE, TRUE, 0 );
   $self->{ msgVBox } = $messageVBox;  #Needed in order to configure a
+fter creation!

   my $spacer = new Gtk2::Label();
   $spacer->set_markup( "<span size='small' background = 'red' foregro
+und= 'white'
            >!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!</span>" );
   $messageVBox->pack_end( $spacer, FALSE, FALSE, 0 );

# the timeout eventhandler ID
   $self->{ closeWindowTimeoutID } = 0;

#Add the background property in order to change it
   $self->{ background } = undef;

}

#Chose this event to set up the various widgets in order so they can b
+e in place BEFORE
#The size_allocation happens
sub on_style_set() {

   my ( $self, $event ) = @_;

#Set up the heading
   $self->{ lblHeading }->set_markup( "<span size=\"small\" weight=\"b
+old\" foreground= 'black'>"
       . $self->{ message }
       . "</span>" );

#set up the icon to display
   ( $self->{ messageType } =~ m/info/ )
    && ( $self->{ msgImage }->set_from_stock( 'gtk-dialog-info', 'butt
+on' ) );
   ( $self->{ messageType } =~ m/warning/ )
    && (
      $self->{ msgImage }->set_from_stock( 'gtk-dialog-warning', 'butt
+on' ) );
   ( $self->{ messageType } =~ m/question/ )
    && (
      $self->{ msgImage }->set_from_stock( 'gtk-dialog-question', 'but
+ton' ) );
   ( $self->{ messageType } =~ m/error/ )
    && ( $self->{ msgImage }->set_from_stock( 'gtk-dialog-error', 'but
+ton' ) );


   $self->signal_chain_from_overridden( $event );
   return FALSE;
}

#This will start the timeout handler if the timeout value was more tha
+n 0
sub on_show() {

   my ( $self, $event ) = @_;

   $self->signal_chain_from_overridden;

   if ( ( $self->{ closeWindowTimeoutID } == 0 ) && $self->{ timeout }
+ > 0 ) {
      $self->{ closeWindowTimeoutID } = Glib::Timeout->add(
         $self->{ timeout },
         sub {
            hide_window_callback( $self );
            return FALSE;
         }
      );
   }

}

sub hide_window_callback() {

   my ( $self ) = @_;
   $self->hide();
   $self->destroy();
   return FALSE;

}

#this will do some magick to create the outline for the notification w
+indow
sub on_size_allocate() {

   my ( $self, $sized ) = @_;

#Very important to be at the start :)
   $self->signal_chain_from_overridden( $sized );

   if ( !( $self->{ background } ) ) {
      my $mask;

#Set the values for the active and inactive backgrounds
      ( $self->{ activebackground }, $self->{ inactivebackground }, $m
+ask ) =
       RenderBubbles( $self, $sized );
      $self->{ background } = $self->{ inactivebackground };

      if ( $mask ) {
         $self->shape_combine_mask( $mask, 0, 0 );    #Shape the windo
+w
      }
      else {
         print "mask was null, could not shape the window\n";
      }
   }
}

sub on_expose_event() {

   my ( $self, $event ) = @_;

   my $state = $self->state;
   #print "EXPOSE EVENT $state\n";

   if ( $state eq 'active' ) {
      $self->{ background } = $self->{ activebackground };
   }
   else {
      $self->{ background } = $self->{ inactivebackground };
   }

   my $gc = Gtk2::Gdk::GC->new( $self->window );
   $self->window->draw_pixbuf(
      $gc, $self->{ background },
      0, 0, 0, 0,
      $self->{ background }->get_width(),
      $self->{ background }->get_height(),
      'none', 0, 0
   );

   $self->signal_chain_from_overridden( $event );
   return FALSE;
}


sub on_enter_notify_event() {

   my ( $self, $event ) = @_;
   $self->signal_chain_from_overridden( $event );

   if ( $self->{ closeWindowTimeoutID } != 0 ) {

#Remove the timeout cause the user is interested in the message
      Glib::Source->remove( $self->{ closeWindowTimeoutID } );
      $self->{ closeWindowTimeoutID } = 0;
   }

   $self->set_state( 'active' );
   return FALSE;
}

sub on_leave_notify_event() {
   my ( $self, $event ) = @_;
   $self->signal_chain_from_overridden( $event );

   $self->set_state( 'normal' );
   $self->queue_draw();

   if ( $self->{ closeWindowTimeoutID } != 0 ) {
      Glib::Source->remove( $self->{ closeWindowTimeoutID } );
      $self->{ closeWindowTimeoutID } = 0;
   }

   if ( $self->{ timeout } > 0 ) {

#restart the timeout they lost interest
      $self->{ closeWindowTimeoutID } = Glib::Timeout->add(
         $self->{ timeout },
         sub {
            &hide_window_callback( $self );
            return FALSE;
         }
      );
   }
   return FALSE;
}


sub RenderBubbles() {

   my ( $win, $size ) = @_;
   my ( $pbactive, $pbinactive, $pbbm );
   my ( $pmHeight, $pmWidth );

   my $daPixmap;    #Drawing Area pixmap
   my $daBitmap;    #Drawing Area bitmap

   my $self = $win;
   $win->realize();

   $pmHeight = $size->height - ( wbsize * 2 );
   $pmWidth  = $size->width -  ( wbsize * 2 );

   my $gc = Gtk2::Gdk::GC->new( $win->window );    #Create a Graphic C
+ontext

   #print $win->window->get_window_type;

#-----------------------------------------------------
#--------- Build active Pixbuf------------------------
#-----------------------------------------------------
   my $pm =
    Gtk2::Gdk::Pixmap->new( $win->window, $size->width, $size->height,
+ -1 );

# Paint the background white
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'white' ) );
   $pm->draw_rectangle( $gc, TRUE, 0, 0, $size->width, $size->height )
+;

#/***********************************
#    draw painted oval window
#***********************************/
# Paint the inside of the window

   $gc->set_rgb_fg_color( $self->{ activeBackgroundColor } );
   $pm->draw_polygon( $gc, TRUE,
      CalculateRect( wbsize, wbsize, $pmHeight, $pmWidth ) );

# Paint the border of the window
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'black' ) );
   $pm->draw_polygon( $gc, FALSE,
      CalculateRect( wbsize, wbsize, $pmHeight - 1, $pmWidth - 1 ) );

#/***********************************
#    add tab to bitmap
#***********************************/

#// Draw colored pointer
   $gc->set_rgb_fg_color( $self->{ activeBackgroundColor } );

   my @list = CalcPointerMoveWindow( $self, 
            $size->width, $size->height, wbsize,
             $self->{ parent_widget } );
   
   $pm->draw_polygon( $gc, TRUE, @list );
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'black' ) );

#// subtract one because the fill above used and extra line
   $pm->draw_line( $gc, $list[ 0 ], ( $list[ 1 ] - 1 ), $list[ 2 ],
      $list[ 3 ] );

   $pm->draw_line( $gc, $list[ 2 ], $list[ 3 ], $list[ 4 ],
      ( $list[ 5 ] - 1 ) );


   my $pb =
    Gtk2::Gdk::Pixbuf->new( 'rgb', TRUE, 8, $size->width, $size->heigh
+t );
   $pb->get_from_drawable( $pm, $pm->get_colormap, 0, 0, 0, 0, $size->
+width,
      $size->height );

   $pb = $pb->add_alpha( TRUE, 255, 255, 255 );

   ( $daPixmap, $daBitmap ) = $pb->render_pixmap_and_mask( 255 );

   $pbactive = $pb;          #The active layout
   $pbbm     = $daBitmap;    #The mask

#-----------------------------------------------------
#--------- Build INACATIVE Pixbuf---------------------
#-----------------------------------------------------

#// Reset backgound to white and get next bitmap (The inactive one)
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'white' ) );
   $pm->draw_rectangle( $gc, TRUE, 0, 0, $size->width, $size->height )
+;

#// Paint the border of the window
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'black' ) );
   $pm->draw_polygon( $gc, FALSE,
      CalculateRect( wbsize, wbsize, $pmHeight - 1, $pmWidth - 1 ) );

#// Draw white pointer
   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'white' ) );
   $pm->draw_polygon( $gc, TRUE, @list );

   $gc->set_rgb_fg_color( Gtk2::Gdk::Color->parse( 'black' ) );

#// subtract one because the fill above used and extra line
   $pm->draw_line( $gc, $list[ 0 ], ( $list[ 1 ] - 1 ), $list[ 2 ],
      $list[ 3 ] );

   $pm->draw_line( $gc, $list[ 2 ], $list[ 3 ], $list[ 4 ],
      ( $list[ 5 ] - 1 ) );

   $pb =
    Gtk2::Gdk::Pixbuf->get_from_drawable( $pm, $pm->get_colormap, 0, 0
+, 0, 0,
      $size->width, $size->height );
   $pbinactive = $pb;    #fetch the incative bixbuf

   my @return_value = ( $pbactive, $pbinactive, $pbbm );
   return @return_value;
}


sub CalcPointerMoveWindow() {
   my ( $self, $width, $height, $wbsize, $parentWidget ) = @_;

   my ( $parentX, $parentY, $parentWidth, $parentHeight, $parentDepth 
+);
   my ( $midParentX, $midParentY, $posX, $posY );
   my $ptsize = $wbsize;
   my ( $drawRight, $drawDown );

# test for a real window or an x y option
   if( defined $parentWidget ){    
   ( $parentX, $parentY, $parentWidth, $parentHeight, $parentDepth ) =
                                           $parentWidget->window->get_
+geometry();
   ( $parentX, $parentY ) = $parentWidget->window->get_origin();
   $midParentX = $parentX + ( $parentWidth / 2 );
   $midParentY = $parentY + ( $parentHeight / 2 );
   }else{
      $parentX  = $self->{x};
      $parentY  = $self->{y};
      $midParentX = $self->{x};
      $midParentY = $self->{y};
   }


my ($x0, $y0, $width0, $height0, $depth) = 
       Gtk2::Gdk::Screen->get_default->get_root_window->get_geometry;
 #print "$x0, $y0, $width0, $height0, $depth\n";

# Do we draw to the left or to the right of the icon/window
   if ( $parentX >= $width0/2  ) {
      $drawRight = FALSE;
      $posX      = $midParentX - $width;
   }
   else {
      $drawRight = TRUE;
      $posX      = $midParentX;
   }

# Do we draw above or below the icon/window
   if ( $parentY >=  $height0/2 ) {
      $drawDown = FALSE;
      $posY     = $midParentY - $height;
   }
   else {
      $drawDown = TRUE;
      $posY     = $midParentY;
   }


   $self->move( $posX, $posY );
   $self->show_all();

   my @list;

   if ( $drawRight ) {
      if ( $drawDown ) {
         push @list, $wbsize;
         push @list, ( $wbsize + $ptsize );

         push @list, 0;
         push @list, 0;

         push @list, ( $wbsize + $ptsize );
         push @list, ( $wbsize );
      }
      else {

         push @list, ( $wbsize + $ptsize );
         push @list, ( $height - $wbsize );

         push @list, 0;
         push @list, $height;

         push @list, $wbsize;
         push @list, ( $height - $wbsize - $ptsize );

      }
   }
   else {
      if ( $drawDown ) {
         push @list, ( $width - $wbsize - $ptsize );
         push @list, $wbsize;

         push @list, $width;
         push @list, 0;

         push @list, ( $width - $wbsize );
         push @list, ( $wbsize + $ptsize );
      }
      else {
         push @list, ( $width - $wbsize - $ptsize );
         push @list, ( $height - $wbsize );

         push @list, $width;
         push @list, $height;

         push @list, ( $width - $wbsize );
         push @list, ( $height - $wbsize - $ptsize );
      }
   }

   return @list;
}


sub CalculateRect() {

   my ( $xorg, $yorg, $width, $height ) = @_;

   my @list = (

# top left corner
      $xorg         => ( $yorg + 4 ),
      ( $xorg + 1 ) => ( $yorg + 4 ),
      ( $xorg + 1 ) => ( $yorg + 2 ),
      ( $xorg + 2 ) => ( $yorg + 2 ),
      ( $xorg + 2 ) => ( $yorg + 1 ),
      ( $xorg + 4 ) => ( $yorg + 1 ),
      ( $xorg + 4 ) => ( $yorg ),

# top Right corner
      ( ( $xorg + $height ) - 4 ) => $yorg,
      ( ( $xorg + $height ) - 4 ) => ( $yorg + 1 ),
      ( ( $xorg + $height ) - 2 ) => ( $yorg + 1 ),
      ( ( $xorg + $height ) - 2 ) => ( $yorg + 2 ),
      ( ( $xorg + $height ) - 1 ) => ( $yorg + 2 ),
      ( ( $xorg + $height ) - 1 ) => ( $yorg + 4 ),
      ( ( $xorg + $height ) )     => ( $yorg + 4 ),

# bottom Right corner
      ( $xorg + $height ) => ( ( $yorg + $width ) - 4 ),
      ( ( $xorg + $height ) - 1 ) => ( ( $yorg + $width ) - 4 ),
      ( ( $xorg + $height ) - 1 ) => ( ( $yorg + $width ) - 2 ),
      ( ( $xorg + $height ) - 2 ) => ( ( $yorg + $width ) - 2 ),
      ( ( $xorg + $height ) - 2 ) => ( ( $yorg + $width ) - 1 ),
      ( ( $xorg + $height ) - 4 ) => ( ( $yorg + $width ) - 1 ),
      ( ( $xorg + $height ) - 4 ) => ( $yorg + $width ),

# bottom Left corner
      ( $xorg + 4 ) => ( $yorg + $width ),
      ( $xorg + 4 ) => ( ( $yorg + $width ) - 1 ),
      ( $xorg + 2 ) => ( ( $yorg + $width ) - 1 ),
      ( $xorg + 2 ) => ( ( $yorg + $width ) - 2 ),
      ( $xorg + 1 ) => ( ( $yorg + $width ) - 2 ),
      ( $xorg + 1 ) => ( ( $yorg + $width ) - 4 ),
      $xorg => ( ( $yorg + $width ) - 4 ),
   );
   return @list;
}

package main;

use Gtk2 -init;
use MIME::Base64;

# a 32 X 32 png
my $icodata = decode_base64(
      'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+
+gvaeTAAAACXBI
WXMAAAsTAAALEwEAmpwYAAABwUlEQVRYw+2VvS8EURTFf8/4CKJQTKOgEIlEqFCIRnSUhE
+6iUOhV
Go3Kf0ChkiipNWqFQuErEtFJSEREfO7O0dyNid2Z3X3sbmHfy82bdzPv3TPnnnvHSaKWo4
+EajzqA
OoBGn0MOl+cT+l8pmANugEdb53wBuHIbkdGfAYKYOws0+qTBl4GgyL5ehhUHcFVkX3ERfm
+9jraAq
IrQgLmbewX+TghcgsvKLAFVbA895xFQLgMOFQHstq2ACaMnD5TsklWyIEPGMiFDe3C7nrp
+yVy8AI
0JaQ81mHO3C4oJIp2E0R3TswBKxWJAWIW6M+W4D+LOLd7BaxWPq9xQP3I64teITIxAIvIc
+7t+dPW
N8QdIvwrAKex4PG5guhAjMd8T8bCh4m13wuAqX0H8ZCg+GlEA6IV0YZYLvBOZKycIcaSAD
+hJuebS
B0wCvcAUECbIZgC4AJqAZhNfaNWxA4wWOBMBl8AecAIc2n/lHsQ8Yh9xYvQlzW3EIKIZ0f
+KDsU7z
dyO2TANKYCWyFB0jZkgJmJsZxIZRHqRoBYQzG0AclXD3axqAF8SaT3czED2ILsRmKoQCrj
+vEQtrX
egAKEMOI9R9lHH0BkYPO/OE8jeIAAAAASUVORK5CYII='
   );

my $icopixbuf = do {
     my $loader = Gtk2::Gdk::PixbufLoader->new();
     $loader->write( $icodata );
     $loader->close();
     $loader->get_pixbuf();
  };

my ($xscr, $yscr) = (Gtk2::Gdk->screen_width, Gtk2::Gdk->screen_height
+);
#print "$xscr $yscr\n";

my $window = Gtk2::Window->new;
$window->set_border_width(10);
$window->set_title('Window 0');
my $width = 300;
my $height = 100;
$window->set_size_request($width,$height);
$window->set_gravity('GDK_GRAVITY_SOUTH_EAST');
$window->signal_connect( delete_event => sub { Gtk2->main_quit; 1 } );
$window->move($xscr,$yscr);

my $window1 = Gtk2::Window->new;
$window1->set_border_width(10);
$window1->set_title('Window 1');
$window1->set_size_request($width,$height);
$window1->set_gravity('GDK_GRAVITY_SOUTH_WEST');
$window1->move(0,$yscr);

my $window2 = Gtk2::Window->new;
$window2->set_border_width(10);
$window2->set_title('Window 2');
$window2->set_size_request($width,$height);
$window2->set_gravity('GDK_GRAVITY_NORTH_WEST');
$window2->move(0,0);

my $statusicon = Gtk2::StatusIcon->new_from_pixbuf($icopixbuf);
# will make a nice icon automagically from a file if desired
#my $statusicon = Gtk2::StatusIcon->new_from_file('12uni2.png');

$statusicon->set_tooltip( "Info v1.0" );
#show in tray
$statusicon->set_visible( 1 );

$statusicon->signal_connect( 'activate',   sub { print "1\n" } );
$statusicon->signal_connect( 'popup-menu', \&config_it );

my $vbox = Gtk2::VBox->new;
my $b    = Gtk2::Button->new_from_stock( "gtk-close" );
$b->signal_connect('button_press_event' => sub { exit  });
$vbox->pack_start( $b, TRUE, TRUE, 0 );
$window->add( $vbox );
$vbox->show_all;

$window->show_all();
$window1->show_all();
$window2->show_all();

# messageTypes: info warning question error

my $id = Glib::Timeout->add (3000, sub {
my $test = Gtk2::Ex::ZNotify->new(
   type          => 'popup',
  parent_widget => $window,
   messageType   => "info",
   timeout       => 10000,
   message       => "Info for Window 0",
);
$test->show_all();
 return  0;    # don't run again
 });

# messageTypes: info warning question error
my $id1 = Glib::Timeout->add (4000, sub {
my $test = Gtk2::Ex::ZNotify->new(
   type          => 'popup',
  parent_widget => $window1,
   messageType   => "warning",
   timeout       => 10000,
   message       => "Warning for Window 1",
);
$test->show_all();
 return  0;    # don't run again
 });

# messageTypes: info warning question error
my $id2 = Glib::Timeout->add (5000, sub {
my $test = Gtk2::Ex::ZNotify->new(
   type          => 'popup',
  parent_widget => $window2,
   messageType   => "question",
   timeout       => 10000,
   message       => "Question for Window 2\nWhere is your response?\nW
+e are waiting",
);
$test->show_all();
 return  0;    # don't run again
 });


my $id3 = Glib::Timeout->add (6000, sub {
# statusicon is not a widget, so need to find it's rectangle 
# after it has visibility, but it has no expose event, so
# check it's location, also location may change
   my ($screen,$rect)=$statusicon->get_geometry;
   my ($x,$y,$w, $h) = $rect->values;
#  print "exposed $x $y $w $h\n";

  my $test = Gtk2::Ex::ZNotify->new(
      type          => 'popup',
      #   parent_widget => $window, # undefind, use x y location
      messageType   => "error",
      timeout       => 15000,
      message       => 'Hoo hah Please check now',
      x             => ($x + $w/2),
      y             => ($y + $h ),
   );
 $test->show_all();
 return  0;    # don't run again
  });


Gtk2->main;

sub config_it{

my $menu = Gtk2::Menu->new();
#Quit
   my $menu_quit = Gtk2::ImageMenuItem->new_with_label( "Quit" );
   $menu_quit->signal_connect( activate => sub{ undef $statusicon } );
   $menu_quit->set_image( Gtk2::Image->new_from_stock( 'gtk-quit', 'me
+nu' ) );
   $menu->add( $menu_quit );
   $menu->show_all();

 #to position the menu under the icon, instead of at mouse position
 my ($x, $y, $push_in) = Gtk2::StatusIcon::position_menu($menu, $statu
+sicon);
 print "$x, $y, $push_in\n";
 $menu->popup( undef, undef, sub{return ($x,$y,0)} , undef, 0, 0 );
 return 1;
}

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: sourcecode [id://726821]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (5)
As of 2024-04-18 02:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found