[perl-Gtk2] Add offscreen window examples



commit 2a9ccb4271c31d565be38ce9664361954b99844d
Author: Quentin Sculo <squentin free fr>
Date:   Wed May 12 00:43:52 2010 +0200

    Add offscreen window examples

 examples/offscreen_reflection.pl |  156 ++++++++++++++++++++++++++++
 examples/offscreen_rotation.pl   |  212 ++++++++++++++++++++++++++++++++++++++
 examples/offscreen_scale.pl      |  130 +++++++++++++++++++++++
 3 files changed, 498 insertions(+), 0 deletions(-)
---
diff --git a/examples/offscreen_reflection.pl b/examples/offscreen_reflection.pl
new file mode 100644
index 0000000..1427472
--- /dev/null
+++ b/examples/offscreen_reflection.pl
@@ -0,0 +1,156 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Glib qw(TRUE FALSE);
+use Gtk2 '-init';
+use Cairo;
+
+my $window = Gtk2::Window->new;
+$window->set_title("Reflection example");
+$window->signal_connect( delete_event => sub { exit } );
+$window->set_border_width(10);
+
+my $reflected= Gtk2::Ex::ReflectBin->new;
+
+my $entry=Gtk2::Entry->new;
+my $button1=Gtk2::Button->new;
+$button1->add( Gtk2::Image->new_from_stock('gtk-go-back','button') );
+$button1->signal_connect(clicked=>sub {warn "button 1 clicked\n";});
+my $button2=Gtk2::Button->new;
+$button2->add( Gtk2::Image->new_from_stock('gtk-apply','button') );
+$button2->signal_connect(clicked=>sub {warn "button 2 clicked\n";});
+
+my $hbox=Gtk2::HBox->new;
+$hbox->pack_start($button1,FALSE,FALSE,2);
+$hbox->add($entry);
+$hbox->pack_start($button2,FALSE,FALSE,2);
+
+$reflected->add($hbox);
+$window->add($reflected);
+$window->show_all;
+
+Gtk2->main;
+
+
+
+package Gtk2::Ex::ReflectBin;
+use Gtk2;
+use Glib::Object::Subclass
+	Gtk2::EventBox::,
+	signals =>
+	{	size_allocate	=> \&do_size_allocate,
+		size_request	=> \&do_size_request,
+	};
+
+sub INIT_INSTANCE {
+	my $self=shift;
+	$self->signal_connect( expose_event	=> \&expose_cb);
+	$self->signal_connect( damage_event	=> \&damage_cb);
+	$self->signal_connect( realize		=> \&realize_cb);
+	return $self;
+}
+
+sub do_size_request {
+	my ($self,$req)= _;
+	my $border= $self->get_border_width;
+	return unless $self->child;
+	my $child_req=$self->child->size_request;
+	my $w= $child_req->width;
+	my $h= $child_req->height;
+	$req->width( $w +$border*2 );
+	$req->height( $h*2 +$border*2 );
+}
+
+sub do_size_allocate {
+	my ($self,$alloc)= _;
+	my $border= $self->get_border_width;
+	my ($x,$y,$w,$h)=$alloc->values;
+	my $olda=$self->allocation;
+	 $olda->x($x); $olda->width($w);
+	 $olda->y($y); $olda->height($h);
+	$w-= 2*$border;
+	$h-= 2*$border;
+	$self->window->move_resize($x+$border,$y+$border,$w,$h) if $self->window;
+	if (my $child=$self->child) {
+		my $req= $child->size_request;
+		my $rect=Gtk2::Gdk::Rectangle->new(0,0,$w,$h/2);
+		$self->child->size_allocate($rect);
+	}
+	if (my $offscreen= $self->{offscreen}) {
+		$offscreen->move_resize(0,0,$w,$h/2);
+		$offscreen->geometry_changed;
+	}
+}
+
+sub damage_cb {
+	my ($self,$event)= _;
+	# invalidate the whole window, it could be better to invalidate $event->area and the rectangle containing its reflection
+	$self->window->invalidate_rect(undef,0);
+	return 1;
+}
+
+sub realize_cb {
+	my ($self)= _;
+	my $border= $self->get_border_width;
+	my ($x,$y,$w,$h)=$self->allocation->values;
+	my %attr=
+	 (	window_type	=> 'offscreen',
+		wclass		=> 'output',
+		x		=> 0,
+		y		=> 0,
+		width		=> $w-$border*2,
+		height		=> ($h-$border*2)/2,
+		event_mask	=> [qw/pointer-motion-mask button-press-mask button-release-mask exposure_mask/],
+	 );
+	$self->{offscreen}= my $offscreen= Gtk2::Gdk::Window->new($self->get_root_window,\%attr);
+	$offscreen->set_user_data($self->Glib::Object::get_pointer());
+	$self->window->signal_connect( pick_embedded_child =>sub { return $offscreen; });
+	$self->child->set_parent_window($offscreen) if $self->child;
+	$offscreen->set_embedder($self->window);
+	$offscreen->signal_connect( to_embedder  => sub {my ($offscreen,$x,$y)= _; return $x,$y });
+	$offscreen->signal_connect( from_embedder=> sub {my ($offscreen,$x,$y)= _; return $x,$y });
+	$self->style->set_background($offscreen,'normal');
+	$offscreen->show;
+}
+
+sub expose_cb {
+	my ($self,$event)= _;
+	my $offscreen= $self->{offscreen};
+	if ($event->window == $self->window) {
+		my $pixmap = $offscreen->get_pixmap;
+		return 1 unless $pixmap && $self->child;
+		my (undef,$height)= $pixmap->get_size;
+		my $cr=Gtk2::Gdk::Cairo::Context->create($self->window);
+		my $child_alloc= $self->child->allocation;
+		$cr->rectangle($event->area);
+		$cr->clip;
+		$cr->save; # the above clip is used for both draw
+		 # draw normal version on upper half (0..$height)
+		 $cr->rectangle($child_alloc);
+		 $cr->clip;
+		 $cr->set_source_pixmap($pixmap,0,0);
+		 $cr->paint;
+		 $cr->restore;
+		# draw reflection on lower half ($height..$height*2)
+		my $mask= Cairo::LinearGradient->create(0,$height,0,$height*2);
+		$mask->add_color_stop_rgba(0,    0, 0, 0, 1   );
+		$mask->add_color_stop_rgba(0.25, 0, 0, 0, 0.5 );
+		$mask->add_color_stop_rgba(0.5,  0, 0, 0, 0.25);
+		$mask->add_color_stop_rgba(0.75, 0, 0, 0, 0.1 );
+		$mask->add_color_stop_rgba(1.0,  0, 0, 0, 0   );
+		my $matrix= Cairo::Matrix->init(1, 0, .3, 1, 0, 0);
+		$matrix->translate(-10,$height*2);
+		$matrix->scale(1,-1);
+		$cr->transform($matrix);
+		$mask->set_matrix($matrix);
+		$cr->rectangle($child_alloc);
+		$cr->clip;
+		$cr->set_source_pixmap($pixmap,0,0);
+		$cr->mask($mask);
+	}
+	elsif ($event->window == $offscreen) {
+		$self->propagate_expose($self->child,$event) if $self->child;
+	}
+	1;
+}
+
diff --git a/examples/offscreen_rotation.pl b/examples/offscreen_rotation.pl
new file mode 100644
index 0000000..b993bc5
--- /dev/null
+++ b/examples/offscreen_rotation.pl
@@ -0,0 +1,212 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Glib qw(TRUE FALSE);
+use Gtk2 '-init';
+use Cairo;
+
+my $window = Gtk2::Window->new;
+$window->set_title("Rotation example");
+$window->signal_connect( delete_event => sub { exit } );
+$window->set_border_width(5);
+
+my $rotated= Gtk2::Ex::RotatedBin->new;
+
+my $entry=Gtk2::Entry->new;
+my $button=Gtk2::Button->new_from_stock('gtk-ok');
+$button->signal_connect(clicked=>sub {warn "clicked\n";});
+
+my $checkh=Gtk2::CheckButton->new('mirror horizontally');
+$checkh->signal_connect(toggled=>sub { $rotated->set_mirror( horizontal=> $_[0]->get_active ) });
+my $checkv=Gtk2::CheckButton->new('mirror vertically');
+$checkv->signal_connect(toggled=>sub { $rotated->set_mirror( vertical=> $_[0]->get_active ) });
+
+my $adj=Gtk2::Adjustment->new(10, 0, 360, 1,10,0);
+$adj->signal_connect(value_changed=> sub { $rotated->set_angle( $_[0]->value ); });
+my $scale=Gtk2::HScale->new($adj);
+
+my $vbox=Gtk2::VBox->new;
+$vbox->add($_) for $entry,$button,$checkh,$checkv,$scale;
+
+$rotated->add($vbox);
+$window->add($rotated);
+$window->show_all;
+
+#Gtk2::Gdk::Window->set_debug_updates(1);
+Gtk2->main;
+
+
+#FIXME the child needs to be added before the RotatedBin is realized
+# I haven't managed to make it work if the child is added or replaced after
+package Gtk2::Ex::RotatedBin;
+use Gtk2;
+use List::Util qw/min max/;
+use Glib::Object::Subclass
+	Gtk2::EventBox::,
+	signals =>
+	{	size_allocate	=> \&do_size_allocate,
+		size_request	=> \&do_size_request,
+	};
+use constant PI => 4 * atan2(1,1); # needed for the rotation
+
+sub INIT_INSTANCE {
+	my $self=shift;
+	$self->{angle}=10;
+	$self->signal_connect( expose_event	=> \&expose_cb);
+	$self->signal_connect( damage_event	=> \&damage_cb);
+	$self->signal_connect( realize		=> \&realize_cb);
+	return $self;
+}
+
+sub do_size_request {
+	my ($self,$req)= _;
+	my $border= $self->get_border_width;
+	my $child_req=$self->child->size_request;
+	my $w= $child_req->width;
+	my $h= $child_req->height;
+	my $diag= sqrt( $w**2 + $h**2 );
+	$diag= 1+int $diag  unless int($diag)==$diag; #round up
+	# request enough to satisfy the child request for any angle
+	$req->width( $diag+$border*2 );
+	$req->height( $diag+$border*2 );
+}
+
+sub do_size_allocate {
+	my ($self,$alloc)= _;
+	my $border= $self->get_border_width;
+	my ($x,$y,$w,$h)=$alloc->values;
+	my $olda=$self->allocation;
+	 $olda->x($x); $olda->width($w);
+	 $olda->y($y); $olda->height($h);
+	$w-= 2*$border;
+	$h-= 2*$border;
+	$self->window->move_resize($x+$border,$y+$border,$w,$h) if $self->window;
+	$self->update_matrix;
+}
+
+sub set_angle {
+	my ($self,$angle)= _;
+	$self->{angle}=$angle;
+	$self->update_matrix;
+	$self->queue_resize;
+}
+
+sub set_mirror {
+	my ($self,$h_or_v,$on)= _;
+	my $key= $h_or_v eq 'vertical' ? 'vmirror' : 'hmirror';
+	$self->{$key}=$on;
+	$self->update_matrix;
+	$self->queue_draw;
+}
+
+# transform the rectangle and find a rectangle containing the transformed rectangle
+sub transform_expose_rectangle {
+	my ($self,$rect,$inv)= _;
+	my ($x,$y,$w,$h)=$rect->values;
+	my $matrix= $inv ? $self->{imatrix} : $self->{matrix};
+	my ($xa,$ya)=$matrix->transform_point($x,   $y);
+	my ($xb,$yb)=$matrix->transform_point($x+$w,$y);
+	my ($xc,$yc)=$matrix->transform_point($x,   $y+$h);
+	my ($xd,$yd)=$matrix->transform_point($x+$w,$y+$h);
+	$x= min($xa,$xb,$xc,$xd);
+	$y= min($ya,$yb,$yc,$yd);
+	$w= max($xa,$xb,$xc,$xd) -$x;
+	$h= max($ya,$yb,$yc,$yd) -$y;
+	return Gtk2::Gdk::Rectangle->new($x,$y,$w,$h);
+}
+
+sub update_matrix {
+	my $self=shift;
+	my ($x,$y,$w,$h)= $self->allocation->values;
+	my $border= $self->get_border_width;
+	$x+=$border; $w-=2*$border;
+	$y+=$border; $h-=2*$border;
+	my $angle= $self->{angle}*PI/180;
+	my $matrix0=Cairo::Matrix->init_rotate($angle);
+	my ($xa,$ya)=$matrix0->transform_distance(0,0);
+	my ($xb,$yb)=$matrix0->transform_distance($w,0);
+	my ($xc,$yc)=$matrix0->transform_distance(0,$h);
+	my ($xd,$yd)=$matrix0->transform_distance($w,$h);
+	my $rw= $w / ( max($xa,$xb,$xc,$xd) - min($xa,$xb,$xc,$xd) );
+	my $rh= $h / ( max($ya,$yb,$yc,$yd) - min($ya,$yb,$yc,$yd) );
+	my $r= min($rw,$rh);
+	my $cw= $w*$r;
+	my $ch= $h*$r;
+
+	my $matrix=Cairo::Matrix->init_identity;
+	$matrix->translate($w/2,$h/2);
+	$matrix->rotate($angle);
+	$matrix->translate( -$cw/2,-$ch/2 );
+	if ($self->{hmirror}) {
+		$matrix->scale(-1,1);
+		$matrix->translate( -$cw,0 );
+	}
+	if ($self->{vmirror}) {
+		$matrix->scale(1,-1);
+		$matrix->translate( 0,-$ch );
+	}
+	$self->{matrix}=$matrix;
+
+	my $imatrix= $matrix->multiply( Cairo::Matrix->init_identity ); #copy matrix
+	$imatrix->invert;
+	$self->{imatrix}= $imatrix;
+
+	if (my $o=$self->{offscreen})
+	{	$o->{matrix}= $self->{matrix};
+		$o->{imatrix}=$self->{imatrix};
+		$o->move_resize(0,0,$cw,$ch);
+		$o->geometry_changed;
+	}
+	if (my $child=$self->child)
+	{	my $rect=Gtk2::Gdk::Rectangle->new(0,0, $cw,$ch);
+		$self->child->size_allocate($rect);
+	}
+}
+
+sub damage_cb {
+	my ($self,$event)= _;
+	my $rect= transform_expose_rectangle($self,$event->area);
+	$self->window->invalidate_rect( $rect, 0);
+	1;
+}
+
+sub realize_cb {
+	my ($self)= _;
+	my ($x,$y,$w,$h)=$self->allocation->values;
+	my %attr=
+	 (	window_type	=> 'offscreen',
+		wclass		=> 'output',
+		event_mask	=> [qw/pointer-motion-mask button-press-mask button-release-mask exposure_mask/],
+	 );
+	$self->{offscreen}= my $offscreen= Gtk2::Gdk::Window->new($self->get_root_window,\%attr);
+	$self->update_matrix;	# will resize $offscreen to the correct size
+	$offscreen->set_user_data($self->Glib::Object::get_pointer());
+	$self->window->signal_connect( pick_embedded_child =>sub { return $offscreen; }); #could check if transformed position is inside offscreen window
+	$self->child->set_parent_window($offscreen) if $self->child;
+	$offscreen->set_embedder($self->window);
+	$offscreen->signal_connect( to_embedder  => sub { return $_[0]{ matrix}->transform_point($_[1],$_[2]); });
+	$offscreen->signal_connect( from_embedder=> sub { return $_[0]{imatrix}->transform_point($_[1],$_[2]); });
+	$self->style->set_background($offscreen,'normal');
+	$offscreen->show;
+}
+
+sub expose_cb {
+	my ($self,$event)= _;
+	my $offscreen= $self->{offscreen};
+	if ($event->window == $self->window) {
+		my $pixmap = $offscreen->get_pixmap;
+		return 1 unless $pixmap;
+		my $cr=Gtk2::Gdk::Cairo::Context->create($self->window);
+		$cr->rectangle($event->area);
+		$cr->clip;
+		$cr->set_matrix( $self->{matrix} );
+		$cr->set_source_pixmap($pixmap,0,0);
+		$cr->paint;
+	}
+	elsif ($event->window == $offscreen) {
+		$self->propagate_expose($self->child,$event) if $self->child;
+	}
+	1;
+}
+
+
diff --git a/examples/offscreen_scale.pl b/examples/offscreen_scale.pl
new file mode 100644
index 0000000..9b3c5d8
--- /dev/null
+++ b/examples/offscreen_scale.pl
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Glib qw(TRUE FALSE);
+use Gtk2 '-init';
+use Cairo;
+
+my $window = Gtk2::Window->new;
+$window->set_title("Resize me");
+$window->signal_connect( delete_event => sub { exit } );
+$window->set_border_width(5);
+
+my $scaled= Gtk2::Ex::ScaleBin->new;
+
+my $entry=Gtk2::Entry->new;
+my $button=Gtk2::Button->new_from_stock('gtk-ok');
+$button->signal_connect(clicked=>sub {warn "clicked\n";});
+
+my $vbox=Gtk2::VBox->new;
+$vbox->add($entry);
+$vbox->add($button);
+
+$scaled->add($vbox);
+$window->add($scaled);
+$window->show_all;
+
+
+Gtk2->main;
+
+
+
+package Gtk2::Ex::ScaleBin;
+use Gtk2;
+use Glib::Object::Subclass
+	Gtk2::EventBox::,
+	signals =>
+	{	size_allocate	=> \&do_size_allocate,
+	};
+
+sub INIT_INSTANCE {
+	my $self=shift;
+	$self->signal_connect( expose_event	=> \&expose_cb);
+	$self->signal_connect( damage_event	=> \&damage_cb);
+	$self->signal_connect( realize		=> \&realize_cb);
+	return $self;
+}
+
+sub do_size_allocate {
+	my ($self,$alloc)= _;
+	my $border= $self->get_border_width;
+	my ($x,$y,$w,$h)=$alloc->values;
+	my $olda=$self->allocation;
+	 $olda->x($x); $olda->width($w);
+	 $olda->y($y); $olda->height($h);
+	$w-= 2*$border;
+	$h-= 2*$border;
+	$self->window->move_resize($x+$border,$y+$border,$w,$h) if $self->window;
+	my ($reqw,$reqh)= ($w,$h);
+	if (my $child=$self->child) {
+		my $req= $child->size_request;
+		$reqw= $req->width;
+		$reqh= $req->height;
+		my $rect=Gtk2::Gdk::Rectangle->new(0, 0, $reqw, $reqh);
+		$self->child->size_allocate($rect);
+	}
+	$self->{zoom_x}= $w / $reqw;
+	$self->{zoom_y}= $h / $reqh;
+	if (my $offscreen= $self->{offscreen}) {
+		$offscreen->{zoom_x}= $self->{zoom_x};
+		$offscreen->{zoom_y}= $self->{zoom_y};
+		$offscreen->move_resize(0,0, $reqw, $reqh);
+		$offscreen->geometry_changed;
+	}
+}
+
+sub damage_cb {
+	my ($self,$event)= _;
+	my ($x,$y,$w,$h)=$event->area->values;
+	my $zx= $self->{zoom_x};
+	my $zy= $self->{zoom_y};
+	my $rect=Gtk2::Gdk::Rectangle->new($x*$zx, $y*$zy, $w*$zx, $h*$zy);
+	$self->window->invalidate_rect($rect,0);
+	1;
+}
+
+sub realize_cb {
+	my ($self)= _;
+	my ($x,$y,$w,$h)=$self->allocation->values;
+	my %attr=
+	 (	window_type	=> 'offscreen',
+		wclass		=> 'output',
+		x		=> 0,
+		y		=> 0,
+		width		=> $w,
+		height		=> $h,
+		event_mask	=> [qw/pointer-motion-mask button-press-mask button-release-mask exposure_mask/],
+	 );
+	$self->{offscreen}= my $offscreen= Gtk2::Gdk::Window->new($self->get_root_window,\%attr);
+	$offscreen->set_user_data($self->Glib::Object::get_pointer());
+	$self->window->signal_connect( pick_embedded_child =>sub { return $offscreen; });
+	$self->child->set_parent_window($offscreen) if $self->child;
+	$offscreen->set_embedder($self->window);
+		$offscreen->{zoom_x}= $self->{zoom_x};
+		$offscreen->{zoom_y}= $self->{zoom_y};
+	$offscreen->signal_connect( to_embedder  => sub {my ($offscreen,$x,$y)= _; return $x*$offscreen->{zoom_x},$y*$offscreen->{zoom_y} });
+	$offscreen->signal_connect( from_embedder=> sub {my ($offscreen,$x,$y)= _; return $x/$offscreen->{zoom_x},$y/$offscreen->{zoom_y} });
+	$self->style->set_background($offscreen,'normal');
+	$offscreen->show;
+}
+
+sub expose_cb {
+	my ($self,$event)= _;
+	my $offscreen= $self->{offscreen};
+	if ($event->window == $self->window) {
+		my $pixmap = $offscreen->get_pixmap;
+		return 1 unless $pixmap;
+		my ($w,$h)= $pixmap->get_size;
+		my $cr=Gtk2::Gdk::Cairo::Context->create($self->window);
+		$cr->rectangle($event->area);
+		$cr->clip;
+		$cr->scale($self->{zoom_x},$self->{zoom_y});
+		$cr->set_source_pixmap($pixmap,0,0);
+		$cr->paint;
+	}
+	elsif ($event->window == $offscreen) {
+		$self->propagate_expose($self->child,$event) if $self->child;
+	}
+	1;
+}
+



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]