ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.79
Committed: Wed Apr 12 00:26:50 2006 UTC (18 years, 1 month ago) by root
Branch: MAIN
Changes since 1.78: +80 -4 lines
Log Message:
add simple buttons

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.58 # translate global koordinates to local coordinate system
139     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     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT;
643     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT;
644 elmex 1.34
645 root 1.72 my $rep_x = $cw / $bg->{w};
646     my $rep_y = $ch / $bg->{h};
647 elmex 1.34
648 root 1.72 $bg->draw_quad ($left->{w}, $top->{h}, $cw, $ch);
649 elmex 1.34
650 root 1.76 glDisable GL_TEXTURE_2D;
651 elmex 1.34 glDisable GL_BLEND;
652 elmex 1.36
653 root 1.39 $self->child->draw;
654 elmex 1.36
655 elmex 1.34 }
656 elmex 1.31
657 root 1.39 #############################################################################
658    
659 root 1.73 package CFClient::UI::Table;
660 elmex 1.15
661 root 1.73 our @ISA = CFClient::UI::Base::;
662 elmex 1.15
663 root 1.75 use List::Util qw(max sum);
664    
665 elmex 1.15 use SDL::OpenGL;
666    
667 root 1.78 sub new {
668     my $class = shift;
669    
670     $class->SUPER::new (
671     col_expand => [],
672     @_
673     )
674     }
675    
676 elmex 1.15 sub add {
677     my ($self, $x, $y, $chld) = @_;
678 elmex 1.32
679 root 1.38 $self->{children}[$y][$x] = $chld;
680 elmex 1.32 $chld->set_parent ($self);
681 root 1.75
682     $self->{w} = $self->{h} = -1;
683 elmex 1.32 $self->update;
684 elmex 1.15 }
685    
686 root 1.75 sub get_wh {
687     my ($self) = @_;
688    
689     my (@w, @h);
690 elmex 1.15
691 root 1.75 for my $y (0 .. $#{$self->{children}}) {
692     my $row = $self->{children}[$y]
693     or next;
694 elmex 1.15
695 root 1.75 for my $x (0 .. $#$row) {
696     my $widget = $row->[$x]
697     or next;
698     my ($w, $h) = $widget->size_request;
699 elmex 1.15
700 root 1.75 $w[$x] = max $w[$x], $w;
701     $h[$y] = max $h[$y], $h;
702 elmex 1.17 }
703 elmex 1.15 }
704 root 1.75
705     (\@w, \@h)
706 elmex 1.15 }
707    
708     sub size_request {
709     my ($self) = @_;
710    
711 root 1.75 my ($ws, $hs) = $self->get_wh;
712 elmex 1.15
713 root 1.75 (
714 root 1.78 (sum @$ws),
715     (sum @$hs),
716 root 1.75 )
717     }
718    
719     sub size_allocate {
720     my ($self, $x, $y, $w, $h) = @_;
721    
722     $self->_size_allocate ($x, $y, $w, $h) or return;
723    
724     my ($ws, $hs) = $self->get_wh;
725    
726 root 1.78 my $req_w = sum @$ws;
727     my $req_h = sum @$hs;
728    
729     # TODO: nicer code && do row_expand
730     my @col_expand = @{$self->{col_expand}};
731     @col_expand = (1) x @$ws unless @col_expand;
732     my $col_expand = (sum @col_expand) || 1;
733 elmex 1.15
734 root 1.75 # linearly scale sizes
735 root 1.78 $ws->[$_] += $col_expand[$_] / $col_expand * ($w - $req_w) for 0 .. $#$ws;
736     $hs->[$_] *= 1 * $h / $req_h for 0 .. $#$hs;
737 elmex 1.15
738 root 1.75 my $y;
739 elmex 1.15
740 root 1.75 for my $r (0 .. $#{$self->{children}}) {
741     my $row = $self->{children}[$r]
742     or next;
743 elmex 1.15
744     my $x = 0;
745 root 1.75 my $row_h = $hs->[$r];
746    
747     for my $c (0 .. $#$row) {
748     my $widget = $row->[$c]
749     or next;
750 elmex 1.15
751 root 1.75 my $col_w = $ws->[$c];
752 elmex 1.15
753 root 1.75 $widget->size_allocate ($x, $y, $col_w, $row_h);
754 elmex 1.15
755 root 1.75 $x += $col_w;
756 elmex 1.15 }
757    
758 root 1.75 $y += $row_h;
759     }
760    
761     }
762    
763 root 1.76 sub find_widget {
764     my ($self, $x, $y) = @_;
765    
766     $x -= $self->{x};
767     $y -= $self->{y};
768    
769     my $res;
770    
771     for (grep $_, map @$_, grep $_, @{ $self->{children} }) {
772     $res = $_->find_widget ($x, $y)
773     and return $res;
774     }
775    
776     $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
777     }
778    
779 root 1.75 sub _draw {
780     my ($self) = @_;
781    
782     for (grep $_, @{$self->{children}}) {
783     $_->draw for grep $_, @$_;
784 elmex 1.15 }
785     }
786    
787 root 1.39 #############################################################################
788    
789 root 1.76 package CFClient::UI::HBox;
790    
791     # TODO: wrap into common Box base class
792    
793     our @ISA = CFClient::UI::Container::;
794    
795     sub size_request {
796     my ($self) = @_;
797    
798     my @alloc = map [$_->size_request], @{$self->{children}};
799    
800     (
801     (List::Util::sum map $_->[0], @alloc),
802     (List::Util::max map $_->[1], @alloc),
803     )
804     }
805    
806     sub size_allocate {
807     my ($self, $x, $y, $w, $h) = @_;
808    
809     $self->_size_allocate ($x, $y, $w, $h) or return;
810    
811     return unless $self->{w};
812    
813     ($h, $w) = ($w, $h);
814    
815     my $children = $self->{children};
816    
817     my @h = map +($_->size_request)[0], @$children;
818    
819     my $req_h = List::Util::sum @h;
820    
821     if ($req_h > $h) {
822     # ah well, not enough space
823 root 1.78 $_ *= $h / $req_h for @h;
824 root 1.76 } else {
825 root 1.77 my $exp = List::Util::sum map $_->{expand}, @$children;
826     $exp ||= 1;
827 root 1.76
828     for (0 .. $#$children) {
829     my $child = $children->[$_];
830    
831     my $alloc_h = $h[$_];
832 root 1.77 $alloc_h += ($h - $req_h) * $child->{expand} / $exp;
833 root 1.76 $h[$_] = $alloc_h;
834     }
835     }
836    
837     my $y = 0;
838     for (0 .. $#$children) {
839     my $child = $children->[$_];
840     my $h = $h[$_];
841     $child->size_allocate ($y, 0, $h, $w);
842    
843     $y += $h;
844     }
845     }
846    
847     #############################################################################
848    
849 root 1.73 package CFClient::UI::VBox;
850 elmex 1.15
851 root 1.76 # TODO: wrap into common Box base class
852    
853 root 1.73 our @ISA = CFClient::UI::Container::;
854 elmex 1.15
855 root 1.43 sub size_request {
856     my ($self) = @_;
857    
858     my @alloc = map [$_->size_request], @{$self->{children}};
859    
860     (
861     (List::Util::max map $_->[0], @alloc),
862     (List::Util::sum map $_->[1], @alloc),
863     )
864     }
865    
866 elmex 1.36 sub size_allocate {
867 root 1.75 my ($self, $x, $y, $w, $h) = @_;
868 elmex 1.36
869 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
870 root 1.68
871     return unless $self->{h};
872    
873     my $children = $self->{children};
874    
875     my @h = map +($_->size_request)[1], @$children;
876    
877     my $req_h = List::Util::sum @h;
878    
879     if ($req_h > $h) {
880     # ah well, not enough space
881 root 1.78 $_ *= $h / $req_h for @h;
882 root 1.68 } else {
883 root 1.77 my $exp = List::Util::sum map $_->{expand}, @$children;
884     $exp ||= 1;
885 root 1.68
886     for (0 .. $#$children) {
887     my $child = $children->[$_];
888 elmex 1.36
889 root 1.77 $h[$_] += ($h - $req_h) * $child->{expand} / $exp;
890 root 1.68 }
891 elmex 1.36 }
892    
893     my $y = 0;
894 root 1.68 for (0 .. $#$children) {
895     my $child = $children->[$_];
896     my $h = $h[$_];
897 root 1.75 $child->size_allocate (0, $y, $w, $h);
898 elmex 1.36
899 root 1.68 $y += $h;
900 elmex 1.36 }
901     }
902    
903 root 1.39 #############################################################################
904    
905 root 1.73 package CFClient::UI::Label;
906 root 1.10
907 root 1.73 our @ISA = CFClient::UI::Base::;
908 root 1.12
909 root 1.10 use SDL::OpenGL;
910    
911     sub new {
912 root 1.64 my ($class, %arg) = @_;
913 root 1.51
914 root 1.59 my $self = $class->SUPER::new (
915 root 1.76 fg => [1, 1, 1],
916     height => $::FONTSIZE,
917     text => "",
918     align => -1,
919     padding => 2,
920     layout => new CFClient::Layout,
921 root 1.64 %arg
922 root 1.59 );
923 root 1.10
924 root 1.64 $self->set_text ($self->{text});
925 root 1.10
926     $self
927     }
928    
929 root 1.68 sub escape_text {
930     local $_ = $_[1];
931    
932     s/&/&amp;/g;
933     s/>/&gt;/g;
934     s/</&lt;/g;
935    
936     $_[1]
937     }
938    
939 elmex 1.15 sub set_text {
940     my ($self, $text) = @_;
941 root 1.28
942     $self->{text} = $text;
943 root 1.59 $self->{layout}->set_markup ($text);
944 root 1.28
945 root 1.59 delete $self->{texture};
946 root 1.68 $self->update;
947 elmex 1.15 }
948    
949     sub get_text {
950     my ($self, $text) = @_;
951 root 1.28
952 elmex 1.15 $self->{text}
953     }
954    
955 root 1.14 sub size_request {
956     my ($self) = @_;
957    
958 root 1.59 $self->{layout}->set_width;
959 root 1.64 $self->{layout}->set_height ($self->{height});
960 root 1.76 my ($w, $h) = $self->{layout}->size;
961    
962     (
963     $w + $self->{padding} * 2,
964     $h + $self->{padding} * 2,
965     )
966 root 1.59 }
967 root 1.51
968 root 1.59 sub size_allocate {
969 root 1.75 my ($self, $x, $y, $w, $h) = @_;
970 root 1.51
971 root 1.75 $self->_size_allocate ($x, $y, $w, $h) or return;
972 root 1.68
973 root 1.59 delete $self->{texture};
974 root 1.14 }
975    
976 root 1.68 sub update {
977     my ($self) = @_;
978    
979     delete $self->{texture};
980     $self->SUPER::update;
981     }
982    
983 elmex 1.11 sub _draw {
984 root 1.10 my ($self) = @_;
985    
986 root 1.59 my $tex = $self->{texture} ||= do {
987     $self->{layout}->set_width ($self->{w});
988 root 1.78 $self->{layout}->set_height (List::Util::min $self->{h}, $self->{height});
989 root 1.74 new_from_layout CFClient::Texture $self->{layout}
990 root 1.59 };
991 root 1.10
992 root 1.12 glEnable GL_BLEND;
993 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
994 root 1.10 glEnable GL_TEXTURE_2D;
995 root 1.28 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
996 root 1.10
997 root 1.68 glColor @{$self->{fg}};
998 root 1.12
999 root 1.72 my $x =
1000 root 1.76 $self->{align} < 0 ? $self->{padding}
1001     : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding}
1002 root 1.74 : ($self->{w} - $tex->{w}) * 0.5;
1003 root 1.72
1004 root 1.77 $tex->draw_quad ($x, $self->{padding});
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.72
1039     $text =~ s/./*/g if $self->{hidden};
1040    
1041 root 1.76 $self->{layout}->set_markup ($self->escape_text ($text) . " ");
1042 root 1.68
1043     $text = substr $text, 0, $self->{cursor};
1044     utf8::encode $text;
1045    
1046     @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text);
1047     }
1048    
1049     sub size_request {
1050     my ($self) = @_;
1051    
1052     my ($w, $h) = $self->SUPER::size_request;
1053    
1054     ($w + 1, $h) # add 1 for cursor
1055     }
1056    
1057     sub size_allocate {
1058 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1059 root 1.68
1060 root 1.75 $self->SUPER::size_allocate ($x, $y, $w, $h);
1061 root 1.68
1062     $self->_set_text ($self->{text});
1063     }
1064    
1065     sub set_text {
1066     my ($self, $text) = @_;
1067    
1068     $self->{cursor} = length $text;
1069     $self->_set_text ($text);
1070     $self->update;
1071     }
1072    
1073 elmex 1.31 sub key_down {
1074     my ($self, $ev) = @_;
1075    
1076     my $mod = $ev->key_mod;
1077     my $sym = $ev->key_sym;
1078    
1079     my $uni = $ev->key_unicode;
1080    
1081     my $text = $self->get_text;
1082    
1083     if ($sym == SDLK_BACKSPACE) {
1084 root 1.68 substr $text, --$self->{cursor}, 1, "" if $self->{cursor};
1085     } elsif ($sym == SDLK_DELETE) {
1086     substr $text, $self->{cursor}, 1, "";
1087     } elsif ($sym == SDLK_LEFT) {
1088     --$self->{cursor} if $self->{cursor};
1089     } elsif ($sym == SDLK_RIGHT) {
1090     ++$self->{cursor} if $self->{cursor} < length $self->{text};
1091 root 1.76 } elsif ($sym == SDLK_HOME) {
1092     $self->{cursor} = 0;
1093     } elsif ($sym == SDLK_END) {
1094     $self->{cursor} = length $text;
1095 elmex 1.31 } elsif ($uni) {
1096 root 1.68 substr $text, $self->{cursor}++, 0, chr $uni;
1097 elmex 1.31 }
1098 root 1.51
1099 root 1.68 $self->_set_text ($text);
1100     $self->update;
1101     }
1102    
1103     sub focus_in {
1104     my ($self) = @_;
1105    
1106     $self->{last_activity} = $::NOW;
1107    
1108     $self->SUPER::focus_in;
1109 elmex 1.31 }
1110    
1111 root 1.51 sub button_down {
1112 root 1.68 my ($self, $ev, $x, $y) = @_;
1113    
1114     $self->SUPER::button_down ($ev, $x, $y);
1115    
1116     my $idx = $self->{layout}->xy_to_index ($x, $y);
1117    
1118     # byte-index to char-index
1119 root 1.76 my $text = $self->{text};
1120 root 1.68 utf8::encode $text;
1121     $self->{cursor} = length substr $text, 0, $idx;
1122 root 1.51
1123 root 1.68 $self->_set_text ($self->{text});
1124     $self->update;
1125 root 1.51 }
1126    
1127 root 1.58 sub mouse_motion {
1128     my ($self, $ev, $x, $y) = @_;
1129 root 1.68 # printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
1130 root 1.58 }
1131    
1132 root 1.51 sub _draw {
1133     my ($self) = @_;
1134    
1135 root 1.68 local $self->{fg} = $self->{fg};
1136    
1137 root 1.51 if ($FOCUS == $self) {
1138 root 1.68 glColor @{$self->{active_bg}};
1139     $self->{fg} = $self->{active_fg};
1140 root 1.51 } else {
1141 root 1.68 glColor @{$self->{bg}};
1142 root 1.51 }
1143    
1144 root 1.76 glEnable GL_BLEND;
1145     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1146 root 1.51 glBegin GL_QUADS;
1147 root 1.68 glVertex 0 , 0;
1148     glVertex 0 , $self->{h};
1149     glVertex $self->{w}, $self->{h};
1150     glVertex $self->{w}, 0;
1151 root 1.51 glEnd;
1152 root 1.76 glDisable GL_BLEND;
1153 root 1.51
1154     $self->SUPER::_draw;
1155 root 1.68
1156     #TODO: force update every cursor change :(
1157     if ($FOCUS == $self && (($::NOW - $self->{last_activity}) & 1023) < 600) {
1158     glColor @{$self->{fg}};
1159     glBegin GL_LINES;
1160     glVertex $self->{cur_x}, $self->{cur_y};
1161     glVertex $self->{cur_x}, $self->{cur_y} + $self->{cur_h};
1162     glEnd;
1163     }
1164     }
1165    
1166     #############################################################################
1167    
1168 root 1.79 package CFClient::UI::Button;
1169    
1170     our @ISA = CFClient::UI::Label::;
1171    
1172     use SDL;
1173     use SDL::OpenGL;
1174    
1175     sub new {
1176     my $class = shift;
1177    
1178     $class->SUPER::new (
1179     padding => 4,
1180     fg => [1, 1, 1],
1181     bg => [1, 1, 1, 0.2],
1182     active_bg => [0, 0, 0, 0.5],
1183     active_fg => [1, 1, 0],
1184     border_fg => [1, 1, 0],
1185     @_
1186     )
1187     }
1188    
1189     sub button_up {
1190     my ($self, $ev, $x, $y) = @_;
1191    
1192     if ($x >= 0 && $x < $self->{w}
1193     && $y >= 0 && $y < $self->{h}) {
1194     $self->emit ("activate");
1195     }
1196     }
1197    
1198     sub _draw {
1199     my ($self) = @_;
1200    
1201     local $self->{fg} = $self->{fg};
1202    
1203     glEnable GL_BLEND;
1204     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1205    
1206     glTranslate 0.375, 0.375, 0; # make line and polyogon coordinates behave similarly
1207    
1208     glBegin GL_LINE_LOOP;
1209     glColor @{$self->{border_fg}};
1210     glVertex 1 , 1;
1211     glVertex 1 , $self->{h} - 2;
1212     glVertex $self->{w} - 2, $self->{h} - 2;
1213     glVertex $self->{w} - 2, 1;
1214     glEnd;
1215    
1216     if ($GRAB == $self) {
1217     glColor @{$self->{active_bg}};
1218     $self->{fg} = $self->{active_fg};
1219     } else {
1220     glColor @{$self->{bg}};
1221     }
1222    
1223     glBegin GL_QUADS;
1224     glVertex 2 , 2;
1225     glVertex 2 , $self->{h} - 2;
1226     glVertex $self->{w} - 2, $self->{h} - 2;
1227     glVertex $self->{w} - 2, 2;
1228     glEnd;
1229     glDisable GL_BLEND;
1230    
1231     $self->SUPER::_draw;
1232     }
1233    
1234     #############################################################################
1235    
1236 root 1.73 package CFClient::UI::Slider;
1237 root 1.68
1238     use strict;
1239    
1240     use SDL::OpenGL;
1241    
1242 root 1.73 our @ISA = CFClient::UI::DrawBG::;
1243 root 1.68
1244     sub new {
1245     my $class = shift;
1246    
1247     # range [value, low, high, page]
1248    
1249 root 1.76 my $self = $class->SUPER::new (
1250 root 1.68 fg => [1, 1, 1],
1251     active_fg => [0, 0, 0],
1252     range => [0, 0, 100, 10],
1253 root 1.76 req_w => 40,
1254     req_h => 10,
1255     vertical => 0,
1256 root 1.68 @_
1257 root 1.76 );
1258    
1259     $self
1260     }
1261    
1262     sub size_request {
1263     my ($self) = @_;
1264    
1265     my $w = $self->{req_w};
1266     my $h = $self->{req_h};
1267    
1268     $self->{vertical} ? ($h, $w) : ($w, $h)
1269 root 1.68 }
1270    
1271 root 1.69 sub button_down {
1272     my ($self, $ev, $x, $y) = @_;
1273    
1274     $self->SUPER::button_down ($ev, $x, $y);
1275     $self->mouse_motion ($ev, $x, $y);
1276     }
1277    
1278     sub mouse_motion {
1279     my ($self, $ev, $x, $y) = @_;
1280    
1281     if ($GRAB == $self) {
1282     my ($value, $lo, $hi, $page) = @{$self->{range}};
1283    
1284 root 1.71 my ($x, $w) = $self->{vertical} ? ($y, $self->{h}) : ($x, $self->{w});
1285    
1286     $x = $x * ($hi - $lo) / $w + $lo;
1287 root 1.69 $x = $lo if $x < $lo;
1288     $x = $hi - $page if $x > $hi - $page;
1289     $self->{range}[0] = $x;
1290    
1291 root 1.72 $self->emit (changed => $x);
1292 root 1.69 $self->update;
1293     }
1294     }
1295    
1296 root 1.68 sub _draw {
1297     my ($self) = @_;
1298    
1299     $self->SUPER::_draw ();
1300    
1301     my ($w, $h) = @$self{qw(w h)};
1302    
1303     if ($self->{vertical}) {
1304     # draw a vertical slider like a rotated horizontal slider
1305    
1306     glRotate 90, 0, 0, 1;
1307 root 1.71 glTranslate 0, -$self->{w}, 0;
1308 root 1.68
1309     ($w, $h) = ($h, $w);
1310     }
1311    
1312     my $fg = $FOCUS == $self ? $self->{active_fg} : $self->{fg};
1313     my $bg = $FOCUS == $self ? $self->{active_bg} : $self->{bg};
1314    
1315 root 1.69 my ($value, $lo, $hi, $page) = @{$self->{range}};
1316    
1317     $page = int $page * $w / ($hi - $lo);
1318     $value = int +($value - $lo) * $w / ($hi - $lo);
1319    
1320     $w -= $page;
1321     $page &= ~1;
1322     glTranslate $page * 0.5, 0, 0;
1323    
1324 root 1.68 glColor @$fg;
1325     glBegin GL_LINES;
1326     glVertex 0, 0; glVertex 0, $h;
1327     glVertex $w - 1, 0; glVertex $w - 1, $h;
1328     glVertex 0, $h * 0.5; glVertex $w, $h * 0.5;
1329     glEnd;
1330 root 1.69
1331     my $knob_a = $value - $page * 0.5;
1332     my $knob_b = $value + $page * 0.5;
1333    
1334     glBegin GL_QUADS;
1335     glColor @$fg;
1336     glVertex $knob_a, 0;
1337     glVertex $knob_a, $h;
1338     glVertex $knob_b, $h;
1339     glVertex $knob_b, 0;
1340    
1341     if ($knob_a < $knob_b - 2) {
1342     glColor @$bg;
1343     glVertex $knob_a + 1, 1;
1344     glVertex $knob_a + 1, $h - 1;
1345     glVertex $knob_b - 1, $h - 1;
1346     glVertex $knob_b - 1, 1;
1347     }
1348     glEnd;
1349 root 1.51 }
1350    
1351 root 1.39 #############################################################################
1352    
1353 root 1.73 package CFClient::UI::MapWidget;
1354 root 1.4
1355 elmex 1.2 use strict;
1356 elmex 1.7
1357 root 1.25 use List::Util qw(min max);
1358 elmex 1.2
1359 root 1.16 use SDL;
1360 elmex 1.2 use SDL::OpenGL;
1361    
1362 root 1.73 our @ISA = CFClient::UI::Base::;
1363 root 1.25
1364 root 1.64 sub new {
1365     my $class = shift;
1366    
1367 root 1.65 $class->SUPER::new (
1368     z => -1,
1369     list => (glGenLists 1),
1370     @_
1371     )
1372 root 1.64 }
1373    
1374 elmex 1.2 sub key_down {
1375     print "MAPKEYDOWN\n";
1376     }
1377    
1378     sub key_up {
1379     }
1380    
1381 elmex 1.36 sub size_request {
1382 root 1.52 (
1383 root 1.77 1 + 32 * int $::WIDTH / 32,
1384     1 + 32 * int $::HEIGHT / 32,
1385 root 1.52 )
1386 elmex 1.36 }
1387    
1388 root 1.65 sub update {
1389     my ($self) = @_;
1390    
1391     $self->{need_update} = 1;
1392 root 1.74 $self->SUPER::update;
1393 root 1.65 }
1394    
1395 root 1.77 sub draw {
1396 root 1.21 my ($self) = @_;
1397    
1398 root 1.65 if (delete $self->{need_update}) {
1399     glNewList $self->{list}, GL_COMPILE;
1400 root 1.25
1401 root 1.65 my $mx = $::CONN->{mapx};
1402     my $my = $::CONN->{mapy};
1403 root 1.25
1404 root 1.65 my $map = $::CONN->{map};
1405 root 1.25
1406 root 1.65 my ($xofs, $yofs);
1407 root 1.25
1408 root 1.65 my $sw = 1 + int $::WIDTH / 32;
1409     my $sh = 1 + int $::HEIGHT / 32;
1410 root 1.25
1411 root 1.65 if ($::CONN->{mapw} > $sw) {
1412     $xofs = $mx + ($::CONN->{mapw} - $sw) * 0.5;
1413     } else {
1414     $xofs = $self->{xofs} = min $mx, max $mx + $::CONN->{mapw} - $sw + 1, $self->{xofs};
1415     }
1416 root 1.25
1417 root 1.65 if ($::CONN->{maph} > $sh) {
1418     $yofs = $my + ($::CONN->{maph} - $sh) * 0.5;
1419     } else {
1420     $yofs = $self->{yofs} = min $my, max $my + $::CONN->{maph} - $sh + 1, $self->{yofs};
1421     }
1422 root 1.35
1423 root 1.65 glEnable GL_TEXTURE_2D;
1424     glEnable GL_BLEND;
1425 root 1.74 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
1426 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1427 elmex 1.2
1428 root 1.65 my $sw4 = ($sw + 3) & ~3;
1429     my $darkness = "\x00" x ($sw4 * $sh);
1430 elmex 1.2
1431 root 1.65 for my $x (0 .. $sw - 1) {
1432     my $row = $map->[$x + $xofs];
1433     for my $y (0 .. $sh - 1) {
1434    
1435     my $cell = $row->[$y + $yofs]
1436     or next;
1437    
1438     my $dark = $cell->[0];
1439     if ($dark < 0) {
1440     substr $darkness, $y * $sw4 + $x, 1, chr 224;
1441     } else {
1442     substr $darkness, $y * $sw4 + $x, 1, chr 255 - $dark;
1443     }
1444    
1445     for my $num (grep $_, @$cell[1,2,3]) {
1446     my $tex = $::CONN->{face}[$num]{texture} || next;
1447    
1448 root 1.72 my ($w, $h) = @$tex{qw(w h)};
1449 root 1.19
1450 root 1.65 $tex->draw_quad (($x + 1) * 32 - $w, ($y + 1) * 32 - $h, $w, $h);
1451     }
1452 elmex 1.2 }
1453     }
1454    
1455 root 1.35 # if (1) { # higher quality darkness
1456     # $lighting =~ s/(.)/$1$1$1/gs;
1457     # my $pb = new_from_data Gtk2::Gdk::Pixbuf $lighting, "rgb", 0, 8, $sw4, $sh, $sw4 * 3;
1458     #
1459     # $pb = $pb->scale_simple ($sw4 * 0.5, $sh * 0.5, "bilinear");
1460     #
1461     # $lighting = $pb->get_pixels;
1462     # $lighting =~ s/(.)../$1/gs;
1463     # }
1464    
1465 root 1.65 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1466    
1467     $darkness = new CFClient::Texture
1468 root 1.74 w => $sw4,
1469     h => $sh,
1470 root 1.65 data => $darkness,
1471     internalformat => GL_ALPHA,
1472     format => GL_ALPHA;
1473 root 1.57
1474 root 1.65 glColor 0.45, 0.45, 0.45, 1;
1475     $darkness->draw_quad (0, 0, $sw4 * 32, $sh * 32);
1476 root 1.35
1477 root 1.65 glDisable GL_TEXTURE_2D;
1478     glDisable GL_BLEND;
1479 root 1.35
1480 root 1.65 glEndList;
1481     }
1482    
1483     glCallList $self->{list};
1484 elmex 1.2 }
1485    
1486 root 1.16 my %DIR = (
1487     SDLK_KP8, [1, "north"],
1488 root 1.18 SDLK_KP9, [2, "northeast"],
1489 root 1.16 SDLK_KP6, [3, "east"],
1490     SDLK_KP3, [4, "southeast"],
1491     SDLK_KP2, [5, "south"],
1492     SDLK_KP1, [6, "southwest"],
1493     SDLK_KP4, [7, "west"],
1494     SDLK_KP7, [8, "northwest"],
1495 root 1.18
1496     SDLK_UP, [1, "north"],
1497     SDLK_RIGHT, [3, "east"],
1498     SDLK_DOWN, [5, "south"],
1499     SDLK_LEFT, [7, "west"],
1500 root 1.16 );
1501    
1502     sub key_down {
1503     my ($self, $ev) = @_;
1504    
1505     my $mod = $ev->key_mod;
1506     my $sym = $ev->key_sym;
1507    
1508     if ($sym == SDLK_KP5) {
1509     $::CONN->send ("command stay fire");
1510     } elsif (exists $DIR{$sym}) {
1511     if ($mod & KMOD_SHIFT) {
1512 root 1.18 $self->{shft}++;
1513 root 1.16 $::CONN->send ("command fire $DIR{$sym}[0]");
1514     } elsif ($mod & KMOD_CTRL) {
1515 root 1.18 $self->{ctrl}++;
1516 root 1.16 $::CONN->send ("command run $DIR{$sym}[0]");
1517     } else {
1518 root 1.18 $::CONN->send ("command $DIR{$sym}[1]");
1519 root 1.16 }
1520     }
1521     }
1522    
1523     sub key_up {
1524     my ($self, $ev) = @_;
1525    
1526     my $mod = $ev->key_mod;
1527     my $sym = $ev->key_sym;
1528    
1529 root 1.18 if (!($mod & KMOD_SHIFT) && delete $self->{shft}) {
1530     $::CONN->send ("command fire_stop");
1531     }
1532     if (!($mod & KMOD_CTRL ) && delete $self->{ctrl}) {
1533     $::CONN->send ("command run_stop");
1534 root 1.16 }
1535     }
1536    
1537 root 1.39 #############################################################################
1538    
1539 root 1.73 package CFClient::UI::Animator;
1540 root 1.35
1541     use SDL::OpenGL;
1542    
1543 root 1.73 our @ISA = CFClient::UI::Bin::;
1544 root 1.35
1545     sub moveto {
1546     my ($self, $x, $y) = @_;
1547    
1548     $self->{moveto} = [$self->{x}, $self->{y}, $x, $y];
1549 root 1.56 $self->{speed} = 0.001;
1550 root 1.35 $self->{time} = 1;
1551    
1552     ::animation_start $self;
1553     }
1554    
1555     sub animate {
1556     my ($self, $interval) = @_;
1557    
1558     $self->{time} -= $interval * $self->{speed};
1559     if ($self->{time} <= 0) {
1560     $self->{time} = 0;
1561     ::animation_stop $self;
1562     }
1563    
1564     my ($x0, $y0, $x1, $y1) = @{$self->{moveto}};
1565    
1566     $self->{x} = $x0 * $self->{time} + $x1 * (1 - $self->{time});
1567     $self->{y} = $y0 * $self->{time} + $y1 * (1 - $self->{time});
1568     }
1569    
1570     sub _draw {
1571     my ($self) = @_;
1572    
1573     glPushMatrix;
1574 root 1.51 glRotate $self->{time} * 1000, 0, 1, 0;
1575 root 1.38 $self->{children}[0]->draw;
1576 root 1.35 glPopMatrix;
1577     }
1578    
1579 root 1.51 #############################################################################
1580    
1581 root 1.73 package CFClient::UI::Toplevel;
1582 root 1.51
1583 root 1.73 our @ISA = CFClient::UI::Container::;
1584 root 1.51
1585     sub size_request {
1586     ($::WIDTH, $::HEIGHT)
1587     }
1588    
1589     sub size_allocate {
1590 root 1.75 my ($self, $x, $y, $w, $h) = @_;
1591 root 1.51
1592 root 1.75 $self->_size_allocate ($x, $y, $w, $h);
1593 root 1.51
1594 root 1.75 $_->size_allocate ($_->{x}, $_->{y}, $_->size_request)
1595 root 1.51 for @{$self->{children}};
1596     }
1597    
1598 root 1.58 sub translate {
1599     my ($self, $x, $y) = @_;
1600    
1601     ($x, $y)
1602     }
1603    
1604 root 1.51 sub update {
1605     my ($self) = @_;
1606    
1607 root 1.75 $self->size_allocate (0, 0, $::WIDTH, $::HEIGHT);
1608 root 1.51 ::refresh ();
1609     }
1610    
1611     sub add {
1612     my ($self, $widget) = @_;
1613    
1614     $self->SUPER::add ($widget);
1615    
1616 root 1.75 $widget->size_allocate ($widget->{x}, $widget->{y}, $widget->size_request);
1617 root 1.51 }
1618    
1619     sub draw {
1620     my ($self) = @_;
1621    
1622     $self->_draw;
1623     }
1624    
1625     #############################################################################
1626    
1627 root 1.73 package CFClient::UI;
1628 root 1.51
1629 root 1.73 $TOPLEVEL = new CFClient::UI::Toplevel;
1630 root 1.51
1631     1
1632 root 1.5