ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.78
Committed: Tue Apr 11 22:49:13 2006 UTC (18 years, 1 month ago) by root
Branch: MAIN
Changes since 1.77: +26 -25 lines
Log Message:
fixes

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