ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.76
Committed: Tue Apr 11 21:24:09 2006 UTC (18 years, 1 month ago) by root
Branch: MAIN
Changes since 1.75: +129 -39 lines
Log Message:
functional but unconnected setup menu in opengl

File Contents

# User Rev Content
1 root 1.73 package CFClient::UI;
2 root 1.8
3 elmex 1.1 use strict;
4 root 1.18
5 root 1.74 use Scalar::Util ();
6     use List::Util ();
7 root 1.18
8 root 1.60 use CFClient;
9 root 1.41
10 root 1.51 our ($FOCUS, $HOVER, $GRAB); # various widgets
11    
12     our $TOPLEVEL;
13     our $BUTTON_STATE;
14    
15 elmex 1.1 # class methods for events
16 root 1.51 sub feed_sdl_key_down_event {
17     $FOCUS->key_down ($_[0]) if $FOCUS;
18     }
19    
20     sub feed_sdl_key_up_event {
21     $FOCUS->key_up ($_[0]) if $FOCUS;
22     }
23    
24     sub feed_sdl_button_down_event {
25     my ($ev) = @_;
26     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
27    
28     if (!$BUTTON_STATE) {
29     my $widget = $TOPLEVEL->find_widget ($x, $y);
30    
31     $GRAB = $widget;
32     $GRAB->update if $GRAB;
33     }
34    
35     $BUTTON_STATE |= 1 << ($ev->button - 1);
36    
37 root 1.58 $GRAB->button_down ($ev, $GRAB->translate ($x, $y)) if $GRAB;
38 root 1.51 }
39    
40     sub feed_sdl_button_up_event {
41     my ($ev) = @_;
42     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
43    
44     my $widget = $GRAB || $TOPLEVEL->find_widget ($x, $y);
45    
46     $BUTTON_STATE &= ~(1 << ($ev->button - 1));
47    
48 root 1.58 $GRAB->button_down ($ev, $GRAB->translate ($x, $y)) if $GRAB;
49    
50 root 1.51 if (!$BUTTON_STATE) {
51     my $grab = $GRAB; undef $GRAB;
52     $grab->update if $grab;
53     $GRAB->update if $GRAB;
54     }
55     }
56    
57     sub feed_sdl_motion_event {
58     my ($ev) = @_;
59     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
60    
61     my $widget = $GRAB || $TOPLEVEL->find_widget ($x, $y);
62    
63     if ($widget != $HOVER) {
64     my $hover = $HOVER; $HOVER = $widget;
65    
66     $hover->update if $hover;
67     $HOVER->update if $HOVER;
68     }
69    
70 root 1.58 $HOVER->mouse_motion ($ev, $HOVER->translate ($x, $y)) if $HOVER;
71 root 1.51 }
72 elmex 1.1
73 root 1.73 #############################################################################
74    
75     package CFClient::UI::Base;
76    
77     use strict;
78    
79     use SDL::OpenGL;
80    
81 elmex 1.1 sub new {
82     my $class = shift;
83 root 1.10
84 root 1.65 bless {
85     x => 0,
86     y => 0,
87     z => 0,
88 root 1.75 w => -1,
89     h => -1,
90 root 1.65 @_
91     }, $class
92 elmex 1.1 }
93    
94 root 1.18 sub move {
95     my ($self, $x, $y, $z) = @_;
96     $self->{x} = $x;
97     $self->{y} = $y;
98     $self->{z} = $z if defined $z;
99     }
100    
101 elmex 1.20 sub needs_redraw {
102     0
103     }
104    
105 root 1.14 sub size_request {
106 elmex 1.36 require Carp;
107     Carp::confess "size_request is abtract";
108     }
109    
110 root 1.75 sub _size_allocate {
111     my ($self, $x, $y, $w, $h) = @_;
112    
113     $self->{x} = $x;
114     $self->{y} = $y;
115    
116     return unless $self->{w} != $w || $self->{h} != $h;
117 root 1.40
118     $self->{w} = $w;
119     $self->{h} = $h;
120 root 1.75
121     1
122     }
123    
124     sub size_allocate {
125     my ($self, $x, $y, $w, $h) = @_;
126    
127     $self->_size_allocate ($x, $y, $w, $h);
128 root 1.14 }
129    
130 root 1.58 # translate global koordinates to local coordinate system
131     sub translate {
132     my ($self, $x, $y) = @_;
133    
134     $self->{parent}->translate ($x - $self->{x}, $y - $self->{y});
135     }
136    
137 elmex 1.1 sub focus_in {
138 root 1.51 my ($self) = @_;
139    
140 root 1.68 return if $FOCUS == $self;
141    
142 root 1.51 my $focus = $FOCUS; $FOCUS = $self;
143     $focus->update if $focus;
144     $FOCUS->update;
145 elmex 1.1 }
146 root 1.4
147 elmex 1.1 sub focus_out {
148 root 1.51 my ($self) = @_;
149 root 1.4
150 root 1.51 return unless $FOCUS == $self;
151 root 1.4
152 root 1.51 my $focus = $FOCUS; undef $FOCUS;
153     $focus->update if $focus; #?
154 elmex 1.1 }
155 root 1.4
156 root 1.51 sub mouse_motion { }
157     sub button_up { }
158     sub key_down { }
159     sub key_up { }
160    
161 root 1.68 sub button_down {
162     my ($self, $ev, $x, $y) = @_;
163    
164     $self->focus_in;
165     }
166    
167 root 1.51 sub w { $_[0]{w} = $_[1] if @_ > 1; $_[0]{w} }
168     sub h { $_[0]{h} = $_[1] if @_ > 1; $_[0]{h} }
169     sub x { $_[0]{x} = $_[1] if @_ > 1; $_[0]{x} }
170     sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} }
171     sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} }
172 elmex 1.11
173 elmex 1.1 sub draw {
174 elmex 1.11 my ($self) = @_;
175    
176 root 1.68 return unless $self->{h} && $self->{w};
177    
178 elmex 1.11 glPushMatrix;
179 root 1.12 glTranslate $self->{x}, $self->{y}, 0;
180 elmex 1.11 $self->_draw;
181 root 1.72 glPopMatrix;
182    
183 root 1.51 if ($self == $HOVER) {
184 root 1.73 my ($x, $y) = @$self{qw(x y)};
185 root 1.72
186 root 1.76 glColor 0, 0, 1, 0.2;
187 root 1.50 glEnable GL_BLEND;
188 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
189 root 1.48 glBegin GL_QUADS;
190 root 1.72 glVertex $x , $y;
191     glVertex $x + $self->{w}, $y;
192     glVertex $x + $self->{w}, $y + $self->{h};
193     glVertex $x , $y + $self->{h};
194 root 1.47 glEnd;
195 root 1.50 glDisable GL_BLEND;
196 root 1.47 }
197 elmex 1.11 }
198    
199     sub _draw {
200 root 1.38 my ($self) = @_;
201    
202     warn "no draw defined for $self\n";
203 elmex 1.1 }
204 root 1.4
205 elmex 1.1 sub bbox {
206 elmex 1.32 my ($self) = @_;
207     my ($w, $h) = $self->size_request;
208     (
209     $self->{x},
210     $self->{y},
211     $self->{x} = $w,
212     $self->{y} = $h
213     )
214     }
215    
216 root 1.38 sub find_widget {
217     my ($self, $x, $y) = @_;
218    
219     return $self
220     if $x >= $self->{x} && $x < $self->{x} + $self->{w}
221     && $y >= $self->{y} && $y < $self->{y} + $self->{h};
222    
223     ()
224     }
225    
226 elmex 1.32 sub del_parent { $_[0]->{parent} = undef }
227    
228     sub set_parent {
229     my ($self, $par) = @_;
230    
231     $self->{parent} = $par;
232     Scalar::Util::weaken $self->{parent};
233     }
234    
235     sub get_parent {
236     $_[0]->{parent}
237     }
238    
239     sub update {
240     my ($self) = @_;
241    
242     $self->{parent}->update
243     if $self->{parent};
244 elmex 1.1 }
245 elmex 1.2
246 root 1.72 sub connect {
247     my ($self, $signal, $cb) = @_;
248    
249     push @{ $self->{cb}{$signal} }, $cb;
250     }
251    
252     sub emit {
253     my ($self, $signal, @args) = @_;
254    
255     $_->($self, @args)
256     for @{$self->{cb}{$signal} || []};
257     }
258    
259 root 1.18 sub DESTROY {
260     my ($self) = @_;
261    
262 elmex 1.32 #$self->deactivate;
263 root 1.18 }
264    
265 root 1.39 #############################################################################
266    
267 root 1.73 package CFClient::UI::DrawBG;
268 root 1.68
269 root 1.73 our @ISA = CFClient::UI::Base::;
270 root 1.68
271     use strict;
272     use SDL::OpenGL;
273    
274     sub new {
275     my $class = shift;
276    
277     # range [value, low, high, page]
278    
279     $class->SUPER::new (
280 root 1.76 bg => [0, 0, 0, 0.2],
281 root 1.68 active_bg => [1, 1, 1],
282     @_
283     )
284     }
285    
286     sub _draw {
287     my ($self) = @_;
288    
289     my ($w, $h) = @$self{qw(w h)};
290    
291 root 1.76 glEnable GL_BLEND;
292     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
293 root 1.68 glColor @{ $FOCUS == $self ? $self->{active_bg} : $self->{bg} };
294 root 1.76
295 root 1.68 glBegin GL_QUADS;
296     glVertex 0 , 0;
297     glVertex 0 , $h;
298     glVertex $w, $h;
299     glVertex $w, 0;
300     glEnd;
301 root 1.76
302     glDisable GL_BLEND;
303 root 1.68 }
304    
305     #############################################################################
306    
307 root 1.73 package CFClient::UI::Empty;
308 root 1.66
309 root 1.73 our @ISA = CFClient::UI::Base::;
310 root 1.66
311     sub size_request {
312     (0, 0)
313     }
314    
315 root 1.67 sub draw { }
316 root 1.66
317     #############################################################################
318    
319 root 1.73 package CFClient::UI::Container;
320 elmex 1.15
321 root 1.73 our @ISA = CFClient::UI::Base::;
322 elmex 1.15
323 root 1.38 sub new {
324 root 1.64 my ($class, %arg) = @_;
325    
326     my $children = delete $arg{children} || [];
327 root 1.38
328 root 1.65 my $self = $class->SUPER::new (children => [], %arg);
329 root 1.64 $self->add ($_) for @$children;
330 root 1.38
331     $self
332     }
333    
334     sub add {
335     my ($self, $chld, $expand) = @_;
336    
337     $chld->{expand} = $expand;
338     $chld->set_parent ($self);
339    
340 root 1.66 $self->{children} = [
341 root 1.38 sort { $a->{z} <=> $b->{z} }
342 root 1.66 @{$self->{children}}, $chld
343     ];
344 root 1.38
345 root 1.75 $self->{w} = $self->{h} = -1;
346     $self->update;
347 root 1.38 }
348 root 1.35
349 elmex 1.32 sub remove {
350 root 1.38 my ($self, $widget) = @_;
351    
352     $self->{children} = [ grep $_ != $widget, @{ $self->{children} } ];
353    
354 root 1.75 $self->size_allocate (0, 0, $self->{w}, $self->{h});
355 root 1.38 }
356    
357     sub find_widget {
358     my ($self, $x, $y) = @_;
359    
360 root 1.45 $x -= $self->{x};
361     $y -= $self->{y};
362    
363 root 1.38 my $res;
364    
365 root 1.46 for (reverse @{ $self->{children} }) {
366 root 1.45 $res = $_->find_widget ($x, $y)
367 root 1.38 and return $res;
368     }
369    
370 root 1.46 $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
371 elmex 1.32 }
372 elmex 1.15
373 root 1.35 sub _draw {
374     my ($self) = @_;
375    
376 root 1.38 $_->draw for @{$self->{children}};
377 root 1.35 }
378 elmex 1.15
379 root 1.39 #############################################################################
380    
381 root 1.73 package CFClient::UI::Bin;
382 elmex 1.32
383 root 1.73 our @ISA = CFClient::UI::Container::;
384 elmex 1.32
385 root 1.66 sub new {
386     my ($class, %arg) = @_;
387    
388 root 1.73 my $child = (delete $arg{child}) || new CFClient::UI::Empty::;
389 root 1.66
390     $class->SUPER::new (children => [$child], %arg)
391     }
392    
393     sub add {
394     my ($self, $widget) = @_;
395    
396     $self->{children} = [];
397    
398     $self->SUPER::add ($widget);
399     }
400    
401     sub remove {
402     my ($self, $widget) = @_;
403    
404     $self->SUPER::remove ($widget);
405    
406 root 1.73 $self->{children} = [new CFClient::UI::Empty]
407 root 1.66 unless @{$self->{children}};
408     }
409    
410 root 1.39 sub child { $_[0]->{children}[0] }
411 elmex 1.32
412 root 1.38 sub size_request {
413 root 1.68 $_[0]{children}[0]->size_request
414 root 1.38 }
415 elmex 1.32
416 root 1.38 sub size_allocate {
417 root 1.75 my ($self, $x, $y, $w, $h) = @_;
418 root 1.42
419 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
420 root 1.68
421 root 1.75 $self->{children}[0]->size_allocate (0, 0, $w, $h);
422 root 1.38 }
423 elmex 1.32
424 root 1.39 #############################################################################
425    
426 root 1.73 package CFClient::UI::Window;
427 elmex 1.20
428 root 1.73 our @ISA = CFClient::UI::Bin::;
429 elmex 1.20
430     use SDL::OpenGL;
431    
432 root 1.42 sub new {
433 root 1.64 my ($class, %arg) = @_;
434 elmex 1.32
435 root 1.64 my $self = $class->SUPER::new (%arg);
436 elmex 1.32 }
437    
438     sub update {
439     my ($self) = @_;
440 root 1.42
441 root 1.63 # we want to do this delayed...
442 elmex 1.32 $self->render_chld;
443 root 1.42 $self->SUPER::update;
444 elmex 1.20 }
445    
446     sub render_chld {
447     my ($self) = @_;
448    
449     $self->{texture} =
450 root 1.60 CFClient::Texture->new_from_opengl (
451 root 1.42 $self->{w}, $self->{h}, sub { $self->child->draw }
452 elmex 1.20 );
453 elmex 1.36 }
454    
455     sub size_allocate {
456 root 1.75 my ($self, $x, $y, $w, $h) = @_;
457 elmex 1.36
458 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
459 root 1.68
460 root 1.75 $self->child->size_allocate (0, 0, $w, $h);
461 elmex 1.36
462 root 1.42 $self->render_chld;
463 elmex 1.20 }
464    
465     sub _draw {
466     my ($self) = @_;
467    
468 elmex 1.36 my ($w, $h) = ($self->w, $self->h);
469 root 1.29
470 elmex 1.20 my $tex = $self->{texture}
471     or return;
472    
473     glEnable GL_BLEND;
474 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
475 elmex 1.20 glEnable GL_TEXTURE_2D;
476 root 1.35 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
477 elmex 1.20
478 root 1.56 $tex->draw_quad (0, 0, $w, $h);
479 elmex 1.20
480     glDisable GL_BLEND;
481     glDisable GL_TEXTURE_2D;
482     }
483    
484 root 1.39 #############################################################################
485    
486 root 1.73 package CFClient::UI::Frame;
487 elmex 1.15
488 root 1.73 our @ISA = CFClient::UI::Bin::;
489 elmex 1.15
490     use SDL::OpenGL;
491    
492     sub size_request {
493     my ($self) = @_;
494 root 1.39 my $chld = $self->child
495 elmex 1.15 or return (0, 0);
496 root 1.30
497     $chld->move (2, 2);
498    
499 elmex 1.15 map { $_ + 4 } $chld->size_request;
500     }
501    
502 elmex 1.36 sub size_allocate {
503 root 1.75 my ($self, $x, $y, $w, $h) = @_;
504 root 1.42
505 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
506 elmex 1.36
507 root 1.75 $self->child->size_allocate (2, 2, $w - 4, $h - 4);
508 elmex 1.36 }
509    
510 elmex 1.15 sub _draw {
511     my ($self) = @_;
512    
513 root 1.39 my $chld = $self->child;
514 elmex 1.15
515     my ($w, $h) = $chld->size_request;
516    
517     glBegin GL_QUADS;
518 root 1.30 glColor 0, 0, 0;
519 root 1.56 glVertex 0 , 0;
520     glVertex 0 , $h + 4;
521     glVertex $w + 4 , $h + 4;
522     glVertex $w + 4 , 0;
523 elmex 1.15 glEnd;
524    
525 root 1.23 $chld->draw;
526 elmex 1.15 }
527    
528 root 1.39 #############################################################################
529    
530 root 1.73 package CFClient::UI::FancyFrame;
531 elmex 1.31
532 root 1.73 our @ISA = CFClient::UI::Bin::;
533 elmex 1.31
534     use SDL::OpenGL;
535    
536 root 1.41 my @tex =
537 root 1.60 map { new_from_file CFClient::Texture CFClient::find_rcfile $_ }
538 root 1.41 qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
539 elmex 1.34
540     sub size_request {
541     my ($self) = @_;
542 root 1.39
543     my ($w, $h) = $self->SUPER::size_request;
544 elmex 1.34
545 root 1.72 $h += $tex[1]->{h};
546     $h += $tex[4]->{h};
547     $w += $tex[2]->{w};
548     $w += $tex[3]->{w};
549 elmex 1.34
550 elmex 1.36 ($w, $h)
551     }
552    
553     sub size_allocate {
554 root 1.75 my ($self, $x, $y, $w, $h) = @_;
555 root 1.68
556 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
557 root 1.40
558 root 1.72 $h -= $tex[1]->{h};
559     $h -= $tex[4]->{h};
560     $w -= $tex[2]->{w};
561     $w -= $tex[3]->{w};
562 elmex 1.36
563     $h = $h < 0 ? 0 : $h;
564     $w = $w < 0 ? 0 : $w;
565 root 1.43
566 root 1.66 my $child = $self->child;
567    
568 root 1.75 $child->size_allocate ($tex[3]->{w}, $tex[1]->{h}, $w, $h);
569 elmex 1.34 }
570    
571     sub _draw {
572     my ($self) = @_;
573    
574 root 1.43 my ($w, $h) = ($self->{w}, $self->{h});
575     my ($cw, $ch) = ($self->child->{w}, $self->child->{h});
576 elmex 1.34
577     glEnable GL_BLEND;
578 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
579 elmex 1.34 glEnable GL_TEXTURE_2D;
580 elmex 1.36 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
581 elmex 1.34
582 root 1.41 my $top = $tex[1];
583 root 1.72 $top->draw_quad (0, 0, $w, $top->{h});
584 elmex 1.34
585 root 1.41 my $left = $tex[3];
586 root 1.72 $left->draw_quad (0, $top->{h}, $left->{w}, $ch);
587 elmex 1.34
588 root 1.41 my $right = $tex[2];
589 root 1.72 $right->draw_quad ($w - $right->{w}, $top->{h}, $right->{w}, $ch);
590 elmex 1.34
591 root 1.41 my $bottom = $tex[4];
592 root 1.72 $bottom->draw_quad (0, $h - $bottom->{h}, $w, $bottom->{h});
593 elmex 1.34
594 root 1.41 my $bg = $tex[0];
595 root 1.76
596 elmex 1.36 glBindTexture GL_TEXTURE_2D, $bg->{name};
597     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
598     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT;
599     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT;
600 elmex 1.34
601 root 1.72 my $rep_x = $cw / $bg->{w};
602     my $rep_y = $ch / $bg->{h};
603 elmex 1.34
604 root 1.72 $bg->draw_quad ($left->{w}, $top->{h}, $cw, $ch);
605 elmex 1.34
606 root 1.76 glDisable GL_TEXTURE_2D;
607 elmex 1.34 glDisable GL_BLEND;
608 elmex 1.36
609 root 1.39 $self->child->draw;
610 elmex 1.36
611 elmex 1.34 }
612 elmex 1.31
613 root 1.39 #############################################################################
614    
615 root 1.73 package CFClient::UI::Table;
616 elmex 1.15
617 root 1.73 our @ISA = CFClient::UI::Base::;
618 elmex 1.15
619 root 1.75 use List::Util qw(max sum);
620    
621 elmex 1.15 use SDL::OpenGL;
622    
623     sub add {
624     my ($self, $x, $y, $chld) = @_;
625 elmex 1.32
626 root 1.38 $self->{children}[$y][$x] = $chld;
627 elmex 1.32 $chld->set_parent ($self);
628 root 1.75
629     $self->{w} = $self->{h} = -1;
630 elmex 1.32 $self->update;
631 elmex 1.15 }
632    
633 root 1.75 sub get_wh {
634     my ($self) = @_;
635    
636     my (@w, @h);
637 elmex 1.15
638 root 1.75 for my $y (0 .. $#{$self->{children}}) {
639     my $row = $self->{children}[$y]
640     or next;
641 elmex 1.15
642 root 1.75 for my $x (0 .. $#$row) {
643     my $widget = $row->[$x]
644     or next;
645     my ($w, $h) = $widget->size_request;
646 elmex 1.15
647 root 1.75 $w[$x] = max $w[$x], $w;
648     $h[$y] = max $h[$y], $h;
649 elmex 1.17 }
650 elmex 1.15 }
651 root 1.75
652     (\@w, \@h)
653 elmex 1.15 }
654    
655     sub size_request {
656     my ($self) = @_;
657    
658 root 1.75 my ($ws, $hs) = $self->get_wh;
659 elmex 1.15
660 root 1.75 (
661     (List::Util::sum @$ws),
662     (List::Util::sum @$hs),
663     )
664     }
665    
666     sub size_allocate {
667     my ($self, $x, $y, $w, $h) = @_;
668    
669     $self->_size_allocate ($x, $y, $w, $h) or return;
670    
671     my ($ws, $hs) = $self->get_wh;
672    
673     my $req_w = List::Util::sum @$ws;
674     my $req_h = List::Util::sum @$hs;
675 elmex 1.15
676 root 1.75 # linearly scale sizes
677     $_ *= $req_w / $w for @$ws;
678     $_ *= $req_h / $h for @$hs;
679 elmex 1.15
680 root 1.75 my $y;
681 elmex 1.15
682 root 1.75 for my $r (0 .. $#{$self->{children}}) {
683     my $row = $self->{children}[$r]
684     or next;
685 elmex 1.15
686     my $x = 0;
687 root 1.75 my $row_h = $hs->[$r];
688    
689     for my $c (0 .. $#$row) {
690     my $widget = $row->[$c]
691     or next;
692 elmex 1.15
693 root 1.75 my $col_w = $ws->[$c];
694 elmex 1.15
695 root 1.75 $widget->size_allocate ($x, $y, $col_w, $row_h);
696 elmex 1.15
697 root 1.75 $x += $col_w;
698 elmex 1.15 }
699    
700 root 1.75 $y += $row_h;
701     }
702    
703     }
704    
705 root 1.76 sub find_widget {
706     my ($self, $x, $y) = @_;
707    
708     $x -= $self->{x};
709     $y -= $self->{y};
710    
711     my $res;
712    
713     for (grep $_, map @$_, grep $_, @{ $self->{children} }) {
714     $res = $_->find_widget ($x, $y)
715     and return $res;
716     }
717    
718     $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
719     }
720    
721 root 1.75 sub _draw {
722     my ($self) = @_;
723    
724     for (grep $_, @{$self->{children}}) {
725     $_->draw for grep $_, @$_;
726 elmex 1.15 }
727     }
728    
729 root 1.39 #############################################################################
730    
731 root 1.76 package CFClient::UI::HBox;
732    
733     # TODO: wrap into common Box base class
734    
735     our @ISA = CFClient::UI::Container::;
736    
737     sub size_request {
738     my ($self) = @_;
739    
740     my @alloc = map [$_->size_request], @{$self->{children}};
741    
742     (
743     (List::Util::sum map $_->[0], @alloc),
744     (List::Util::max map $_->[1], @alloc),
745     )
746     }
747    
748     sub size_allocate {
749     my ($self, $x, $y, $w, $h) = @_;
750    
751     $self->_size_allocate ($x, $y, $w, $h) or return;
752    
753     return unless $self->{w};
754    
755     ($h, $w) = ($w, $h);
756    
757     my $children = $self->{children};
758    
759     my @h = map +($_->size_request)[0], @$children;
760    
761     my $req_h = List::Util::sum @h;
762    
763     if ($req_h > $h) {
764     # ah well, not enough space
765     $_ = $h[$_] * $h / $req_h for @h;
766     } else {
767     my @exp = grep $_->{expand}, @$children;
768     @exp = @$children unless @exp;
769    
770     my %exp = map +($_ => 1), @exp;
771    
772     for (0 .. $#$children) {
773     my $child = $children->[$_];
774    
775     my $alloc_h = $h[$_];
776     $alloc_h += ($h - $req_h) / @exp if $exp{$child};
777     $h[$_] = $alloc_h;
778     }
779     }
780    
781     my $y = 0;
782     for (0 .. $#$children) {
783     my $child = $children->[$_];
784     my $h = $h[$_];
785     $child->size_allocate ($y, 0, $h, $w);
786    
787     $y += $h;
788     }
789     }
790    
791     #############################################################################
792    
793 root 1.73 package CFClient::UI::VBox;
794 elmex 1.15
795 root 1.76 # TODO: wrap into common Box base class
796    
797 root 1.73 our @ISA = CFClient::UI::Container::;
798 elmex 1.15
799 root 1.43 sub size_request {
800     my ($self) = @_;
801    
802     my @alloc = map [$_->size_request], @{$self->{children}};
803    
804     (
805     (List::Util::max map $_->[0], @alloc),
806     (List::Util::sum map $_->[1], @alloc),
807     )
808     }
809    
810 elmex 1.36 sub size_allocate {
811 root 1.75 my ($self, $x, $y, $w, $h) = @_;
812 elmex 1.36
813 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
814 root 1.68
815     return unless $self->{h};
816    
817     my $children = $self->{children};
818    
819     my @h = map +($_->size_request)[1], @$children;
820    
821     my $req_h = List::Util::sum @h;
822    
823     if ($req_h > $h) {
824     # ah well, not enough space
825     $_ = $h[$_] * $h / $req_h for @h;
826     } else {
827     my @exp = grep $_->{expand}, @$children;
828     @exp = @$children unless @exp;
829    
830     my %exp = map +($_ => 1), @exp;
831 elmex 1.36
832 root 1.68 for (0 .. $#$children) {
833     my $child = $children->[$_];
834 elmex 1.36
835 root 1.68 my $alloc_h = $h[$_];
836     $alloc_h += ($h - $req_h) / @exp if $exp{$child};
837     $h[$_] = $alloc_h;
838     }
839 elmex 1.36 }
840    
841     my $y = 0;
842 root 1.68 for (0 .. $#$children) {
843     my $child = $children->[$_];
844     my $h = $h[$_];
845 root 1.75 $child->size_allocate (0, $y, $w, $h);
846 elmex 1.36
847 root 1.68 $y += $h;
848 elmex 1.36 }
849     }
850    
851 root 1.39 #############################################################################
852    
853 root 1.73 package CFClient::UI::Label;
854 root 1.10
855 root 1.73 our @ISA = CFClient::UI::Base::;
856 root 1.12
857 root 1.10 use SDL::OpenGL;
858    
859     sub new {
860 root 1.64 my ($class, %arg) = @_;
861 root 1.51
862 root 1.59 my $self = $class->SUPER::new (
863 root 1.76 fg => [1, 1, 1],
864     height => $::FONTSIZE,
865     text => "",
866     align => -1,
867     padding => 2,
868     layout => new CFClient::Layout,
869 root 1.64 %arg
870 root 1.59 );
871 root 1.10
872 root 1.64 $self->set_text ($self->{text});
873 root 1.10
874     $self
875     }
876    
877 root 1.68 sub escape_text {
878     local $_ = $_[1];
879    
880     s/&/&amp;/g;
881     s/>/&gt;/g;
882     s/</&lt;/g;
883    
884     $_[1]
885     }
886    
887 elmex 1.15 sub set_text {
888     my ($self, $text) = @_;
889 root 1.28
890     $self->{text} = $text;
891 root 1.59 $self->{layout}->set_markup ($text);
892 root 1.28
893 root 1.59 delete $self->{texture};
894 root 1.68 $self->update;
895 elmex 1.15 }
896    
897     sub get_text {
898     my ($self, $text) = @_;
899 root 1.28
900 elmex 1.15 $self->{text}
901     }
902    
903 root 1.14 sub size_request {
904     my ($self) = @_;
905    
906 root 1.59 $self->{layout}->set_width;
907 root 1.64 $self->{layout}->set_height ($self->{height});
908 root 1.76 my ($w, $h) = $self->{layout}->size;
909    
910     (
911     $w + $self->{padding} * 2,
912     $h + $self->{padding} * 2,
913     )
914 root 1.59 }
915 root 1.51
916 root 1.59 sub size_allocate {
917 root 1.75 my ($self, $x, $y, $w, $h) = @_;
918 root 1.51
919 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
920 root 1.68
921 root 1.59 delete $self->{texture};
922 root 1.14 }
923    
924 root 1.68 sub update {
925     my ($self) = @_;
926    
927     delete $self->{texture};
928     $self->SUPER::update;
929     }
930    
931 elmex 1.11 sub _draw {
932 root 1.10 my ($self) = @_;
933    
934 root 1.59 my $tex = $self->{texture} ||= do {
935     $self->{layout}->set_width ($self->{w});
936 root 1.74 new_from_layout CFClient::Texture $self->{layout}
937 root 1.59 };
938 root 1.10
939 root 1.12 glEnable GL_BLEND;
940 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
941 root 1.10 glEnable GL_TEXTURE_2D;
942 root 1.28 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
943 root 1.10
944 root 1.68 glColor @{$self->{fg}};
945 root 1.12
946 root 1.72 my $x =
947 root 1.76 $self->{align} < 0 ? $self->{padding}
948     : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding}
949 root 1.74 : ($self->{w} - $tex->{w}) * 0.5;
950 root 1.72
951     $tex->draw_quad ($x, 0);
952 root 1.10
953 root 1.74 glDisable GL_TEXTURE_2D;
954 root 1.12 glDisable GL_BLEND;
955 root 1.10 }
956    
957 root 1.39 #############################################################################
958    
959 root 1.73 package CFClient::UI::Entry;
960 elmex 1.31
961 root 1.73 our @ISA = CFClient::UI::Label::;
962 elmex 1.31
963     use SDL;
964     use SDL::OpenGL;
965    
966 root 1.68 sub new {
967     my $class = shift;
968    
969     $class->SUPER::new (
970     fg => [1, 1, 1],
971 root 1.76 bg => [0, 0, 0, 0.2],
972 root 1.68 active_bg => [1, 1, 1],
973     active_fg => [0, 0, 0],
974     @_
975     )
976     }
977    
978     sub _set_text {
979     my ($self, $text) = @_;
980    
981     $self->{last_activity} = $::NOW;
982    
983     $self->{text} = $text;
984     $self->{layout}->set_width ($self->{w});
985 root 1.72
986     $text =~ s/./*/g if $self->{hidden};
987    
988 root 1.76 $self->{layout}->set_markup ($self->escape_text ($text) . " ");
989 root 1.68
990     $text = substr $text, 0, $self->{cursor};
991     utf8::encode $text;
992    
993     @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text);
994     }
995    
996     sub size_request {
997     my ($self) = @_;
998    
999     my ($w, $h) = $self->SUPER::size_request;
1000    
1001     ($w + 1, $h) # add 1 for cursor
1002     }
1003    
1004     sub size_allocate {
1005 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1006 root 1.68
1007 root 1.75 $self->SUPER::size_allocate ($x, $y, $w, $h);
1008 root 1.68
1009     $self->_set_text ($self->{text});
1010     }
1011    
1012     sub set_text {
1013     my ($self, $text) = @_;
1014    
1015     $self->{cursor} = length $text;
1016     $self->_set_text ($text);
1017     $self->update;
1018     }
1019    
1020 elmex 1.31 sub key_down {
1021     my ($self, $ev) = @_;
1022    
1023     my $mod = $ev->key_mod;
1024     my $sym = $ev->key_sym;
1025    
1026     my $uni = $ev->key_unicode;
1027    
1028     my $text = $self->get_text;
1029    
1030     if ($sym == SDLK_BACKSPACE) {
1031 root 1.68 substr $text, --$self->{cursor}, 1, "" if $self->{cursor};
1032     } elsif ($sym == SDLK_DELETE) {
1033     substr $text, $self->{cursor}, 1, "";
1034     } elsif ($sym == SDLK_LEFT) {
1035     --$self->{cursor} if $self->{cursor};
1036     } elsif ($sym == SDLK_RIGHT) {
1037     ++$self->{cursor} if $self->{cursor} < length $self->{text};
1038 root 1.76 } elsif ($sym == SDLK_HOME) {
1039     $self->{cursor} = 0;
1040     } elsif ($sym == SDLK_END) {
1041     $self->{cursor} = length $text;
1042 elmex 1.31 } elsif ($uni) {
1043 root 1.68 substr $text, $self->{cursor}++, 0, chr $uni;
1044 elmex 1.31 }
1045 root 1.51
1046 root 1.68 $self->_set_text ($text);
1047     $self->update;
1048     }
1049    
1050     sub focus_in {
1051     my ($self) = @_;
1052    
1053     $self->{last_activity} = $::NOW;
1054    
1055     $self->SUPER::focus_in;
1056 elmex 1.31 }
1057    
1058 root 1.51 sub button_down {
1059 root 1.68 my ($self, $ev, $x, $y) = @_;
1060    
1061     $self->SUPER::button_down ($ev, $x, $y);
1062    
1063     my $idx = $self->{layout}->xy_to_index ($x, $y);
1064    
1065     # byte-index to char-index
1066 root 1.76 my $text = $self->{text};
1067 root 1.68 utf8::encode $text;
1068     $self->{cursor} = length substr $text, 0, $idx;
1069 root 1.51
1070 root 1.68 $self->_set_text ($self->{text});
1071     $self->update;
1072 root 1.51 }
1073    
1074 root 1.58 sub mouse_motion {
1075     my ($self, $ev, $x, $y) = @_;
1076 root 1.68 # printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
1077 root 1.58 }
1078    
1079 root 1.51 sub _draw {
1080     my ($self) = @_;
1081    
1082 root 1.68 local $self->{fg} = $self->{fg};
1083    
1084 root 1.51 if ($FOCUS == $self) {
1085 root 1.68 glColor @{$self->{active_bg}};
1086     $self->{fg} = $self->{active_fg};
1087 root 1.51 } else {
1088 root 1.68 glColor @{$self->{bg}};
1089 root 1.51 }
1090    
1091 root 1.76 glEnable GL_BLEND;
1092     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1093 root 1.51 glBegin GL_QUADS;
1094 root 1.68 glVertex 0 , 0;
1095     glVertex 0 , $self->{h};
1096     glVertex $self->{w}, $self->{h};
1097     glVertex $self->{w}, 0;
1098 root 1.51 glEnd;
1099 root 1.76 glDisable GL_BLEND;
1100 root 1.51
1101     $self->SUPER::_draw;
1102 root 1.68
1103     #TODO: force update every cursor change :(
1104     if ($FOCUS == $self && (($::NOW - $self->{last_activity}) & 1023) < 600) {
1105     glColor @{$self->{fg}};
1106     glBegin GL_LINES;
1107     glVertex $self->{cur_x}, $self->{cur_y};
1108     glVertex $self->{cur_x}, $self->{cur_y} + $self->{cur_h};
1109     glEnd;
1110     }
1111     }
1112    
1113     #############################################################################
1114    
1115 root 1.73 package CFClient::UI::Slider;
1116 root 1.68
1117     use strict;
1118    
1119     use SDL::OpenGL;
1120    
1121 root 1.73 our @ISA = CFClient::UI::DrawBG::;
1122 root 1.68
1123     sub new {
1124     my $class = shift;
1125    
1126     # range [value, low, high, page]
1127    
1128 root 1.76 my $self = $class->SUPER::new (
1129 root 1.68 fg => [1, 1, 1],
1130     active_fg => [0, 0, 0],
1131     range => [0, 0, 100, 10],
1132 root 1.76 req_w => 40,
1133     req_h => 10,
1134     vertical => 0,
1135 root 1.68 @_
1136 root 1.76 );
1137    
1138     $self
1139     }
1140    
1141     sub size_request {
1142     my ($self) = @_;
1143    
1144     my $w = $self->{req_w};
1145     my $h = $self->{req_h};
1146    
1147     $self->{vertical} ? ($h, $w) : ($w, $h)
1148 root 1.68 }
1149    
1150 root 1.69 sub button_down {
1151     my ($self, $ev, $x, $y) = @_;
1152    
1153     $self->SUPER::button_down ($ev, $x, $y);
1154     $self->mouse_motion ($ev, $x, $y);
1155     }
1156    
1157     sub mouse_motion {
1158     my ($self, $ev, $x, $y) = @_;
1159    
1160     if ($GRAB == $self) {
1161     my ($value, $lo, $hi, $page) = @{$self->{range}};
1162    
1163 root 1.71 my ($x, $w) = $self->{vertical} ? ($y, $self->{h}) : ($x, $self->{w});
1164    
1165     $x = $x * ($hi - $lo) / $w + $lo;
1166 root 1.69 $x = $lo if $x < $lo;
1167     $x = $hi - $page if $x > $hi - $page;
1168     $self->{range}[0] = $x;
1169    
1170 root 1.72 $self->emit (changed => $x);
1171 root 1.69 $self->update;
1172     }
1173     }
1174    
1175 root 1.68 sub _draw {
1176     my ($self) = @_;
1177    
1178     $self->SUPER::_draw ();
1179    
1180     my ($w, $h) = @$self{qw(w h)};
1181    
1182     if ($self->{vertical}) {
1183     # draw a vertical slider like a rotated horizontal slider
1184    
1185     glRotate 90, 0, 0, 1;
1186 root 1.71 glTranslate 0, -$self->{w}, 0;
1187 root 1.68
1188     ($w, $h) = ($h, $w);
1189     }
1190    
1191     my $fg = $FOCUS == $self ? $self->{active_fg} : $self->{fg};
1192     my $bg = $FOCUS == $self ? $self->{active_bg} : $self->{bg};
1193    
1194 root 1.69 my ($value, $lo, $hi, $page) = @{$self->{range}};
1195    
1196     $page = int $page * $w / ($hi - $lo);
1197     $value = int +($value - $lo) * $w / ($hi - $lo);
1198    
1199     $w -= $page;
1200     $page &= ~1;
1201     glTranslate $page * 0.5, 0, 0;
1202    
1203 root 1.68 glColor @$fg;
1204     glBegin GL_LINES;
1205     glVertex 0, 0; glVertex 0, $h;
1206     glVertex $w - 1, 0; glVertex $w - 1, $h;
1207     glVertex 0, $h * 0.5; glVertex $w, $h * 0.5;
1208     glEnd;
1209 root 1.69
1210     my $knob_a = $value - $page * 0.5;
1211     my $knob_b = $value + $page * 0.5;
1212    
1213     glBegin GL_QUADS;
1214     glColor @$fg;
1215     glVertex $knob_a, 0;
1216     glVertex $knob_a, $h;
1217     glVertex $knob_b, $h;
1218     glVertex $knob_b, 0;
1219    
1220     if ($knob_a < $knob_b - 2) {
1221     glColor @$bg;
1222     glVertex $knob_a + 1, 1;
1223     glVertex $knob_a + 1, $h - 1;
1224     glVertex $knob_b - 1, $h - 1;
1225     glVertex $knob_b - 1, 1;
1226     }
1227     glEnd;
1228 root 1.51 }
1229    
1230 root 1.39 #############################################################################
1231    
1232 root 1.73 package CFClient::UI::MapWidget;
1233 root 1.4
1234 elmex 1.2 use strict;
1235 elmex 1.7
1236 root 1.25 use List::Util qw(min max);
1237 elmex 1.2
1238 root 1.16 use SDL;
1239 elmex 1.2 use SDL::OpenGL;
1240    
1241 root 1.73 our @ISA = CFClient::UI::Base::;
1242 root 1.25
1243 root 1.64 sub new {
1244     my $class = shift;
1245    
1246 root 1.65 $class->SUPER::new (
1247     z => -1,
1248     list => (glGenLists 1),
1249     @_
1250     )
1251 root 1.64 }
1252    
1253 elmex 1.2 sub key_down {
1254     print "MAPKEYDOWN\n";
1255     }
1256    
1257     sub key_up {
1258     }
1259    
1260 elmex 1.36 sub size_request {
1261 root 1.52 (
1262     1 + int $::WIDTH / 32,
1263     1 + int $::HEIGHT / 32,
1264     )
1265 elmex 1.36 }
1266    
1267 root 1.65 sub update {
1268     my ($self) = @_;
1269    
1270     $self->{need_update} = 1;
1271 root 1.74 $self->SUPER::update;
1272 root 1.65 }
1273    
1274 elmex 1.11 sub _draw {
1275 root 1.21 my ($self) = @_;
1276    
1277 root 1.65 if (delete $self->{need_update}) {
1278     glNewList $self->{list}, GL_COMPILE;
1279 root 1.25
1280 root 1.65 my $mx = $::CONN->{mapx};
1281     my $my = $::CONN->{mapy};
1282 root 1.25
1283 root 1.65 my $map = $::CONN->{map};
1284 root 1.25
1285 root 1.65 my ($xofs, $yofs);
1286 root 1.25
1287 root 1.65 my $sw = 1 + int $::WIDTH / 32;
1288     my $sh = 1 + int $::HEIGHT / 32;
1289 root 1.25
1290 root 1.65 if ($::CONN->{mapw} > $sw) {
1291     $xofs = $mx + ($::CONN->{mapw} - $sw) * 0.5;
1292     } else {
1293     $xofs = $self->{xofs} = min $mx, max $mx + $::CONN->{mapw} - $sw + 1, $self->{xofs};
1294     }
1295 root 1.25
1296 root 1.65 if ($::CONN->{maph} > $sh) {
1297     $yofs = $my + ($::CONN->{maph} - $sh) * 0.5;
1298     } else {
1299     $yofs = $self->{yofs} = min $my, max $my + $::CONN->{maph} - $sh + 1, $self->{yofs};
1300     }
1301 root 1.35
1302 root 1.65 glEnable GL_TEXTURE_2D;
1303     glEnable GL_BLEND;
1304 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1305 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1306 elmex 1.2
1307 root 1.65 my $sw4 = ($sw + 3) & ~3;
1308     my $darkness = "\x00" x ($sw4 * $sh);
1309 elmex 1.2
1310 root 1.65 for my $x (0 .. $sw - 1) {
1311     my $row = $map->[$x + $xofs];
1312     for my $y (0 .. $sh - 1) {
1313    
1314     my $cell = $row->[$y + $yofs]
1315     or next;
1316    
1317     my $dark = $cell->[0];
1318     if ($dark < 0) {
1319     substr $darkness, $y * $sw4 + $x, 1, chr 224;
1320     } else {
1321     substr $darkness, $y * $sw4 + $x, 1, chr 255 - $dark;
1322     }
1323    
1324     for my $num (grep $_, @$cell[1,2,3]) {
1325     my $tex = $::CONN->{face}[$num]{texture} || next;
1326    
1327 root 1.72 my ($w, $h) = @$tex{qw(w h)};
1328 root 1.19
1329 root 1.65 $tex->draw_quad (($x + 1) * 32 - $w, ($y + 1) * 32 - $h, $w, $h);
1330     }
1331 elmex 1.2 }
1332     }
1333    
1334 root 1.35 # if (1) { # higher quality darkness
1335     # $lighting =~ s/(.)/$1$1$1/gs;
1336     # my $pb = new_from_data Gtk2::Gdk::Pixbuf $lighting, "rgb", 0, 8, $sw4, $sh, $sw4 * 3;
1337     #
1338     # $pb = $pb->scale_simple ($sw4 * 0.5, $sh * 0.5, "bilinear");
1339     #
1340     # $lighting = $pb->get_pixels;
1341     # $lighting =~ s/(.)../$1/gs;
1342     # }
1343    
1344 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1345    
1346     $darkness = new CFClient::Texture
1347 root 1.74 w => $sw4,
1348     h => $sh,
1349 root 1.65 data => $darkness,
1350     internalformat => GL_ALPHA,
1351     format => GL_ALPHA;
1352 root 1.57
1353 root 1.65 glColor 0.45, 0.45, 0.45, 1;
1354     $darkness->draw_quad (0, 0, $sw4 * 32, $sh * 32);
1355 root 1.35
1356 root 1.65 glDisable GL_TEXTURE_2D;
1357     glDisable GL_BLEND;
1358 root 1.35
1359 root 1.65 glEndList;
1360     }
1361    
1362     glCallList $self->{list};
1363 elmex 1.2 }
1364    
1365 root 1.16 my %DIR = (
1366     SDLK_KP8, [1, "north"],
1367 root 1.18 SDLK_KP9, [2, "northeast"],
1368 root 1.16 SDLK_KP6, [3, "east"],
1369     SDLK_KP3, [4, "southeast"],
1370     SDLK_KP2, [5, "south"],
1371     SDLK_KP1, [6, "southwest"],
1372     SDLK_KP4, [7, "west"],
1373     SDLK_KP7, [8, "northwest"],
1374 root 1.18
1375     SDLK_UP, [1, "north"],
1376     SDLK_RIGHT, [3, "east"],
1377     SDLK_DOWN, [5, "south"],
1378     SDLK_LEFT, [7, "west"],
1379 root 1.16 );
1380    
1381     sub key_down {
1382     my ($self, $ev) = @_;
1383    
1384     my $mod = $ev->key_mod;
1385     my $sym = $ev->key_sym;
1386    
1387     if ($sym == SDLK_KP5) {
1388     $::CONN->send ("command stay fire");
1389     } elsif (exists $DIR{$sym}) {
1390     if ($mod & KMOD_SHIFT) {
1391 root 1.18 $self->{shft}++;
1392 root 1.16 $::CONN->send ("command fire $DIR{$sym}[0]");
1393     } elsif ($mod & KMOD_CTRL) {
1394 root 1.18 $self->{ctrl}++;
1395 root 1.16 $::CONN->send ("command run $DIR{$sym}[0]");
1396     } else {
1397 root 1.18 $::CONN->send ("command $DIR{$sym}[1]");
1398 root 1.16 }
1399     }
1400     }
1401    
1402     sub key_up {
1403     my ($self, $ev) = @_;
1404    
1405     my $mod = $ev->key_mod;
1406     my $sym = $ev->key_sym;
1407    
1408 root 1.18 if (!($mod & KMOD_SHIFT) && delete $self->{shft}) {
1409     $::CONN->send ("command fire_stop");
1410     }
1411     if (!($mod & KMOD_CTRL ) && delete $self->{ctrl}) {
1412     $::CONN->send ("command run_stop");
1413 root 1.16 }
1414     }
1415    
1416 root 1.39 #############################################################################
1417    
1418 root 1.73 package CFClient::UI::Animator;
1419 root 1.35
1420     use SDL::OpenGL;
1421    
1422 root 1.73 our @ISA = CFClient::UI::Bin::;
1423 root 1.35
1424     sub moveto {
1425     my ($self, $x, $y) = @_;
1426    
1427     $self->{moveto} = [$self->{x}, $self->{y}, $x, $y];
1428 root 1.56 $self->{speed} = 0.001;
1429 root 1.35 $self->{time} = 1;
1430    
1431     ::animation_start $self;
1432     }
1433    
1434     sub animate {
1435     my ($self, $interval) = @_;
1436    
1437     $self->{time} -= $interval * $self->{speed};
1438     if ($self->{time} <= 0) {
1439     $self->{time} = 0;
1440     ::animation_stop $self;
1441     }
1442    
1443     my ($x0, $y0, $x1, $y1) = @{$self->{moveto}};
1444    
1445     $self->{x} = $x0 * $self->{time} + $x1 * (1 - $self->{time});
1446     $self->{y} = $y0 * $self->{time} + $y1 * (1 - $self->{time});
1447     }
1448    
1449     sub _draw {
1450     my ($self) = @_;
1451    
1452     glPushMatrix;
1453 root 1.51 glRotate $self->{time} * 1000, 0, 1, 0;
1454 root 1.38 $self->{children}[0]->draw;
1455 root 1.35 glPopMatrix;
1456     }
1457    
1458 root 1.51 #############################################################################
1459    
1460 root 1.73 package CFClient::UI::Toplevel;
1461 root 1.51
1462 root 1.73 our @ISA = CFClient::UI::Container::;
1463 root 1.51
1464     sub size_request {
1465     ($::WIDTH, $::HEIGHT)
1466     }
1467    
1468     sub size_allocate {
1469 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1470 root 1.51
1471 root 1.75 $self->_size_allocate ($x, $y, $w, $h);
1472 root 1.51
1473 root 1.75 $_->size_allocate ($_->{x}, $_->{y}, $_->size_request)
1474 root 1.51 for @{$self->{children}};
1475     }
1476    
1477 root 1.58 sub translate {
1478     my ($self, $x, $y) = @_;
1479    
1480     ($x, $y)
1481     }
1482    
1483 root 1.51 sub update {
1484     my ($self) = @_;
1485    
1486 root 1.75 $self->size_allocate (0, 0, $::WIDTH, $::HEIGHT);
1487 root 1.51 ::refresh ();
1488     }
1489    
1490     sub add {
1491     my ($self, $widget) = @_;
1492    
1493     $self->SUPER::add ($widget);
1494    
1495 root 1.75 $widget->size_allocate ($widget->{x}, $widget->{y}, $widget->size_request);
1496 root 1.51 }
1497    
1498     sub draw {
1499     my ($self) = @_;
1500    
1501     $self->_draw;
1502     }
1503    
1504     #############################################################################
1505    
1506 root 1.73 package CFClient::UI;
1507 root 1.51
1508 root 1.73 $TOPLEVEL = new CFClient::UI::Toplevel;
1509 root 1.51
1510     1
1511 root 1.5