ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
Revision: 1.67
Committed: Tue Apr 11 14:36:59 2006 UTC (18 years, 2 months ago) by root
Branch: MAIN
Changes since 1.66: +1 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.60 package CFClient::Widget;
2 root 1.8
3 elmex 1.1 use strict;
4 root 1.18
5     use Scalar::Util;
6    
7 elmex 1.11 use SDL::OpenGL;
8     use SDL::OpenGL::Constants;
9 elmex 1.1
10 root 1.60 use CFClient;
11 root 1.41
12 root 1.51 our ($FOCUS, $HOVER, $GRAB); # various widgets
13    
14     our $TOPLEVEL;
15     our $BUTTON_STATE;
16    
17 elmex 1.1 # class methods for events
18 root 1.51 sub feed_sdl_key_down_event {
19     $FOCUS->key_down ($_[0]) if $FOCUS;
20     }
21    
22     sub feed_sdl_key_up_event {
23     $FOCUS->key_up ($_[0]) if $FOCUS;
24     }
25    
26     sub feed_sdl_button_down_event {
27     my ($ev) = @_;
28     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
29    
30     if (!$BUTTON_STATE) {
31     my $widget = $TOPLEVEL->find_widget ($x, $y);
32    
33     $GRAB = $widget;
34     $GRAB->update if $GRAB;
35     }
36    
37     $BUTTON_STATE |= 1 << ($ev->button - 1);
38    
39 root 1.58 $GRAB->button_down ($ev, $GRAB->translate ($x, $y)) if $GRAB;
40 root 1.51 }
41    
42     sub feed_sdl_button_up_event {
43     my ($ev) = @_;
44     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
45    
46     my $widget = $GRAB || $TOPLEVEL->find_widget ($x, $y);
47    
48     $BUTTON_STATE &= ~(1 << ($ev->button - 1));
49    
50 root 1.58 $GRAB->button_down ($ev, $GRAB->translate ($x, $y)) if $GRAB;
51    
52 root 1.51 if (!$BUTTON_STATE) {
53     my $grab = $GRAB; undef $GRAB;
54     $grab->update if $grab;
55     $GRAB->update if $GRAB;
56     }
57     }
58    
59     sub feed_sdl_motion_event {
60     my ($ev) = @_;
61     my ($x, $y) = ($ev->motion_x, $ev->motion_y);
62    
63     my $widget = $GRAB || $TOPLEVEL->find_widget ($x, $y);
64    
65     if ($widget != $HOVER) {
66     my $hover = $HOVER; $HOVER = $widget;
67    
68     $hover->update if $hover;
69     $HOVER->update if $HOVER;
70     }
71    
72 root 1.58 $HOVER->mouse_motion ($ev, $HOVER->translate ($x, $y)) if $HOVER;
73 root 1.51 }
74 elmex 1.1
75     sub new {
76     my $class = shift;
77 root 1.10
78 root 1.65 bless {
79     x => 0,
80     y => 0,
81     z => 0,
82     @_
83     }, $class
84 elmex 1.1 }
85    
86 root 1.18 sub move {
87     my ($self, $x, $y, $z) = @_;
88     $self->{x} = $x;
89     $self->{y} = $y;
90     $self->{z} = $z if defined $z;
91     }
92    
93 elmex 1.20 sub needs_redraw {
94     0
95     }
96    
97 root 1.14 sub size_request {
98 elmex 1.36 require Carp;
99     Carp::confess "size_request is abtract";
100     }
101    
102     sub size_allocate {
103     my ($self, $w, $h) = @_;
104 root 1.40
105     $self->{w} = $w;
106     $self->{h} = $h;
107 root 1.14 }
108    
109 root 1.58 # translate global koordinates to local coordinate system
110     sub translate {
111     my ($self, $x, $y) = @_;
112    
113     $self->{parent}->translate ($x - $self->{x}, $y - $self->{y});
114     }
115    
116 elmex 1.1 sub focus_in {
117 root 1.51 my ($self) = @_;
118    
119     my $focus = $FOCUS; $FOCUS = $self;
120     $focus->update if $focus;
121     $FOCUS->update;
122 elmex 1.1 }
123 root 1.4
124 elmex 1.1 sub focus_out {
125 root 1.51 my ($self) = @_;
126 root 1.4
127 root 1.51 return unless $FOCUS == $self;
128 root 1.4
129 root 1.51 my $focus = $FOCUS; undef $FOCUS;
130     $focus->update if $focus; #?
131 elmex 1.1 }
132 root 1.4
133 root 1.51 sub mouse_motion { }
134     sub button_up { }
135     sub button_down { }
136     sub key_down { }
137     sub key_up { }
138    
139     sub w { $_[0]{w} = $_[1] if @_ > 1; $_[0]{w} }
140     sub h { $_[0]{h} = $_[1] if @_ > 1; $_[0]{h} }
141     sub x { $_[0]{x} = $_[1] if @_ > 1; $_[0]{x} }
142     sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} }
143     sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} }
144 elmex 1.11
145 elmex 1.1 sub draw {
146 elmex 1.11 my ($self) = @_;
147    
148     glPushMatrix;
149 root 1.12 glTranslate $self->{x}, $self->{y}, 0;
150 elmex 1.11 $self->_draw;
151 root 1.51 if ($self == $HOVER) {
152 root 1.48 glColor 1, 1, 1, 0.4;
153 root 1.50 glEnable GL_BLEND;
154 root 1.48 glBegin GL_QUADS;
155 root 1.51 glVertex 0 , 0;
156     glVertex $self->{w}, 0;
157     glVertex $self->{w}, $self->{h};
158     glVertex 0 , $self->{h};
159 root 1.47 glEnd;
160 root 1.50 glDisable GL_BLEND;
161 root 1.47 }
162 elmex 1.11 glPopMatrix;
163     }
164    
165     sub _draw {
166 root 1.38 my ($self) = @_;
167    
168     warn "no draw defined for $self\n";
169 elmex 1.1 }
170 root 1.4
171 elmex 1.1 sub bbox {
172 elmex 1.32 my ($self) = @_;
173     my ($w, $h) = $self->size_request;
174     (
175     $self->{x},
176     $self->{y},
177     $self->{x} = $w,
178     $self->{y} = $h
179     )
180     }
181    
182 root 1.38 sub find_widget {
183     my ($self, $x, $y) = @_;
184    
185     return $self
186     if $x >= $self->{x} && $x < $self->{x} + $self->{w}
187     && $y >= $self->{y} && $y < $self->{y} + $self->{h};
188    
189     ()
190     }
191    
192 elmex 1.32 sub del_parent { $_[0]->{parent} = undef }
193    
194     sub set_parent {
195     my ($self, $par) = @_;
196    
197     $self->{parent} = $par;
198     Scalar::Util::weaken $self->{parent};
199     }
200    
201     sub get_parent {
202     $_[0]->{parent}
203     }
204    
205     sub update {
206     my ($self) = @_;
207    
208     $self->{parent}->update
209     if $self->{parent};
210 elmex 1.1 }
211 elmex 1.2
212 root 1.18 sub DESTROY {
213     my ($self) = @_;
214    
215 elmex 1.32 #$self->deactivate;
216 root 1.18 }
217    
218 root 1.39 #############################################################################
219    
220 root 1.66 package CFClient::Widget::Empty;
221    
222     our @ISA = CFClient::Widget::;
223    
224     sub size_request {
225     (0, 0)
226     }
227    
228 root 1.67 sub draw { }
229 root 1.66
230     #############################################################################
231    
232 root 1.60 package CFClient::Widget::Container;
233 elmex 1.15
234 root 1.60 our @ISA = CFClient::Widget::;
235 elmex 1.15
236 root 1.38 sub new {
237 root 1.64 my ($class, %arg) = @_;
238    
239     my $children = delete $arg{children} || [];
240 root 1.38
241 root 1.65 my $self = $class->SUPER::new (children => [], %arg);
242 root 1.64 $self->add ($_) for @$children;
243 root 1.38
244     $self
245     }
246    
247     sub add {
248     my ($self, $chld, $expand) = @_;
249    
250     $chld->{expand} = $expand;
251     $chld->set_parent ($self);
252    
253 root 1.66 $self->{children} = [
254 root 1.38 sort { $a->{z} <=> $b->{z} }
255 root 1.66 @{$self->{children}}, $chld
256     ];
257 root 1.38
258 root 1.43 $self->size_allocate ($self->{w}, $self->{h})
259     if $self->{w}; #TODO: check for "realised state"
260 root 1.38 }
261 root 1.35
262 elmex 1.32 sub remove {
263 root 1.38 my ($self, $widget) = @_;
264    
265     $self->{children} = [ grep $_ != $widget, @{ $self->{children} } ];
266    
267     $self->size_allocate ($self->{w}, $self->{h});
268     }
269    
270     sub find_widget {
271     my ($self, $x, $y) = @_;
272    
273 root 1.45 $x -= $self->{x};
274     $y -= $self->{y};
275    
276 root 1.38 my $res;
277    
278 root 1.46 for (reverse @{ $self->{children} }) {
279 root 1.45 $res = $_->find_widget ($x, $y)
280 root 1.38 and return $res;
281     }
282    
283 root 1.46 $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
284 elmex 1.32 }
285 elmex 1.15
286 root 1.35 sub _draw {
287     my ($self) = @_;
288    
289 root 1.38 $_->draw for @{$self->{children}};
290 root 1.35 }
291 elmex 1.15
292 root 1.39 #############################################################################
293    
294 root 1.60 package CFClient::Widget::Bin;
295 elmex 1.32
296 root 1.60 our @ISA = CFClient::Widget::Container::;
297 elmex 1.32
298 root 1.66 sub new {
299     my ($class, %arg) = @_;
300    
301     my $child = (delete $arg{child}) || new CFClient::Widget::Empty::;
302    
303     $class->SUPER::new (children => [$child], %arg)
304     }
305    
306     sub add {
307     my ($self, $widget) = @_;
308    
309     $self->{children} = [];
310    
311     $self->SUPER::add ($widget);
312     }
313    
314     sub remove {
315     my ($self, $widget) = @_;
316    
317     $self->SUPER::remove ($widget);
318    
319     $self->{children} = [new CFClient::Widget::Empty]
320     unless @{$self->{children}};
321     }
322    
323 root 1.39 sub child { $_[0]->{children}[0] }
324 elmex 1.32
325 root 1.38 sub size_request {
326     $_[0]{children}[0]->size_request if $_[0]{children}[0];
327     }
328 elmex 1.32
329 root 1.38 sub size_allocate {
330     my ($self, $w, $h) = @_;
331 root 1.42
332 root 1.38 $self->SUPER::size_allocate ($w, $h);
333     $self->{children}[0]->size_allocate ($w, $h)
334     if $self->{children}[0]
335     }
336 elmex 1.32
337 root 1.39 #############################################################################
338    
339 root 1.60 package CFClient::Widget::Window;
340 elmex 1.20
341 root 1.60 our @ISA = CFClient::Widget::Bin::;
342 elmex 1.20
343     use SDL::OpenGL;
344    
345 root 1.42 sub new {
346 root 1.64 my ($class, %arg) = @_;
347 elmex 1.32
348 root 1.64 my $self = $class->SUPER::new (%arg);
349 elmex 1.32 }
350    
351     sub update {
352     my ($self) = @_;
353 root 1.42
354 root 1.63 # we want to do this delayed...
355 elmex 1.32 $self->render_chld;
356 root 1.42 $self->SUPER::update;
357 elmex 1.20 }
358    
359     sub render_chld {
360     my ($self) = @_;
361    
362     $self->{texture} =
363 root 1.60 CFClient::Texture->new_from_opengl (
364 root 1.42 $self->{w}, $self->{h}, sub { $self->child->draw }
365 elmex 1.20 );
366 elmex 1.36 }
367    
368     sub size_allocate {
369     my ($self, $w, $h) = @_;
370    
371 root 1.42 $self->{w} = $w;
372     $self->{h} = $h;
373    
374     $self->child->size_allocate ($w, $h);
375 elmex 1.36
376 root 1.42 $self->render_chld;
377 elmex 1.20 }
378    
379     sub _draw {
380     my ($self) = @_;
381    
382 elmex 1.36 my ($w, $h) = ($self->w, $self->h);
383 root 1.29
384 elmex 1.20 my $tex = $self->{texture}
385     or return;
386    
387     glEnable GL_BLEND;
388     glEnable GL_TEXTURE_2D;
389 root 1.35 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
390 elmex 1.20
391 root 1.56 $tex->draw_quad (0, 0, $w, $h);
392 elmex 1.20
393     glDisable GL_BLEND;
394     glDisable GL_TEXTURE_2D;
395     }
396    
397 root 1.39 #############################################################################
398    
399 root 1.60 package CFClient::Widget::Frame;
400 elmex 1.15
401 root 1.60 our @ISA = CFClient::Widget::Bin::;
402 elmex 1.15
403     use SDL::OpenGL;
404    
405     sub size_request {
406     my ($self) = @_;
407 root 1.39 my $chld = $self->child
408 elmex 1.15 or return (0, 0);
409 root 1.30
410     $chld->move (2, 2);
411    
412 elmex 1.15 map { $_ + 4 } $chld->size_request;
413     }
414    
415 elmex 1.36 sub size_allocate {
416     my ($self, $w, $h) = @_;
417 root 1.42
418     $self->{w} = $w;
419     $self->{h} = $h;
420 elmex 1.36
421 root 1.39 $self->child->size_allocate ($w - 4, $h - 4);
422     $self->child->move (2, 2);
423 elmex 1.36 }
424    
425 elmex 1.15 sub _draw {
426     my ($self) = @_;
427    
428 root 1.39 my $chld = $self->child;
429 elmex 1.15
430     my ($w, $h) = $chld->size_request;
431    
432     glBegin GL_QUADS;
433 root 1.30 glColor 0, 0, 0;
434 root 1.56 glVertex 0 , 0;
435     glVertex 0 , $h + 4;
436     glVertex $w + 4 , $h + 4;
437     glVertex $w + 4 , 0;
438 elmex 1.15 glEnd;
439    
440 root 1.23 $chld->draw;
441 elmex 1.15 }
442    
443 root 1.39 #############################################################################
444    
445 root 1.60 package CFClient::Widget::FancyFrame;
446 elmex 1.31
447 root 1.60 our @ISA = CFClient::Widget::Bin::;
448 elmex 1.31
449     use SDL::OpenGL;
450    
451 root 1.41 my @tex =
452 root 1.60 map { new_from_file CFClient::Texture CFClient::find_rcfile $_ }
453 root 1.41 qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
454 elmex 1.34
455     sub size_request {
456     my ($self) = @_;
457 root 1.39
458     my ($w, $h) = $self->SUPER::size_request;
459 elmex 1.34
460 root 1.41 $h += $tex[1]->{height};
461     $h += $tex[4]->{height};
462     $w += $tex[2]->{width};
463     $w += $tex[3]->{width};
464 elmex 1.34
465 elmex 1.36 ($w, $h)
466     }
467    
468     sub size_allocate {
469     my ($self, $w, $h) = @_;
470    
471 root 1.40 $self->SUPER::size_allocate ($w, $h);
472    
473 root 1.41 $h -= $tex[1]->{height};
474     $h -= $tex[4]->{height};
475     $w -= $tex[2]->{width};
476     $w -= $tex[3]->{width};
477 elmex 1.36
478     $h = $h < 0 ? 0 : $h;
479     $w = $w < 0 ? 0 : $w;
480 root 1.43
481 root 1.66 my $child = $self->child;
482    
483     $child->size_allocate ($w, $h);
484     $child->move ($tex[3]->{width}, $tex[1]->{height});
485 elmex 1.34 }
486    
487     sub _draw {
488     my ($self) = @_;
489    
490 root 1.43 my ($w, $h) = ($self->{w}, $self->{h});
491     my ($cw, $ch) = ($self->child->{w}, $self->child->{h});
492 elmex 1.34
493     glEnable GL_BLEND;
494     glEnable GL_TEXTURE_2D;
495     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
496 elmex 1.36 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
497 elmex 1.34
498 root 1.41 my $top = $tex[1];
499 root 1.56 $top->draw_quad (0, 0, $w, $top->{height});
500 elmex 1.34
501 root 1.41 my $left = $tex[3];
502 root 1.56 $left->draw_quad (0, $top->{height}, $left->{width}, $ch);
503 elmex 1.34
504 root 1.41 my $right = $tex[2];
505 root 1.56 $right->draw_quad ($w - $right->{width}, $top->{height}, $right->{width}, $ch);
506 elmex 1.34
507 root 1.41 my $bottom = $tex[4];
508 root 1.56 $bottom->draw_quad (0, $h - $bottom->{height}, $w, $bottom->{height});
509 elmex 1.34
510 root 1.41 my $bg = $tex[0];
511 elmex 1.36 glBindTexture GL_TEXTURE_2D, $bg->{name};
512     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
513     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT;
514     glTexParameter GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT;
515 elmex 1.34
516 elmex 1.36 my $rep_x = $cw / $bg->{width};
517     my $rep_y = $ch / $bg->{height};
518 elmex 1.34
519 root 1.56 $bg->draw_quad ($left->{width}, $top->{height}, $cw, $ch);
520 elmex 1.34
521     glDisable GL_BLEND;
522     glDisable GL_TEXTURE_2D;
523 elmex 1.36
524 root 1.39 $self->child->draw;
525 elmex 1.36
526 elmex 1.34 }
527 elmex 1.31
528 root 1.39 #############################################################################
529    
530 root 1.60 package CFClient::Widget::Table;
531 elmex 1.15
532 root 1.60 our @ISA = CFClient::Widget::Bin::;
533 elmex 1.15
534     use SDL::OpenGL;
535    
536     sub add {
537     my ($self, $x, $y, $chld) = @_;
538 root 1.38 my $old_chld = $self->{children}[$y][$x];
539 elmex 1.32
540 root 1.38 $self->{children}[$y][$x] = $chld;
541 elmex 1.32 $chld->set_parent ($self);
542     $self->update;
543 elmex 1.15 }
544    
545     sub max_row_height {
546     my ($self, $row) = @_;
547    
548     my $hs = 0;
549 root 1.38 for (my $xi = 0; $xi <= $#{$self->{children}->[$row] || []}; $xi++) {
550     my $c = $self->{children}->[$row]->[$xi];
551 elmex 1.17 if ($c) {
552     my ($w, $h) = $c->size_request;
553     if ($hs < $h) { $hs = $h }
554     }
555 elmex 1.15 }
556     return $hs;
557     }
558    
559     sub max_col_width {
560     my ($self, $col) = @_;
561    
562     my $ws = 0;
563 root 1.38 for (my $yi = 0; $yi <= $#{$self->{children} || []}; $yi++) {
564     my $c = ($self->{children}->[$yi] || [])->[$col];
565 elmex 1.17 if ($c) {
566     my ($w, $h) = $c->size_request;
567     if ($ws < $w) { $ws = $w }
568     }
569 elmex 1.15 }
570     return $ws;
571     }
572    
573     sub size_request {
574     my ($self) = @_;
575    
576     my ($hs, $ws) = (0, 0);
577    
578 root 1.38 for (my $yi = 0; $yi <= $#{$self->{children}}; $yi++) {
579 elmex 1.15 $hs += $self->max_row_height ($yi);
580     }
581    
582 root 1.38 for (my $yi = 0; $yi <= $#{$self->{children}}; $yi++) {
583 elmex 1.15 my $wm = 0;
584 root 1.38 for (my $xi = 0; $xi <= $#{$self->{children}->[$yi]}; $xi++) {
585 elmex 1.15 $wm += $self->max_col_width ($xi)
586     }
587     if ($ws < $wm) { $ws = $wm }
588     }
589    
590     return ($ws, $hs);
591     }
592    
593     sub _draw {
594     my ($self) = @_;
595    
596     my $y = 0;
597 root 1.38 for (my $yi = 0; $yi <= $#{$self->{children}}; $yi++) {
598 elmex 1.15 my $x = 0;
599    
600 root 1.38 for (my $xi = 0; $xi <= $#{$self->{children}->[$yi]}; $xi++) {
601 elmex 1.15
602 root 1.38 my $c = $self->{children}->[$yi]->[$xi];
603 elmex 1.26 if ($c) {
604     $c->move ($x, $y, 0); #TODO: Move to size_request
605     $c->draw if $c;
606     }
607 elmex 1.15
608     $x += $self->max_col_width ($xi);
609     }
610    
611     $y += $self->max_row_height ($yi);
612     }
613     }
614    
615 root 1.39 #############################################################################
616    
617 root 1.60 package CFClient::Widget::VBox;
618 elmex 1.15
619 root 1.60 our @ISA = CFClient::Widget::Container::;
620 elmex 1.15
621     use SDL::OpenGL;
622    
623 root 1.43 sub size_request {
624     my ($self) = @_;
625    
626     my @alloc = map [$_->size_request], @{$self->{children}};
627    
628     (
629     (List::Util::max map $_->[0], @alloc),
630     (List::Util::sum map $_->[1], @alloc),
631     )
632     }
633    
634 elmex 1.36 sub size_allocate {
635     my ($self, $w, $h) = @_;
636    
637     $self->w ($w);
638     $self->h ($h);
639    
640     my $exp;
641     my @oth;
642     # find expand widget
643 root 1.38 for (@{$self->{children}}) {
644 elmex 1.36 if ($_->{expand}) {
645     $exp = $_;
646     last;
647     }
648     push @oth, $_;
649     }
650    
651     my ($ow, $oh);
652    
653     # get sizes of other widgets
654     for (@oth) {
655     my ($w, $h) = $_->size_request;
656     $oh += $h;
657     if ($ow < $w) { $ow = $w }
658     }
659    
660     my $y = 0;
661 root 1.38 for (@{$self->{children}}) {
662 elmex 1.36 $_->move (0, $y);
663    
664     if ($_ == $exp) {
665     $_->size_allocate ($w, $h - $oh);
666     $y += $h - $oh;
667     } else {
668     my ($cw, $h) = $_->size_request;
669     $_->size_allocate ($w, $h);
670     $y += $h;
671     }
672     }
673     }
674    
675 root 1.39 #############################################################################
676    
677 root 1.60 package CFClient::Widget::Label;
678 root 1.10
679 root 1.60 our @ISA = CFClient::Widget::;
680 root 1.12
681 root 1.10 use SDL::OpenGL;
682    
683     sub new {
684 root 1.64 my ($class, %arg) = @_;
685 root 1.51
686 root 1.33 # TODO: color, and make height, xyz etc. optional
687 root 1.59 my $self = $class->SUPER::new (
688 root 1.64 color => [1, 1, 1],
689     height => $::FONTSIZE,
690     text => "",
691     layout => new CFClient::Layout,
692     %arg
693 root 1.59 );
694 root 1.10
695 root 1.64 $self->set_text ($self->{text});
696 root 1.10
697     $self
698     }
699    
700 elmex 1.15 sub set_text {
701     my ($self, $text) = @_;
702 root 1.28
703     $self->{text} = $text;
704 root 1.59 $self->{layout}->set_markup ($text);
705 root 1.28
706 root 1.59 delete $self->{texture};
707 elmex 1.15 }
708    
709     sub get_text {
710     my ($self, $text) = @_;
711 root 1.28
712 elmex 1.15 $self->{text}
713     }
714    
715 root 1.14 sub size_request {
716     my ($self) = @_;
717    
718 root 1.59 $self->{layout}->set_width;
719 root 1.64 $self->{layout}->set_height ($self->{height});
720 root 1.59 $self->{layout}->size
721     # if ($self->{texture}{width} > 1 && $self->{texture}{height} > 1) { #TODO: hack
722     # (
723     # $self->{texture}{width},
724     # $self->{texture}{height},
725     # )
726     # } else {
727 root 1.60 # my ($w, $h, $data) = CFClient::font_render "Yy", $self->{height};
728 root 1.59 #
729     # ($w, $h)
730     # }
731     }
732 root 1.51
733 root 1.59 sub size_allocate {
734     my ($self, $w, $h) = @_;
735 root 1.51
736 root 1.59 $self->SUPER::size_allocate ($w, $h);
737     delete $self->{texture};
738 root 1.14 }
739    
740 elmex 1.11 sub _draw {
741 root 1.10 my ($self) = @_;
742    
743 root 1.59 my $tex = $self->{texture} ||= do {
744     $self->{layout}->set_width ($self->{w});
745 root 1.60 new_from_layout CFClient::Texture $self->{layout};
746 root 1.59 };
747 root 1.10
748 root 1.12 glEnable GL_BLEND;
749 root 1.10 glEnable GL_TEXTURE_2D;
750 root 1.30 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
751 root 1.28 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
752 root 1.10
753 root 1.62 glColor @{$self->{color}};
754 root 1.12
755 root 1.56 $tex->draw_quad (0, 0);
756 root 1.10
757 root 1.12 glDisable GL_BLEND;
758 root 1.10 glDisable GL_TEXTURE_2D;
759     }
760    
761 root 1.39 #############################################################################
762    
763 root 1.60 package CFClient::Widget::Entry;
764 elmex 1.31
765 root 1.60 our @ISA = CFClient::Widget::Label::;
766 elmex 1.31
767     use SDL;
768     use SDL::OpenGL;
769    
770     sub key_down {
771     my ($self, $ev) = @_;
772    
773     my $mod = $ev->key_mod;
774     my $sym = $ev->key_sym;
775    
776     my $uni = $ev->key_unicode;
777    
778     my $text = $self->get_text;
779    
780     if ($sym == SDLK_BACKSPACE) {
781     substr $text, -1, 1, '';
782     } elsif ($uni) {
783     $text .= chr $uni;
784     }
785 root 1.51
786 elmex 1.31 $self->set_text ($text);
787     }
788    
789 root 1.51 sub button_down {
790     my ($self, $ev) = @_;
791    
792     $self->focus_in;
793     }
794    
795 root 1.58 sub mouse_motion {
796     my ($self, $ev, $x, $y) = @_;
797     printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
798     }
799    
800 root 1.51 sub _draw {
801     my ($self) = @_;
802    
803     if ($FOCUS == $self) {
804     glColor 1, 1, 1;
805     } else {
806     glColor 0.7, 0.7, 0.7;
807     }
808    
809     glBegin GL_QUADS;
810     glVertex 0 , 0;
811     glVertex 0 , $self->{h} - 1;
812     glVertex $self->{w} - 1, $self->{h} - 1;
813     glVertex $self->{w} - 1, 0;
814     glEnd;
815    
816     $self->SUPER::_draw;
817     }
818    
819 root 1.39 #############################################################################
820    
821 root 1.60 package CFClient::Widget::MapWidget;
822 root 1.4
823 elmex 1.2 use strict;
824 elmex 1.7
825 root 1.25 use List::Util qw(min max);
826 elmex 1.2
827 root 1.16 use SDL;
828 elmex 1.2 use SDL::OpenGL;
829     use SDL::OpenGL::Constants;
830    
831 root 1.60 our @ISA = CFClient::Widget::;
832 root 1.25
833 root 1.64 sub new {
834     my $class = shift;
835    
836 root 1.65 $class->SUPER::new (
837     z => -1,
838     list => (glGenLists 1),
839     @_
840     )
841 root 1.64 }
842    
843 elmex 1.2 sub key_down {
844     print "MAPKEYDOWN\n";
845     }
846    
847     sub key_up {
848     }
849    
850 elmex 1.36 sub size_request {
851 root 1.52 (
852     1 + int $::WIDTH / 32,
853     1 + int $::HEIGHT / 32,
854     )
855 elmex 1.36 }
856    
857 root 1.65 sub update {
858     my ($self) = @_;
859    
860     $self->{need_update} = 1;
861     }
862    
863 elmex 1.11 sub _draw {
864 root 1.21 my ($self) = @_;
865    
866 root 1.65 if (delete $self->{need_update}) {
867     glNewList $self->{list}, GL_COMPILE;
868 root 1.25
869 root 1.65 my $mx = $::CONN->{mapx};
870     my $my = $::CONN->{mapy};
871 root 1.25
872 root 1.65 my $map = $::CONN->{map};
873 root 1.25
874 root 1.65 my ($xofs, $yofs);
875 root 1.25
876 root 1.65 my $sw = 1 + int $::WIDTH / 32;
877     my $sh = 1 + int $::HEIGHT / 32;
878 root 1.25
879 root 1.65 if ($::CONN->{mapw} > $sw) {
880     $xofs = $mx + ($::CONN->{mapw} - $sw) * 0.5;
881     } else {
882     $xofs = $self->{xofs} = min $mx, max $mx + $::CONN->{mapw} - $sw + 1, $self->{xofs};
883     }
884 root 1.25
885 root 1.65 if ($::CONN->{maph} > $sh) {
886     $yofs = $my + ($::CONN->{maph} - $sh) * 0.5;
887     } else {
888     $yofs = $self->{yofs} = min $my, max $my + $::CONN->{maph} - $sh + 1, $self->{yofs};
889     }
890 root 1.35
891 root 1.65 glEnable GL_TEXTURE_2D;
892     glEnable GL_BLEND;
893     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
894 elmex 1.2
895 root 1.65 my $sw4 = ($sw + 3) & ~3;
896     my $darkness = "\x00" x ($sw4 * $sh);
897 elmex 1.2
898 root 1.65 for my $x (0 .. $sw - 1) {
899     my $row = $map->[$x + $xofs];
900     for my $y (0 .. $sh - 1) {
901    
902     my $cell = $row->[$y + $yofs]
903     or next;
904    
905     my $dark = $cell->[0];
906     if ($dark < 0) {
907     substr $darkness, $y * $sw4 + $x, 1, chr 224;
908     } else {
909     substr $darkness, $y * $sw4 + $x, 1, chr 255 - $dark;
910     }
911    
912     for my $num (grep $_, @$cell[1,2,3]) {
913     my $tex = $::CONN->{face}[$num]{texture} || next;
914    
915     my $w = $tex->{width};
916     my $h = $tex->{height};
917 root 1.19
918 root 1.65 $tex->draw_quad (($x + 1) * 32 - $w, ($y + 1) * 32 - $h, $w, $h);
919     }
920 elmex 1.2 }
921     }
922    
923 root 1.35 # if (1) { # higher quality darkness
924     # $lighting =~ s/(.)/$1$1$1/gs;
925     # my $pb = new_from_data Gtk2::Gdk::Pixbuf $lighting, "rgb", 0, 8, $sw4, $sh, $sw4 * 3;
926     #
927     # $pb = $pb->scale_simple ($sw4 * 0.5, $sh * 0.5, "bilinear");
928     #
929     # $lighting = $pb->get_pixels;
930     # $lighting =~ s/(.)../$1/gs;
931     # }
932    
933 root 1.65 glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
934     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
935    
936     $darkness = new CFClient::Texture
937     width => $sw4,
938     height => $sh,
939     data => $darkness,
940     internalformat => GL_ALPHA,
941     format => GL_ALPHA;
942 root 1.57
943 root 1.65 glColor 0.45, 0.45, 0.45, 1;
944     $darkness->draw_quad (0, 0, $sw4 * 32, $sh * 32);
945 root 1.35
946 root 1.65 glDisable GL_TEXTURE_2D;
947     glDisable GL_BLEND;
948 root 1.35
949 root 1.65 glEndList;
950     }
951    
952     glCallList $self->{list};
953 elmex 1.2 }
954    
955 root 1.16 my %DIR = (
956     SDLK_KP8, [1, "north"],
957 root 1.18 SDLK_KP9, [2, "northeast"],
958 root 1.16 SDLK_KP6, [3, "east"],
959     SDLK_KP3, [4, "southeast"],
960     SDLK_KP2, [5, "south"],
961     SDLK_KP1, [6, "southwest"],
962     SDLK_KP4, [7, "west"],
963     SDLK_KP7, [8, "northwest"],
964 root 1.18
965     SDLK_UP, [1, "north"],
966     SDLK_RIGHT, [3, "east"],
967     SDLK_DOWN, [5, "south"],
968     SDLK_LEFT, [7, "west"],
969 root 1.16 );
970    
971     sub key_down {
972     my ($self, $ev) = @_;
973    
974     my $mod = $ev->key_mod;
975     my $sym = $ev->key_sym;
976    
977     if ($sym == SDLK_KP5) {
978     $::CONN->send ("command stay fire");
979     } elsif (exists $DIR{$sym}) {
980     if ($mod & KMOD_SHIFT) {
981 root 1.18 $self->{shft}++;
982 root 1.16 $::CONN->send ("command fire $DIR{$sym}[0]");
983     } elsif ($mod & KMOD_CTRL) {
984 root 1.18 $self->{ctrl}++;
985 root 1.16 $::CONN->send ("command run $DIR{$sym}[0]");
986     } else {
987 root 1.18 $::CONN->send ("command $DIR{$sym}[1]");
988 root 1.16 }
989     }
990     }
991    
992     sub key_up {
993     my ($self, $ev) = @_;
994    
995     my $mod = $ev->key_mod;
996     my $sym = $ev->key_sym;
997    
998 root 1.18 if (!($mod & KMOD_SHIFT) && delete $self->{shft}) {
999     $::CONN->send ("command fire_stop");
1000     }
1001     if (!($mod & KMOD_CTRL ) && delete $self->{ctrl}) {
1002     $::CONN->send ("command run_stop");
1003 root 1.16 }
1004     }
1005    
1006 root 1.39 #############################################################################
1007    
1008 root 1.60 package CFClient::Widget::Animator;
1009 root 1.35
1010     use SDL::OpenGL;
1011    
1012 root 1.60 our @ISA = CFClient::Widget::Bin::;
1013 root 1.35
1014     sub moveto {
1015     my ($self, $x, $y) = @_;
1016    
1017     $self->{moveto} = [$self->{x}, $self->{y}, $x, $y];
1018 root 1.56 $self->{speed} = 0.001;
1019 root 1.35 $self->{time} = 1;
1020    
1021     ::animation_start $self;
1022     }
1023    
1024     sub animate {
1025     my ($self, $interval) = @_;
1026    
1027     $self->{time} -= $interval * $self->{speed};
1028     if ($self->{time} <= 0) {
1029     $self->{time} = 0;
1030     ::animation_stop $self;
1031     }
1032    
1033     my ($x0, $y0, $x1, $y1) = @{$self->{moveto}};
1034    
1035     $self->{x} = $x0 * $self->{time} + $x1 * (1 - $self->{time});
1036     $self->{y} = $y0 * $self->{time} + $y1 * (1 - $self->{time});
1037     }
1038    
1039     sub _draw {
1040     my ($self) = @_;
1041    
1042     glPushMatrix;
1043 root 1.51 glRotate $self->{time} * 1000, 0, 1, 0;
1044 root 1.38 $self->{children}[0]->draw;
1045 root 1.35 glPopMatrix;
1046     }
1047    
1048 root 1.51 #############################################################################
1049    
1050 root 1.60 package CFClient::Widget::Toplevel;
1051 root 1.51
1052 root 1.60 our @ISA = CFClient::Widget::Container::;
1053 root 1.51
1054     sub size_request {
1055     ($::WIDTH, $::HEIGHT)
1056     }
1057    
1058     sub size_allocate {
1059     my ($self, $w, $h) = @_;
1060    
1061     $self->SUPER::size_allocate ($w, $h);
1062    
1063     $_->size_allocate ($_->size_request)
1064     for @{$self->{children}};
1065     }
1066    
1067 root 1.58 sub translate {
1068     my ($self, $x, $y) = @_;
1069    
1070     ($x, $y)
1071     }
1072    
1073 root 1.51 sub update {
1074     my ($self) = @_;
1075    
1076     $self->size_allocate ($self->size_request);
1077     ::refresh ();
1078     }
1079    
1080     sub add {
1081     my ($self, $widget) = @_;
1082    
1083     $self->SUPER::add ($widget);
1084    
1085     $widget->size_allocate ($widget->size_request);
1086     }
1087    
1088     sub draw {
1089     my ($self) = @_;
1090    
1091     $self->_draw;
1092     }
1093    
1094     #############################################################################
1095    
1096 root 1.60 package CFClient::Widget;
1097 root 1.51
1098 root 1.60 $TOPLEVEL = new CFClient::Widget::Toplevel;
1099 root 1.51
1100     1
1101 root 1.5