File Coverage

blib/lib/Gtk2/Ex/NoShrink.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Copyright 2007, 2008, 2009, 2010 Kevin Ryde
2              
3             # This file is part of Gtk2-Ex-NoShrink.
4             #
5             # Gtk2-Ex-NoShrink is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Gtk2-Ex-NoShrink is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Gtk2-Ex-NoShrink. If not, see .
17              
18             package Gtk2::Ex::NoShrink;
19 2     2   1705 use 5.008;
  2         7  
  2         85  
20 2     2   13 use strict;
  2         4  
  2         67  
21 2     2   24 use warnings;
  2         3  
  2         73  
22 2     2   1128 use Gtk2;
  0            
  0            
23             use List::Util qw(min max);
24             use POSIX ();
25              
26             our $VERSION = 4;
27              
28             # set this to 1 for some diagnostic prints
29             use constant DEBUG => 0;
30              
31             use Glib::Object::Subclass
32             'Gtk2::Bin',
33             signals => { size_allocate => \&do_size_allocate,
34             size_request => \&do_size_request,
35             },
36             properties => [Glib::ParamSpec->int
37             ('minimum-width',
38             'minimum-width',
39             '',
40             0, POSIX::INT_MAX(), # range
41             0, # default
42             Glib::G_PARAM_READWRITE),
43             Glib::ParamSpec->int
44             ('minimum-height',
45             'minimum-height',
46             '',
47             0, POSIX::INT_MAX(), # range
48             0, # default
49             Glib::G_PARAM_READWRITE),
50              
51             Glib::ParamSpec->double
52             ('shrink-width-factor',
53             'shrink-width-factor',
54             '',
55             0, POSIX::DBL_MAX(), # range
56             0, # default
57             Glib::G_PARAM_READWRITE),
58             Glib::ParamSpec->double
59             ('shrink-height-factor',
60             'shrink-height-factor',
61             '',
62             0, POSIX::DBL_MAX(), # range
63             0, # default
64             Glib::G_PARAM_READWRITE)
65             ];
66              
67             sub INIT_INSTANCE {
68             my ($self) = @_;
69              
70             # NoShrink doesn't draw into the window
71             # (child still gets exposes on any resize though)
72             $self->set_redraw_on_allocate(0);
73              
74             # per defaults in the ParamSpec's above
75             $self->{'minimum_width'} = 0;
76             $self->{'minimum_height'} = 0;
77             $self->{'shrink_width_factor'} = 0;
78             $self->{'shrink_height_factor'} = 0;
79             }
80              
81             sub SET_PROPERTY {
82             my ($self, $pspec, $newval) = @_;
83             my $pname = $pspec->get_name;
84             my $oldval = $self->{$pname};
85             $self->{$pname} = $newval; # per default GET_PROPERTY
86              
87             if ($oldval != $newval) {
88             $self->queue_resize;
89             }
90             }
91              
92             # 'size-request' class closure
93             #
94             # called by anyone interested in how big we want to be -- ask child and add
95             # the border width
96             #
97             sub do_size_request {
98             my ($self, $req) = @_;
99              
100             my $old_min_width = $self->{'minimum_width'};
101             my $old_min_height = $self->{'minimum_height'};
102             my $min_width = $old_min_width;
103             my $min_height = $old_min_height;
104              
105             my $child = $self->get_child;
106             if ($child && $child->visible) {
107             my $creq = $child->size_request;
108              
109             my $width_factor = $self->{'shrink_width_factor'};
110             if ($width_factor > 0 && $creq->width * $width_factor <= $min_width) {
111             $min_width = $creq->width;
112             } else {
113             $min_width = max ($min_width, $creq->width);
114             }
115              
116             my $height_factor = $self->{'shrink_height_factor'};
117             if ($height_factor >0 && $creq->height * $height_factor <= $min_height) {
118             $min_height = $creq->height;
119             } else {
120             $min_height = max ($min_height, $creq->height);
121             }
122             }
123              
124             if (DEBUG) {
125             if ($min_width != $old_min_width || $min_height != $old_min_height) {
126             print $self->get_name," request ",$min_width,"x",$min_height,
127             ", extending min ",$old_min_width,"x",$old_min_height;
128             if ($child) {
129             my $creq = $child->size_request;
130             print ", for child ", ($child->visible ? '' : '(not visible) '),
131             "req ",$creq->width,"x",$creq->height;
132             }
133             print " border ",$self->get_border_width,"\n";
134             }
135             }
136              
137             # set and notify any new minimum for the width/height from the child
138             #
139             $self->{'minimum_width'} = $min_width;
140             $self->{'minimum_height'} = $min_height;
141             # believe cleanest to notify after both width and height updated
142             if ($old_min_width != $min_width) { $self->notify ('minimum-width'); }
143             if ($old_min_height != $min_height) { $self->notify ('minimum-height'); }
144              
145             my $border_width = $self->get_border_width;
146             $req->width ($min_width + 2*$border_width);
147             $req->height ($min_height + 2*$border_width);
148             }
149              
150             # 'size-allocate' class closure
151             #
152             # called by our parent to give us actual allocated space -- pass this down
153             # to the child, less the border width
154             #
155             sub do_size_allocate {
156             my ($self, $alloc) = @_;
157             if (my $child = $self->get_child) {
158             my $border_width = $self->get_border_width;
159             my $x = $alloc->x + $border_width;
160             my $y = $alloc->y + $border_width;
161             my $width = max (1, $alloc->width - 2*$border_width);
162             my $height = max (1, $alloc->height - 2*$border_width);
163              
164             my $child_alloc = $child->allocation;
165              
166             if (DEBUG) {
167             my $creq = $child->size_request;
168             print "NoShrink child alloc ${width}x${height} at $x,$y",
169             ", vs child req ",$creq->width,"x",$creq->height,
170             ", and current child ",
171             $child_alloc->x,",",$child_alloc->y,
172             " ",$child_alloc->width,"x",$child_alloc->height,
173             "\n";
174             }
175             if ($x != $child_alloc->x
176             || $y != $child_alloc->y
177             || $width != $child_alloc->width
178             || $height != $child_alloc->height) {
179             $child->size_allocate (Gtk2::Gdk::Rectangle->new ($x, $y, $width, $height));
180             }
181             }
182             }
183              
184             1;
185             __END__