ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.77
Committed: Tue Apr 11 22:14:13 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.76: +70 -26 lines
Log Message:
implement fancyframe move & resize, fix firts round of bugs

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 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 root 1.77 my ($self, $chld) = @_;
336 root 1.38
337     $chld->set_parent ($self);
338    
339 root 1.66 $self->{children} = [
340 root 1.38 sort { $a->{z} <=> $b->{z} }
341 root 1.66 @{$self->{children}}, $chld
342     ];
343 root 1.38
344 root 1.75 $self->{w} = $self->{h} = -1;
345     $self->update;
346 root 1.38 }
347 root 1.35
348 elmex 1.32 sub remove {
349 root 1.38 my ($self, $widget) = @_;
350    
351     $self->{children} = [ grep $_ != $widget, @{ $self->{children} } ];
352    
353 root 1.75 $self->size_allocate (0, 0, $self->{w}, $self->{h});
354 root 1.38 }
355    
356     sub find_widget {
357     my ($self, $x, $y) = @_;
358    
359 root 1.45 $x -= $self->{x};
360     $y -= $self->{y};
361    
362 root 1.38 my $res;
363    
364 root 1.46 for (reverse @{ $self->{children} }) {
365 root 1.45 $res = $_->find_widget ($x, $y)
366 root 1.38 and return $res;
367     }
368    
369 root 1.46 $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
370 elmex 1.32 }
371 elmex 1.15
372 root 1.35 sub _draw {
373     my ($self) = @_;
374    
375 root 1.38 $_->draw for @{$self->{children}};
376 root 1.35 }
377 elmex 1.15
378 root 1.39 #############################################################################
379    
380 root 1.73 package CFClient::UI::Bin;
381 elmex 1.32
382 root 1.73 our @ISA = CFClient::UI::Container::;
383 elmex 1.32
384 root 1.66 sub new {
385     my ($class, %arg) = @_;
386    
387 root 1.73 my $child = (delete $arg{child}) || new CFClient::UI::Empty::;
388 root 1.66
389     $class->SUPER::new (children => [$child], %arg)
390     }
391    
392     sub add {
393     my ($self, $widget) = @_;
394    
395     $self->{children} = [];
396    
397     $self->SUPER::add ($widget);
398     }
399    
400     sub remove {
401     my ($self, $widget) = @_;
402    
403     $self->SUPER::remove ($widget);
404    
405 root 1.73 $self->{children} = [new CFClient::UI::Empty]
406 root 1.66 unless @{$self->{children}};
407     }
408    
409 root 1.39 sub child { $_[0]->{children}[0] }
410 elmex 1.32
411 root 1.38 sub size_request {
412 root 1.68 $_[0]{children}[0]->size_request
413 root 1.38 }
414 elmex 1.32
415 root 1.38 sub size_allocate {
416 root 1.75 my ($self, $x, $y, $w, $h) = @_;
417 root 1.42
418 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
419 root 1.68
420 root 1.75 $self->{children}[0]->size_allocate (0, 0, $w, $h);
421 root 1.38 }
422 elmex 1.32
423 root 1.39 #############################################################################
424    
425 root 1.73 package CFClient::UI::Window;
426 elmex 1.20
427 root 1.73 our @ISA = CFClient::UI::Bin::;
428 elmex 1.20
429     use SDL::OpenGL;
430    
431 root 1.42 sub new {
432 root 1.64 my ($class, %arg) = @_;
433 elmex 1.32
434 root 1.64 my $self = $class->SUPER::new (%arg);
435 elmex 1.32 }
436    
437     sub update {
438     my ($self) = @_;
439 root 1.42
440 root 1.63 # we want to do this delayed...
441 elmex 1.32 $self->render_chld;
442 root 1.42 $self->SUPER::update;
443 elmex 1.20 }
444    
445     sub render_chld {
446     my ($self) = @_;
447    
448     $self->{texture} =
449 root 1.60 CFClient::Texture->new_from_opengl (
450 root 1.42 $self->{w}, $self->{h}, sub { $self->child->draw }
451 elmex 1.20 );
452 elmex 1.36 }
453    
454     sub size_allocate {
455 root 1.75 my ($self, $x, $y, $w, $h) = @_;
456 elmex 1.36
457 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
458 root 1.68
459 root 1.75 $self->child->size_allocate (0, 0, $w, $h);
460 elmex 1.36
461 root 1.42 $self->render_chld;
462 elmex 1.20 }
463    
464     sub _draw {
465     my ($self) = @_;
466    
467 elmex 1.36 my ($w, $h) = ($self->w, $self->h);
468 root 1.29
469 elmex 1.20 my $tex = $self->{texture}
470     or return;
471    
472     glEnable GL_BLEND;
473 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
474 elmex 1.20 glEnable GL_TEXTURE_2D;
475 root 1.35 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
476 elmex 1.20
477 root 1.56 $tex->draw_quad (0, 0, $w, $h);
478 elmex 1.20
479     glDisable GL_BLEND;
480     glDisable GL_TEXTURE_2D;
481     }
482    
483 root 1.39 #############################################################################
484    
485 root 1.73 package CFClient::UI::Frame;
486 elmex 1.15
487 root 1.73 our @ISA = CFClient::UI::Bin::;
488 elmex 1.15
489     use SDL::OpenGL;
490    
491     sub size_request {
492     my ($self) = @_;
493 root 1.39 my $chld = $self->child
494 elmex 1.15 or return (0, 0);
495 root 1.30
496     $chld->move (2, 2);
497    
498 elmex 1.15 map { $_ + 4 } $chld->size_request;
499     }
500    
501 elmex 1.36 sub size_allocate {
502 root 1.75 my ($self, $x, $y, $w, $h) = @_;
503 root 1.42
504 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
505 elmex 1.36
506 root 1.75 $self->child->size_allocate (2, 2, $w - 4, $h - 4);
507 elmex 1.36 }
508    
509 elmex 1.15 sub _draw {
510     my ($self) = @_;
511    
512 root 1.39 my $chld = $self->child;
513 elmex 1.15
514     my ($w, $h) = $chld->size_request;
515    
516     glBegin GL_QUADS;
517 root 1.30 glColor 0, 0, 0;
518 root 1.56 glVertex 0 , 0;
519     glVertex 0 , $h + 4;
520     glVertex $w + 4 , $h + 4;
521     glVertex $w + 4 , 0;
522 elmex 1.15 glEnd;
523    
524 root 1.23 $chld->draw;
525 elmex 1.15 }
526    
527 root 1.39 #############################################################################
528    
529 root 1.73 package CFClient::UI::FancyFrame;
530 elmex 1.31
531 root 1.73 our @ISA = CFClient::UI::Bin::;
532 elmex 1.31
533     use SDL::OpenGL;
534    
535 root 1.41 my @tex =
536 root 1.60 map { new_from_file CFClient::Texture CFClient::find_rcfile $_ }
537 root 1.41 qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
538 elmex 1.34
539     sub size_request {
540     my ($self) = @_;
541 root 1.39
542 root 1.77 my ($w, $h) =
543     $self->{user_w} && $self->{user_h}
544     ? ($self->{user_w}, $self->{user_h})
545     : ($self->SUPER::size_request);
546 elmex 1.34
547 root 1.72 $h += $tex[1]->{h};
548     $h += $tex[4]->{h};
549     $w += $tex[2]->{w};
550     $w += $tex[3]->{w};
551 elmex 1.34
552 elmex 1.36 ($w, $h)
553     }
554    
555     sub size_allocate {
556 root 1.75 my ($self, $x, $y, $w, $h) = @_;
557 root 1.68
558 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
559 root 1.40
560 root 1.77 $h -= $tex[1]{h};
561     $h -= $tex[4]{h};
562     $w -= $tex[2]{w};
563     $w -= $tex[3]{w};
564 elmex 1.36
565     $h = $h < 0 ? 0 : $h;
566     $w = $w < 0 ? 0 : $w;
567 root 1.43
568 root 1.66 my $child = $self->child;
569    
570 root 1.75 $child->size_allocate ($tex[3]->{w}, $tex[1]->{h}, $w, $h);
571 elmex 1.34 }
572    
573 root 1.77 sub button_down {
574     my ($self, $ev, $x, $y) = @_;
575    
576     if ($x < $self->{w} && $x >= $self->{w} - $tex[2]{w}
577     && $y < $self->{h} && $y >= $self->{h} - $tex[4]{h}) {
578    
579     my ($ox, $oy) = ($ev->button_x, $ev->button_y);
580     my ($bw, $bh) = ($self->{w}, $self->{h});
581    
582     $self->{motion} = sub {
583     my ($ev, $x, $y) = @_;
584    
585     ($x, $y) = ($ev->motion_x, $ev->motion_y);
586    
587     $self->{user_w} = $bw + $x - $ox;
588     $self->{user_h} = $bh + $y - $oy;
589     $self->update;
590     };
591    
592     } elsif ($x >= 0 && $x < $self->{w}
593     && $y >= 0 && $y < $tex[1]{h}) {
594    
595     my ($ox, $oy) = ($ev->button_x, $ev->button_y);
596     my ($bx, $by) = ($self->{x}, $self->{y});
597    
598     $self->{motion} = sub {
599     my ($ev, $x, $y) = @_;
600    
601     ($x, $y) = ($ev->motion_x, $ev->motion_y);
602    
603     $self->move ($bx + $x - $ox, $by + $y - $oy);
604     $self->update;
605     };
606     }
607     }
608    
609     sub button_up {
610     my ($self, $ev, $x, $y) = @_;
611    
612     delete $self->{motion};
613     }
614    
615     sub mouse_motion {
616     my ($self, $ev, $x, $y) = @_;
617    
618     $self->{motion}->($ev, $x, $y) if $self->{motion};
619     }
620    
621 elmex 1.34 sub _draw {
622     my ($self) = @_;
623    
624 root 1.43 my ($w, $h) = ($self->{w}, $self->{h});
625     my ($cw, $ch) = ($self->child->{w}, $self->child->{h});
626 elmex 1.34
627     glEnable GL_BLEND;
628 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
629 elmex 1.34 glEnable GL_TEXTURE_2D;
630 elmex 1.36 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
631 elmex 1.34
632 root 1.41 my $top = $tex[1];
633 root 1.72 $top->draw_quad (0, 0, $w, $top->{h});
634 elmex 1.34
635 root 1.41 my $left = $tex[3];
636 root 1.72 $left->draw_quad (0, $top->{h}, $left->{w}, $ch);
637 elmex 1.34
638 root 1.41 my $right = $tex[2];
639 root 1.72 $right->draw_quad ($w - $right->{w}, $top->{h}, $right->{w}, $ch);
640 elmex 1.34
641 root 1.41 my $bottom = $tex[4];
642 root 1.72 $bottom->draw_quad (0, $h - $bottom->{h}, $w, $bottom->{h});
643 elmex 1.34
644 root 1.41 my $bg = $tex[0];
645 root 1.76
646 elmex 1.36 glBindTexture GL_TEXTURE_2D, $bg->{name};
647     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
648     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT;
649     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT;
650 elmex 1.34
651 root 1.72 my $rep_x = $cw / $bg->{w};
652     my $rep_y = $ch / $bg->{h};
653 elmex 1.34
654 root 1.72 $bg->draw_quad ($left->{w}, $top->{h}, $cw, $ch);
655 elmex 1.34
656 root 1.76 glDisable GL_TEXTURE_2D;
657 elmex 1.34 glDisable GL_BLEND;
658 elmex 1.36
659 root 1.39 $self->child->draw;
660 elmex 1.36
661 elmex 1.34 }
662 elmex 1.31
663 root 1.39 #############################################################################
664    
665 root 1.73 package CFClient::UI::Table;
666 elmex 1.15
667 root 1.73 our @ISA = CFClient::UI::Base::;
668 elmex 1.15
669 root 1.75 use List::Util qw(max sum);
670    
671 elmex 1.15 use SDL::OpenGL;
672    
673     sub add {
674     my ($self, $x, $y, $chld) = @_;
675 elmex 1.32
676 root 1.38 $self->{children}[$y][$x] = $chld;
677 elmex 1.32 $chld->set_parent ($self);
678 root 1.75
679     $self->{w} = $self->{h} = -1;
680 elmex 1.32 $self->update;
681 elmex 1.15 }
682    
683 root 1.75 sub get_wh {
684     my ($self) = @_;
685    
686     my (@w, @h);
687 elmex 1.15
688 root 1.75 for my $y (0 .. $#{$self->{children}}) {
689     my $row = $self->{children}[$y]
690     or next;
691 elmex 1.15
692 root 1.75 for my $x (0 .. $#$row) {
693     my $widget = $row->[$x]
694     or next;
695     my ($w, $h) = $widget->size_request;
696 elmex 1.15
697 root 1.75 $w[$x] = max $w[$x], $w;
698     $h[$y] = max $h[$y], $h;
699 elmex 1.17 }
700 elmex 1.15 }
701 root 1.75
702     (\@w, \@h)
703 elmex 1.15 }
704    
705     sub size_request {
706     my ($self) = @_;
707    
708 root 1.75 my ($ws, $hs) = $self->get_wh;
709 elmex 1.15
710 root 1.75 (
711     (List::Util::sum @$ws),
712     (List::Util::sum @$hs),
713     )
714     }
715    
716     sub size_allocate {
717     my ($self, $x, $y, $w, $h) = @_;
718    
719     $self->_size_allocate ($x, $y, $w, $h) or return;
720    
721     my ($ws, $hs) = $self->get_wh;
722    
723     my $req_w = List::Util::sum @$ws;
724     my $req_h = List::Util::sum @$hs;
725 elmex 1.15
726 root 1.75 # linearly scale sizes
727 root 1.77 $_ *= $w / $req_w for @$ws;
728     $_ *= $h / $req_h for @$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     $_ = $h[$_] * $h / $req_h for @h;
816     } 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     $_ = $h[$_] * $h / $req_h for @h;
874     } 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.74 new_from_layout CFClient::Texture $self->{layout}
981 root 1.59 };
982 root 1.10
983 root 1.12 glEnable GL_BLEND;
984 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
985 root 1.10 glEnable GL_TEXTURE_2D;
986 root 1.28 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
987 root 1.10
988 root 1.68 glColor @{$self->{fg}};
989 root 1.12
990 root 1.72 my $x =
991 root 1.76 $self->{align} < 0 ? $self->{padding}
992     : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding}
993 root 1.74 : ($self->{w} - $tex->{w}) * 0.5;
994 root 1.72
995 root 1.77 $tex->draw_quad ($x, $self->{padding});
996 root 1.10
997 root 1.74 glDisable GL_TEXTURE_2D;
998 root 1.12 glDisable GL_BLEND;
999 root 1.10 }
1000    
1001 root 1.39 #############################################################################
1002    
1003 root 1.73 package CFClient::UI::Entry;
1004 elmex 1.31
1005 root 1.73 our @ISA = CFClient::UI::Label::;
1006 elmex 1.31
1007     use SDL;
1008     use SDL::OpenGL;
1009    
1010 root 1.68 sub new {
1011     my $class = shift;
1012    
1013     $class->SUPER::new (
1014     fg => [1, 1, 1],
1015 root 1.76 bg => [0, 0, 0, 0.2],
1016 root 1.68 active_bg => [1, 1, 1],
1017     active_fg => [0, 0, 0],
1018     @_
1019     )
1020     }
1021    
1022     sub _set_text {
1023     my ($self, $text) = @_;
1024    
1025     $self->{last_activity} = $::NOW;
1026    
1027     $self->{text} = $text;
1028     $self->{layout}->set_width ($self->{w});
1029 root 1.72
1030     $text =~ s/./*/g if $self->{hidden};
1031    
1032 root 1.76 $self->{layout}->set_markup ($self->escape_text ($text) . " ");
1033 root 1.68
1034     $text = substr $text, 0, $self->{cursor};
1035     utf8::encode $text;
1036    
1037     @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text);
1038     }
1039    
1040     sub size_request {
1041     my ($self) = @_;
1042    
1043     my ($w, $h) = $self->SUPER::size_request;
1044    
1045     ($w + 1, $h) # add 1 for cursor
1046     }
1047    
1048     sub size_allocate {
1049 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1050 root 1.68
1051 root 1.75 $self->SUPER::size_allocate ($x, $y, $w, $h);
1052 root 1.68
1053     $self->_set_text ($self->{text});
1054     }
1055    
1056     sub set_text {
1057     my ($self, $text) = @_;
1058    
1059     $self->{cursor} = length $text;
1060     $self->_set_text ($text);
1061     $self->update;
1062     }
1063    
1064 elmex 1.31 sub key_down {
1065     my ($self, $ev) = @_;
1066    
1067     my $mod = $ev->key_mod;
1068     my $sym = $ev->key_sym;
1069    
1070     my $uni = $ev->key_unicode;
1071    
1072     my $text = $self->get_text;
1073    
1074     if ($sym == SDLK_BACKSPACE) {
1075 root 1.68 substr $text, --$self->{cursor}, 1, "" if $self->{cursor};
1076     } elsif ($sym == SDLK_DELETE) {
1077     substr $text, $self->{cursor}, 1, "";
1078     } elsif ($sym == SDLK_LEFT) {
1079     --$self->{cursor} if $self->{cursor};
1080     } elsif ($sym == SDLK_RIGHT) {
1081     ++$self->{cursor} if $self->{cursor} < length $self->{text};
1082 root 1.76 } elsif ($sym == SDLK_HOME) {
1083     $self->{cursor} = 0;
1084     } elsif ($sym == SDLK_END) {
1085     $self->{cursor} = length $text;
1086 elmex 1.31 } elsif ($uni) {
1087 root 1.68 substr $text, $self->{cursor}++, 0, chr $uni;
1088 elmex 1.31 }
1089 root 1.51
1090 root 1.68 $self->_set_text ($text);
1091     $self->update;
1092     }
1093    
1094     sub focus_in {
1095     my ($self) = @_;
1096    
1097     $self->{last_activity} = $::NOW;
1098    
1099     $self->SUPER::focus_in;
1100 elmex 1.31 }
1101    
1102 root 1.51 sub button_down {
1103 root 1.68 my ($self, $ev, $x, $y) = @_;
1104    
1105     $self->SUPER::button_down ($ev, $x, $y);
1106    
1107     my $idx = $self->{layout}->xy_to_index ($x, $y);
1108    
1109     # byte-index to char-index
1110 root 1.76 my $text = $self->{text};
1111 root 1.68 utf8::encode $text;
1112     $self->{cursor} = length substr $text, 0, $idx;
1113 root 1.51
1114 root 1.68 $self->_set_text ($self->{text});
1115     $self->update;
1116 root 1.51 }
1117    
1118 root 1.58 sub mouse_motion {
1119     my ($self, $ev, $x, $y) = @_;
1120 root 1.68 # printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
1121 root 1.58 }
1122    
1123 root 1.51 sub _draw {
1124     my ($self) = @_;
1125    
1126 root 1.68 local $self->{fg} = $self->{fg};
1127    
1128 root 1.51 if ($FOCUS == $self) {
1129 root 1.68 glColor @{$self->{active_bg}};
1130     $self->{fg} = $self->{active_fg};
1131 root 1.51 } else {
1132 root 1.68 glColor @{$self->{bg}};
1133 root 1.51 }
1134    
1135 root 1.76 glEnable GL_BLEND;
1136     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1137 root 1.51 glBegin GL_QUADS;
1138 root 1.68 glVertex 0 , 0;
1139     glVertex 0 , $self->{h};
1140     glVertex $self->{w}, $self->{h};
1141     glVertex $self->{w}, 0;
1142 root 1.51 glEnd;
1143 root 1.76 glDisable GL_BLEND;
1144 root 1.51
1145     $self->SUPER::_draw;
1146 root 1.68
1147     #TODO: force update every cursor change :(
1148     if ($FOCUS == $self && (($::NOW - $self->{last_activity}) & 1023) < 600) {
1149     glColor @{$self->{fg}};
1150     glBegin GL_LINES;
1151     glVertex $self->{cur_x}, $self->{cur_y};
1152     glVertex $self->{cur_x}, $self->{cur_y} + $self->{cur_h};
1153     glEnd;
1154     }
1155     }
1156    
1157     #############################################################################
1158    
1159 root 1.73 package CFClient::UI::Slider;
1160 root 1.68
1161     use strict;
1162    
1163     use SDL::OpenGL;
1164    
1165 root 1.73 our @ISA = CFClient::UI::DrawBG::;
1166 root 1.68
1167     sub new {
1168     my $class = shift;
1169    
1170     # range [value, low, high, page]
1171    
1172 root 1.76 my $self = $class->SUPER::new (
1173 root 1.68 fg => [1, 1, 1],
1174     active_fg => [0, 0, 0],
1175     range => [0, 0, 100, 10],
1176 root 1.76 req_w => 40,
1177     req_h => 10,
1178     vertical => 0,
1179 root 1.68 @_
1180 root 1.76 );
1181    
1182     $self
1183     }
1184    
1185     sub size_request {
1186     my ($self) = @_;
1187    
1188     my $w = $self->{req_w};
1189     my $h = $self->{req_h};
1190    
1191     $self->{vertical} ? ($h, $w) : ($w, $h)
1192 root 1.68 }
1193    
1194 root 1.69 sub button_down {
1195     my ($self, $ev, $x, $y) = @_;
1196    
1197     $self->SUPER::button_down ($ev, $x, $y);
1198     $self->mouse_motion ($ev, $x, $y);
1199     }
1200    
1201     sub mouse_motion {
1202     my ($self, $ev, $x, $y) = @_;
1203    
1204     if ($GRAB == $self) {
1205     my ($value, $lo, $hi, $page) = @{$self->{range}};
1206    
1207 root 1.71 my ($x, $w) = $self->{vertical} ? ($y, $self->{h}) : ($x, $self->{w});
1208    
1209     $x = $x * ($hi - $lo) / $w + $lo;
1210 root 1.69 $x = $lo if $x < $lo;
1211     $x = $hi - $page if $x > $hi - $page;
1212     $self->{range}[0] = $x;
1213    
1214 root 1.72 $self->emit (changed => $x);
1215 root 1.69 $self->update;
1216     }
1217     }
1218    
1219 root 1.68 sub _draw {
1220     my ($self) = @_;
1221    
1222     $self->SUPER::_draw ();
1223    
1224     my ($w, $h) = @$self{qw(w h)};
1225    
1226     if ($self->{vertical}) {
1227     # draw a vertical slider like a rotated horizontal slider
1228    
1229     glRotate 90, 0, 0, 1;
1230 root 1.71 glTranslate 0, -$self->{w}, 0;
1231 root 1.68
1232     ($w, $h) = ($h, $w);
1233     }
1234    
1235     my $fg = $FOCUS == $self ? $self->{active_fg} : $self->{fg};
1236     my $bg = $FOCUS == $self ? $self->{active_bg} : $self->{bg};
1237    
1238 root 1.69 my ($value, $lo, $hi, $page) = @{$self->{range}};
1239    
1240     $page = int $page * $w / ($hi - $lo);
1241     $value = int +($value - $lo) * $w / ($hi - $lo);
1242    
1243     $w -= $page;
1244     $page &= ~1;
1245     glTranslate $page * 0.5, 0, 0;
1246    
1247 root 1.68 glColor @$fg;
1248     glBegin GL_LINES;
1249     glVertex 0, 0; glVertex 0, $h;
1250     glVertex $w - 1, 0; glVertex $w - 1, $h;
1251     glVertex 0, $h * 0.5; glVertex $w, $h * 0.5;
1252     glEnd;
1253 root 1.69
1254     my $knob_a = $value - $page * 0.5;
1255     my $knob_b = $value + $page * 0.5;
1256    
1257     glBegin GL_QUADS;
1258     glColor @$fg;
1259     glVertex $knob_a, 0;
1260     glVertex $knob_a, $h;
1261     glVertex $knob_b, $h;
1262     glVertex $knob_b, 0;
1263    
1264     if ($knob_a < $knob_b - 2) {
1265     glColor @$bg;
1266     glVertex $knob_a + 1, 1;
1267     glVertex $knob_a + 1, $h - 1;
1268     glVertex $knob_b - 1, $h - 1;
1269     glVertex $knob_b - 1, 1;
1270     }
1271     glEnd;
1272 root 1.51 }
1273    
1274 root 1.39 #############################################################################
1275    
1276 root 1.73 package CFClient::UI::MapWidget;
1277 root 1.4
1278 elmex 1.2 use strict;
1279 elmex 1.7
1280 root 1.25 use List::Util qw(min max);
1281 elmex 1.2
1282 root 1.16 use SDL;
1283 elmex 1.2 use SDL::OpenGL;
1284    
1285 root 1.73 our @ISA = CFClient::UI::Base::;
1286 root 1.25
1287 root 1.64 sub new {
1288     my $class = shift;
1289    
1290 root 1.65 $class->SUPER::new (
1291     z => -1,
1292     list => (glGenLists 1),
1293     @_
1294     )
1295 root 1.64 }
1296    
1297 elmex 1.2 sub key_down {
1298     print "MAPKEYDOWN\n";
1299     }
1300    
1301     sub key_up {
1302     }
1303    
1304 elmex 1.36 sub size_request {
1305 root 1.52 (
1306 root 1.77 1 + 32 * int $::WIDTH / 32,
1307     1 + 32 * int $::HEIGHT / 32,
1308 root 1.52 )
1309 elmex 1.36 }
1310    
1311 root 1.65 sub update {
1312     my ($self) = @_;
1313    
1314     $self->{need_update} = 1;
1315 root 1.74 $self->SUPER::update;
1316 root 1.65 }
1317    
1318 root 1.77 sub draw {
1319 root 1.21 my ($self) = @_;
1320    
1321 root 1.65 if (delete $self->{need_update}) {
1322     glNewList $self->{list}, GL_COMPILE;
1323 root 1.25
1324 root 1.65 my $mx = $::CONN->{mapx};
1325     my $my = $::CONN->{mapy};
1326 root 1.25
1327 root 1.65 my $map = $::CONN->{map};
1328 root 1.25
1329 root 1.65 my ($xofs, $yofs);
1330 root 1.25
1331 root 1.65 my $sw = 1 + int $::WIDTH / 32;
1332     my $sh = 1 + int $::HEIGHT / 32;
1333 root 1.25
1334 root 1.65 if ($::CONN->{mapw} > $sw) {
1335     $xofs = $mx + ($::CONN->{mapw} - $sw) * 0.5;
1336     } else {
1337     $xofs = $self->{xofs} = min $mx, max $mx + $::CONN->{mapw} - $sw + 1, $self->{xofs};
1338     }
1339 root 1.25
1340 root 1.65 if ($::CONN->{maph} > $sh) {
1341     $yofs = $my + ($::CONN->{maph} - $sh) * 0.5;
1342     } else {
1343     $yofs = $self->{yofs} = min $my, max $my + $::CONN->{maph} - $sh + 1, $self->{yofs};
1344     }
1345 root 1.35
1346 root 1.65 glEnable GL_TEXTURE_2D;
1347     glEnable GL_BLEND;
1348 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1349 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1350 elmex 1.2
1351 root 1.65 my $sw4 = ($sw + 3) & ~3;
1352     my $darkness = "\x00" x ($sw4 * $sh);
1353 elmex 1.2
1354 root 1.65 for my $x (0 .. $sw - 1) {
1355     my $row = $map->[$x + $xofs];
1356     for my $y (0 .. $sh - 1) {
1357    
1358     my $cell = $row->[$y + $yofs]
1359     or next;
1360    
1361     my $dark = $cell->[0];
1362     if ($dark < 0) {
1363     substr $darkness, $y * $sw4 + $x, 1, chr 224;
1364     } else {
1365     substr $darkness, $y * $sw4 + $x, 1, chr 255 - $dark;
1366     }
1367    
1368     for my $num (grep $_, @$cell[1,2,3]) {
1369     my $tex = $::CONN->{face}[$num]{texture} || next;
1370    
1371 root 1.72 my ($w, $h) = @$tex{qw(w h)};
1372 root 1.19
1373 root 1.65 $tex->draw_quad (($x + 1) * 32 - $w, ($y + 1) * 32 - $h, $w, $h);
1374     }
1375 elmex 1.2 }
1376     }
1377    
1378 root 1.35 # if (1) { # higher quality darkness
1379     # $lighting =~ s/(.)/$1$1$1/gs;
1380     # my $pb = new_from_data Gtk2::Gdk::Pixbuf $lighting, "rgb", 0, 8, $sw4, $sh, $sw4 * 3;
1381     #
1382     # $pb = $pb->scale_simple ($sw4 * 0.5, $sh * 0.5, "bilinear");
1383     #
1384     # $lighting = $pb->get_pixels;
1385     # $lighting =~ s/(.)../$1/gs;
1386     # }
1387    
1388 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1389    
1390     $darkness = new CFClient::Texture
1391 root 1.74 w => $sw4,
1392     h => $sh,
1393 root 1.65 data => $darkness,
1394     internalformat => GL_ALPHA,
1395     format => GL_ALPHA;
1396 root 1.57
1397 root 1.65 glColor 0.45, 0.45, 0.45, 1;
1398     $darkness->draw_quad (0, 0, $sw4 * 32, $sh * 32);
1399 root 1.35
1400 root 1.65 glDisable GL_TEXTURE_2D;
1401     glDisable GL_BLEND;
1402 root 1.35
1403 root 1.65 glEndList;
1404     }
1405    
1406     glCallList $self->{list};
1407 elmex 1.2 }
1408    
1409 root 1.16 my %DIR = (
1410     SDLK_KP8, [1, "north"],
1411 root 1.18 SDLK_KP9, [2, "northeast"],
1412 root 1.16 SDLK_KP6, [3, "east"],
1413     SDLK_KP3, [4, "southeast"],
1414     SDLK_KP2, [5, "south"],
1415     SDLK_KP1, [6, "southwest"],
1416     SDLK_KP4, [7, "west"],
1417     SDLK_KP7, [8, "northwest"],
1418 root 1.18
1419     SDLK_UP, [1, "north"],
1420     SDLK_RIGHT, [3, "east"],
1421     SDLK_DOWN, [5, "south"],
1422     SDLK_LEFT, [7, "west"],
1423 root 1.16 );
1424    
1425     sub key_down {
1426     my ($self, $ev) = @_;
1427    
1428     my $mod = $ev->key_mod;
1429     my $sym = $ev->key_sym;
1430    
1431     if ($sym == SDLK_KP5) {
1432     $::CONN->send ("command stay fire");
1433     } elsif (exists $DIR{$sym}) {
1434     if ($mod & KMOD_SHIFT) {
1435 root 1.18 $self->{shft}++;
1436 root 1.16 $::CONN->send ("command fire $DIR{$sym}[0]");
1437     } elsif ($mod & KMOD_CTRL) {
1438 root 1.18 $self->{ctrl}++;
1439 root 1.16 $::CONN->send ("command run $DIR{$sym}[0]");
1440     } else {
1441 root 1.18 $::CONN->send ("command $DIR{$sym}[1]");
1442 root 1.16 }
1443     }
1444     }
1445    
1446     sub key_up {
1447     my ($self, $ev) = @_;
1448    
1449     my $mod = $ev->key_mod;
1450     my $sym = $ev->key_sym;
1451    
1452 root 1.18 if (!($mod & KMOD_SHIFT) && delete $self->{shft}) {
1453     $::CONN->send ("command fire_stop");
1454     }
1455     if (!($mod & KMOD_CTRL ) && delete $self->{ctrl}) {
1456     $::CONN->send ("command run_stop");
1457 root 1.16 }
1458     }
1459    
1460 root 1.39 #############################################################################
1461    
1462 root 1.73 package CFClient::UI::Animator;
1463 root 1.35
1464     use SDL::OpenGL;
1465    
1466 root 1.73 our @ISA = CFClient::UI::Bin::;
1467 root 1.35
1468     sub moveto {
1469     my ($self, $x, $y) = @_;
1470    
1471     $self->{moveto} = [$self->{x}, $self->{y}, $x, $y];
1472 root 1.56 $self->{speed} = 0.001;
1473 root 1.35 $self->{time} = 1;
1474    
1475     ::animation_start $self;
1476     }
1477    
1478     sub animate {
1479     my ($self, $interval) = @_;
1480    
1481     $self->{time} -= $interval * $self->{speed};
1482     if ($self->{time} <= 0) {
1483     $self->{time} = 0;
1484     ::animation_stop $self;
1485     }
1486    
1487     my ($x0, $y0, $x1, $y1) = @{$self->{moveto}};
1488    
1489     $self->{x} = $x0 * $self->{time} + $x1 * (1 - $self->{time});
1490     $self->{y} = $y0 * $self->{time} + $y1 * (1 - $self->{time});
1491     }
1492    
1493     sub _draw {
1494     my ($self) = @_;
1495    
1496     glPushMatrix;
1497 root 1.51 glRotate $self->{time} * 1000, 0, 1, 0;
1498 root 1.38 $self->{children}[0]->draw;
1499 root 1.35 glPopMatrix;
1500     }
1501    
1502 root 1.51 #############################################################################
1503    
1504 root 1.73 package CFClient::UI::Toplevel;
1505 root 1.51
1506 root 1.73 our @ISA = CFClient::UI::Container::;
1507 root 1.51
1508     sub size_request {
1509     ($::WIDTH, $::HEIGHT)
1510     }
1511    
1512     sub size_allocate {
1513 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1514 root 1.51
1515 root 1.75 $self->_size_allocate ($x, $y, $w, $h);
1516 root 1.51
1517 root 1.75 $_->size_allocate ($_->{x}, $_->{y}, $_->size_request)
1518 root 1.51 for @{$self->{children}};
1519     }
1520    
1521 root 1.58 sub translate {
1522     my ($self, $x, $y) = @_;
1523    
1524     ($x, $y)
1525     }
1526    
1527 root 1.51 sub update {
1528     my ($self) = @_;
1529    
1530 root 1.75 $self->size_allocate (0, 0, $::WIDTH, $::HEIGHT);
1531 root 1.51 ::refresh ();
1532     }
1533    
1534     sub add {
1535     my ($self, $widget) = @_;
1536    
1537     $self->SUPER::add ($widget);
1538    
1539 root 1.75 $widget->size_allocate ($widget->{x}, $widget->{y}, $widget->size_request);
1540 root 1.51 }
1541    
1542     sub draw {
1543     my ($self) = @_;
1544    
1545     $self->_draw;
1546     }
1547    
1548     #############################################################################
1549    
1550 root 1.73 package CFClient::UI;
1551 root 1.51
1552 root 1.73 $TOPLEVEL = new CFClient::UI::Toplevel;
1553 root 1.51
1554     1
1555 root 1.5