ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.82
Committed: Wed Apr 12 02:00:06 2006 UTC (18 years, 1 month ago) by root
Branch: MAIN
Changes since 1.81: +4 -4 lines
Log Message:
bugfixes

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