File Coverage

blib/lib/TkUtil/Configure.pm
Criterion Covered Total %
statement 12 98 12.2
branch 0 28 0.0
condition 0 12 0.0
subroutine 4 10 40.0
pod 2 2 100.0
total 18 150 12.0


line stmt bran cond sub pod time code
1             package TkUtil::Configure;
2              
3 1     1   23021 use warnings;
  1         4  
  1         28  
4 1     1   6 use strict;
  1         1  
  1         37  
5 1     1   864 use Perl6::Attributes;
  1         32881  
  1         6  
6 1     1   3219 use Time::HiRes qw(gettimeofday);
  1         7677  
  1         5  
7              
8             =head1 NAME
9              
10             TkUtil::Configure - Trap and act on Tk events
11              
12             =head1 VERSION
13              
14             Version 0.03
15              
16             =cut
17              
18             our $VERSION = '0.03';
19              
20              
21             =head1 SYNOPSIS
22              
23             Fairly intelligent trapping of events within Perl/Tk.
24              
25             use TkUtil::Configure;
26              
27             my $conf = TkUtil::Configure->new(top => $mw, callback => ??);
28              
29             All you currently have is the constructor, because that's all that
30             is needed. See below for additional information.
31              
32             =head1 DESCRIPTION
33              
34             In Perl/Tk programming, you often want to bind to the event
35             so that you can elegantly resize your internal windows when the main
36             window is resized.
37              
38             The problem is that a simple resize can generate hundreds of resize
39             events. And if the job you must do in a window is complex or time
40             consuming, handling all of these events can be problematic.
41              
42             That's what this class was written for... to bind the
43             event, and deal with all of the incoming events in a reasonable fashion,
44             and only call your callback function(s) when we think the user is
45             done.
46              
47             The callback function(s) are where you do the necessary redrawing of
48             the window(s), of course.
49              
50             This was written (and made available) because too many people struggle
51             with this issue (me included). Most people simply give up and don't
52             allow (or deal with) resize at all, because the issue is such a problem.
53             Enjoy.
54              
55             =head1 AUTHOR
56              
57             X Cramps, C<< >>
58              
59             =head1 BUGS
60              
61             Please report any bugs or feature requests to C, or through
62             the web interface at L. I will be notified, and then you'll
63             automatically be notified of progress on your bug as I make changes.
64              
65             =head1 SUPPORT
66              
67             You can find documentation for this module with the perldoc command.
68              
69             perldoc TkUtil::Configure
70              
71             You can also look for information at:
72              
73             =over 4
74              
75             =item * RT: CPAN's request tracker
76              
77             L
78              
79             =item * AnnoCPAN: Annotated CPAN documentation
80              
81             L
82              
83             =item * CPAN Ratings
84              
85             L
86              
87             =item * Search CPAN
88              
89             L
90              
91             =back
92              
93             =head1 COPYRIGHT & LICENSE
94              
95             Copyright 2009 X Cramps, all rights reserved.
96              
97             This program is free software; you can redistribute it and/or modify it
98             under the same terms as Perl itself.
99              
100             =cut
101              
102             our $Class;
103              
104             =head2 B
105              
106             $conf = TkUtil::Configure->new(top => ??, callback => ??, %opts);
107              
108             %opts can be:
109              
110             on - provide a widget id to trigger the callback for [1]
111             timeout - amount of time before a callback is generated (in msec) [2]
112              
113             I is the toplevel widget upon which to bind the
114             event.
115              
116             Note that both I and I can be array references. You can
117             have multiple widgets specified in I and only a single I
118             if you like (since the first argument to the callback is the widget,
119             the callback can behave differently based upon it).
120              
121             [1] callback is called when the top widget is configured (resized). It
122             is called with the widget id and the new width and height of the
123             widget under
124             consideration (I). I is the widget id to trigger this
125             particular callback for.
126              
127             [2] when a widget is resized, we get LOTS of events.
128             Even with fast computers, you can overload with events if
129             you need to do something complex when the user resizes. The
130             timeout allows you to build up events until the last event
131             was I msec ago, and only then trigger a callback.
132             The default is 500 msec (1/2 second). Your callback won't
133             be called unless I msec has elapsed from the last
134             event.
135              
136             =cut
137              
138             sub new {
139 0     0 1   my $class = shift;
140 0           $Class = $class;
141 0           my (%opts) = @_;
142 0           my $self = \%opts;
143 0           bless $self, $class;
144 0 0         $.timeout = 500 unless defined $.timeout;
145 0           $.Pending = 0;
146 0           ./_required('top');
147 0           ./_required('callback');
148 0 0         $.on = $.top unless defined $.on;
149 0           $.widgets = {};
150              
151             # ensure that if both on and callback are array ref's, they
152             # have same # of elements
153 0 0 0       if (ref($.callback) eq 'ARRAY' && ref($.on) eq 'ARRAY') {
154 0           die "$class - need same number of things in callback and on\n"
155 0 0         unless @.callback == @.on;
  0            
156             }
157              
158             # many callbacks and a single on => call all callbacks for same
159             # widget
160 0 0 0       if (ref($.callback) eq 'ARRAY' && ref($.on) ne 'ARRAY') {
161 0           my $n = @{$.callback};
  0            
162 0           my @on;
163 0           push(@on, $.on) foreach 1..$n;
164 0           $.on = \@on;
165             }
166              
167             # one on and a single callback => same callback for all widgets
168 0 0 0       if (ref($.on) eq 'ARRAY' && ref($.callback) ne 'ARRAY') {
169 0           my $n = @{$.on};
  0            
170 0           my @callback;
171 0           push(@callback, $.callback) foreach 1..$n;
172 0           $.callback = \@callback;
173             }
174              
175             # ensure both on and callback are arrays
176 0 0         if (ref($.on) ne 'ARRAY') {
177 0           $.on = [$.on];
178             }
179              
180 0 0         if (ref($.callback) ne 'ARRAY') {
181 0           $.callback = [$.callback];
182             }
183              
184             # make hashes of the dual on/callback arrays
185 0           my %on;
186 0           for (my $i=0; $i < @{$.on}; $i++) {
  0            
187 0           $on{$.on[$i]} = $.callback[$i];
188 0           $.widgets{$.on[$i]} = $.on[$i];
189             }
190              
191 0           $.on = \%on;
192              
193             # cleanly initialize
194 0           $.events = [];
195 0           ./_init();
196 0           return $self;
197             }
198              
199             # test for required args
200             sub _required {
201 0     0     my ($self, $name) = @_;
202 0 0         die "$Class - $name must be defined\n" unless defined $self->{$name};
203             }
204              
205             # set things up for binding to
206             sub _init {
207 0     0     my ($self) = @_;
208 0           my @events = @{$.events};
  0            
209             $.top->bind('',
210             sub {
211 0     0     my ($W, @args) = @_;
212 0           my $event = $W->XEvent;
213 0 0         return unless defined $event;
214 0 0 0       return unless defined $.on && defined $.on{$W};
215 0           my ($w, $h) = ($event->w, $event->h);
216 0           my $t = ./_t();
217 0           push(@{$.events}, [$w, $h, $t]);
  0            
218             # only trigger timer if one isn't already pending
219 0 0         if ($.Pending == 0) {
220 0           $.Pending = 1;
221 0           my $timerProc;
222             $timerProc =
223             sub {
224 0           my ($timeout) = @_;
225             $.top->after($timeout,
226             sub {
227 0           my @events = @{$.events};
  0            
228 0           my $t = ./_t();
229 0           $.Pending = 0;
230 0 0         if (@events) {
231 0           my $n = $#events;
232             # get most recent width/height
233 0           my $w = $events[$n]->[0];
234 0           my $h = $events[$n]->[1];
235 0           my $dt = $t - $events[$n]->[2];
236             #print "dt = $dt, timeout $timeout\n";
237 0 0         if ($dt > $timeout/1000) {
238 0           $.culled = scalar(@events);
239 0           foreach my $widget (keys(%{$.on})) {
  0            
240 0           my $ow = $.widgets{$widget};
241 0           $w = $ow->width;
242 0           $h = $ow->height;
243 0           $.on{$widget}->($ow, $w, $h);
244             }
245 0           $.events = [];
246             }
247             else {
248 0           $.Pending = 1;
249 0           $timerProc->($timeout);
250             }
251             }
252             }
253 0           );
254 0           };
255 0           $timerProc->($.timeout);
256             }
257             }
258 0           );
259              
260             }
261              
262             =head2 B
263              
264             $conf->culled();
265              
266             Find how many events were culled for you.
267              
268             =cut
269              
270             sub culled {
271 0     0 1   my ($self) = @_;
272 0           return $.culled;
273             }
274              
275             sub _t {
276 0     0     my ($seconds, $microseconds) = gettimeofday;
277 0           my $t = $seconds + $microseconds/1000000;
278 0           return $t;
279             }
280             1; # End of TkUtil::Configure