Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
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 learning in the Monastery: (4)
As of 2024-04-19 03:34 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found