ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
(Generate patch)

Comparing deliantra/Deliantra-Client/DC/UI.pm (file contents):
Revision 1.231 by root, Thu May 25 00:26:19 2006 UTC vs.
Revision 1.269 by root, Fri Jun 2 06:22:55 2006 UTC

5 5
6use Scalar::Util (); 6use Scalar::Util ();
7use List::Util (); 7use List::Util ();
8 8
9use CFClient; 9use CFClient;
10use CFClient::Texture;
10 11
11our ($FOCUS, $HOVER, $GRAB); # various widgets 12our ($FOCUS, $HOVER, $GRAB); # various widgets
12 13
14our $LAYOUT;
13our $ROOT; 15our $ROOT;
14our $TOOLTIP; 16our $TOOLTIP;
15our $BUTTON_STATE; 17our $BUTTON_STATE;
16 18
17our %WIDGET; # all widgets, weak-referenced 19our %WIDGET; # all widgets, weak-referenced
18 20
21sub get_layout {
22 my $layout;
23
24 for (grep { $_->{name} } values %WIDGET) {
25 my $win = $layout->{$_->{name}} = { };
26
27 $win->{x} = ($_->{x} + $_->{w} * 0.5) / $::WIDTH if $_->{x} =~ /^[0-9.]+$/;
28 $win->{y} = ($_->{y} + $_->{h} * 0.5) / $::HEIGHT if $_->{y} =~ /^[0-9.]+$/;
29 $win->{w} = $_->{w} / $::WIDTH if defined $_->{w};
30 $win->{h} = $_->{h} / $::HEIGHT if defined $_->{h};
31
32 $win->{show} = $_->{visible} && $_->{is_toplevel};
33 }
34
35 $layout
36}
37
38sub set_layout {
39 my ($layout) = @_;
40
41 $LAYOUT = $layout;
42}
43
19sub check_tooltip { 44sub check_tooltip {
45 return if $ENV{CFPLUS_DEBUG} & 8;
46
20 if (!$GRAB) { 47 if (!$GRAB) {
21 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) { 48 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) {
22 if (length $widget->{tooltip}) { 49 if (length $widget->{tooltip}) {
23
24 if ($TOOLTIP->{owner} != $widget) { 50 if ($TOOLTIP->{owner} != $widget) {
51 $TOOLTIP->hide;
52
25 $TOOLTIP->{owner} = $widget; 53 $TOOLTIP->{owner} = $widget;
26 54
27 my $tip = $widget->{tooltip}; 55 my $tip = $widget->{tooltip};
28 56
29 $tip = $tip->($widget) if CODE:: eq ref $tip; 57 $tip = $tip->($widget) if CODE:: eq ref $tip;
30 58
31 $TOOLTIP->set_tooltip_from ($widget); 59 $TOOLTIP->set_tooltip_from ($widget);
32 $TOOLTIP->show; 60 $TOOLTIP->show;
33
34 my ($x, $y) = $widget->coord2global ($widget->{w}, 0);
35
36 ($x, $y) = $widget->coord2global (-$TOOLTIP->{w}, 0)
37 if $x + $TOOLTIP->{w} > $::WIDTH;
38
39 $TOOLTIP->move ($x, $y);
40 $TOOLTIP->check_size;
41 $TOOLTIP->update;
42 } 61 }
43 62
44 return; 63 return;
45 } 64 }
46 } 65 }
152sub rescale_widgets { 171sub rescale_widgets {
153 my ($sx, $sy) = @_; 172 my ($sx, $sy) = @_;
154 173
155 for my $widget (values %WIDGET) { 174 for my $widget (values %WIDGET) {
156 if ($widget->{is_toplevel}) { 175 if ($widget->{is_toplevel}) {
176 $widget->{x} += int $widget->{w} * 0.5 if $widget->{x} =~ /^[0-9.]+$/;
177 $widget->{y} += int $widget->{h} * 0.5 if $widget->{y} =~ /^[0-9.]+$/;
178
157 $widget->{x} = int 0.5 + $widget->{x} * $sx if exists $widget->{x}; 179 $widget->{x} = int 0.5 + $widget->{x} * $sx if $widget->{x} =~ /^[0-9.]+$/;
158 $widget->{w} = int 0.5 + $widget->{w} * $sx if exists $widget->{w}; 180 $widget->{w} = int 0.5 + $widget->{w} * $sx if exists $widget->{w};
159 $widget->{req_w} = int 0.5 + $widget->{req_w} * $sx if exists $widget->{req_w}; 181 $widget->{force_w} = int 0.5 + $widget->{force_w} * $sx if exists $widget->{force_w};
160 $widget->{user_w} = int 0.5 + $widget->{user_w} * $sx if exists $widget->{user_w};
161 $widget->{y} = int 0.5 + $widget->{y} * $sy if exists $widget->{y}; 182 $widget->{y} = int 0.5 + $widget->{y} * $sy if $widget->{y} =~ /^[0-9.]+$/;
162 $widget->{h} = int 0.5 + $widget->{h} * $sy if exists $widget->{h}; 183 $widget->{h} = int 0.5 + $widget->{h} * $sy if exists $widget->{h};
163 $widget->{req_h} = int 0.5 + $widget->{req_h} * $sy if exists $widget->{req_h}; 184 $widget->{force_h} = int 0.5 + $widget->{force_h} * $sy if exists $widget->{force_h};
164 $widget->{user_h} = int 0.5 + $widget->{user_h} * $sy if exists $widget->{user_h}; 185
186 $widget->{x} -= int $widget->{w} * 0.5 if $widget->{x} =~ /^[0-9.]+$/;
187 $widget->{y} -= int $widget->{h} * 0.5 if $widget->{y} =~ /^[0-9.]+$/;
188
165 } 189 }
166 } 190 }
167 191
168 reconfigure_widgets; 192 reconfigure_widgets;
169} 193}
178 202
179sub new { 203sub new {
180 my $class = shift; 204 my $class = shift;
181 205
182 my $self = bless { 206 my $self = bless {
183 x => 0, 207 x => "center",
184 y => 0, 208 y => "center",
185 z => 0, 209 z => 0,
210 w => undef,
211 h => undef,
186 can_events => 1, 212 can_events => 1,
187 @_ 213 @_
188 }, $class; 214 }, $class;
189 215
216 Scalar::Util::weaken ($CFClient::UI::WIDGET{$self+0} = $self);
217
190 for (keys %$self) { 218 for (keys %$self) {
191 if (/^connect_(.*)$/) { 219 if (/^on_(.*)$/) {
192 $self->connect ($1 => delete $self->{$_}); 220 $self->connect ($1 => delete $self->{$_});
193 } 221 }
194 } 222 }
195 223
196 Scalar::Util::weaken ($CFClient::UI::WIDGET{$self+0} = $self); 224 if (my $layout = $CFClient::UI::LAYOUT->{$self->{name}}) {
225 $self->{x} = $layout->{x} * $CFClient::UI::ROOT->{alloc_w} if exists $layout->{x};
226 $self->{y} = $layout->{y} * $CFClient::UI::ROOT->{alloc_h} if exists $layout->{y};
227 $self->{force_w} = $layout->{w} * $CFClient::UI::ROOT->{alloc_w} if exists $layout->{w};
228 $self->{force_h} = $layout->{h} * $CFClient::UI::ROOT->{alloc_h} if exists $layout->{h};
229
230 $self->{x} -= $self->{force_w} * 0.5 if exists $layout->{x};
231 $self->{y} -= $self->{force_h} * 0.5 if exists $layout->{y};
232
233 $self->show if $layout->{show};
234 }
197 235
198 $self 236 $self
199} 237}
200 238
201sub destroy { 239sub destroy {
205 %$self = (); 243 %$self = ();
206} 244}
207 245
208sub show { 246sub show {
209 my ($self) = @_; 247 my ($self) = @_;
248
210 return if $self->{parent}; 249 return if $self->{parent};
211 250
212 $CFClient::UI::ROOT->add ($self); 251 $CFClient::UI::ROOT->add ($self);
213} 252}
214 253
215sub show_centered { 254sub set_visible {
216 my ($self) = @_; 255 my ($self) = @_;
256
217 return if $self->{parent}; 257 return if $self->{visible};
218 258
219 $self->show; 259 $self->{root} = $self->{parent}{root};
260 $self->{visible} = $self->{parent}{visible} + 1;
220 261
221 $CFClient::UI::ROOT->on_post_alloc ( 262 $self->emit (visibility_change => 1);
222 "centered $self" => sub { 263
223 $self->move (($::WIDTH - $self->{w}) * 0.5, ($::HEIGHT - $self->{h}) * 0.5); 264 $self->realloc if !exists $self->{req_w};
224 }, 265
225 ); 266 $_->set_visible for $self->children;
226} 267}
227 268
228sub set_invisible { 269sub set_invisible {
229 my ($self) = @_; 270 my ($self) = @_;
230 271
231 # broken show/hide model 272 return unless $self->{visible};
232 273
274 $_->set_invisible for $self->children;
275
276 delete $self->{root};
233 delete $self->{visible}; 277 delete $self->{visible};
234 278
235 undef $GRAB if $GRAB == $self; 279 undef $GRAB if $GRAB == $self;
236 undef $HOVER if $HOVER == $self; 280 undef $HOVER if $HOVER == $self;
237 281
282 CFClient::UI::check_tooltip
283 if $TOOLTIP->{owner} == $self;
284
238 $self->focus_out; 285 $self->focus_out;
286
287 $self->emit (visibility_change => 0);
288}
289
290sub set_visibility {
291 my ($self, $visible) = @_;
292
293 return if $self->{visible} == $visible;
294
295 $visible ? $self->hide
296 : $self->show;
297}
298
299sub toggle_visibility {
300 my ($self) = @_;
301
302 $self->{visible}
303 ? $self->hide
304 : $self->show;
239} 305}
240 306
241sub hide { 307sub hide {
242 my ($self) = @_; 308 my ($self) = @_;
243 309
245 311
246 $self->{parent}->remove ($self) 312 $self->{parent}->remove ($self)
247 if $self->{parent}; 313 if $self->{parent};
248} 314}
249 315
250sub move { 316sub move_abs {
251 my ($self, $x, $y, $z) = @_; 317 my ($self, $x, $y, $z) = @_;
252 318
253 $self->{x} = int $x; 319 $self->{x} = List::Util::max 0, int $x;
254 $self->{y} = int $y; 320 $self->{y} = List::Util::max 0, int $y;
255 $self->{z} = $z if defined $z; 321 $self->{z} = $z if defined $z;
256 322
257 $self->update; 323 $self->update;
258} 324}
259 325
260sub set_size { 326sub set_size {
261 my ($self, $w, $h) = @_; 327 my ($self, $w, $h) = @_;
262 328
263 $self->{user_w} = $w; 329 $self->{force_w} = $w;
264 $self->{user_h} = $h; 330 $self->{force_h} = $h;
265 331
266 $self->check_size; 332 $self->realloc;
267} 333}
268 334
269sub size_request { 335sub size_request {
270 require Carp; 336 require Carp;
271 Carp::confess "size_request is abstract"; 337 Carp::confess "size_request is abstract";
273 339
274sub configure { 340sub configure {
275 my ($self, $x, $y, $w, $h) = @_; 341 my ($self, $x, $y, $w, $h) = @_;
276 342
277 if ($self->{aspect}) { 343 if ($self->{aspect}) {
344 my ($ow, $oh) = ($w, $h);
345
278 my $w2 = List::Util::min $w, int $h * $self->{aspect}; 346 $w = List::Util::min $w, int $h * $self->{aspect};
279 my $h2 = List::Util::min $h, int $w / $self->{aspect}; 347 $h = List::Util::min $h, int $w / $self->{aspect};
280 348
281 # use alignment to adjust x, y 349 # use alignment to adjust x, y
282 350
283 $x += int +($w - $w2) * 0.5; 351 $x += int 0.5 * ($ow - $w);
284 $y += int +($h - $h2) * 0.5; 352 $y += int 0.5 * ($oh - $h);
285
286 ($w, $h) = ($w2, $h2);
287 } 353 }
288 354
289 if ($self->{x} != $x || $self->{y} != $y) { 355 if ($self->{x} ne $x || $self->{y} ne $y) {
290 $self->{x} = $x; 356 $self->{x} = $x;
291 $self->{y} = $y; 357 $self->{y} = $y;
292 $self->update; 358 $self->update;
293 } 359 }
294 360
295 if ($self->{w} != $w || $self->{h} != $h) { 361 if ($self->{alloc_w} != $w || $self->{alloc_h} != $h) {
296 $CFClient::UI::ROOT->{size_alloc}{$self} = [$self, $w, $h]; 362 return unless $self->{visible};
363
364 $self->{alloc_w} = $w;
365 $self->{alloc_h} = $h;
366
367 $self->{root}{size_alloc}{$self+0} = $self;
297 } 368 }
298} 369}
299 370
300sub size_allocate { 371sub size_allocate {
301 # nothing to be done 372 # nothing to be done
302} 373}
303 374
304sub reconfigure {
305 my ($self) = @_;
306
307 $self->check_size (1);
308 $self->update;
309}
310
311sub children { 375sub children {
312} 376}
313 377
314sub set_max_size { 378sub set_max_size {
315 my ($self, $w, $h) = @_; 379 my ($self, $w, $h) = @_;
318 delete $self->{max_h}; $self->{max_h} = $h if $h; 382 delete $self->{max_h}; $self->{max_h} = $h if $h;
319} 383}
320 384
321sub set_tooltip { 385sub set_tooltip {
322 my ($self, $tooltip) = @_; 386 my ($self, $tooltip) = @_;
387
388 $tooltip =~ s/^\s+//;
389 $tooltip =~ s/\s+$//;
390
391 return if $self->{tooltip} eq $tooltip;
323 392
324 $self->{tooltip} = $tooltip; 393 $self->{tooltip} = $tooltip;
325 394
326 if ($CFClient::UI::TOOLTIP->{owner} == $self) { 395 if ($CFClient::UI::TOOLTIP->{owner} == $self) {
327 delete $CFClient::UI::TOOLTIP->{owner}; 396 delete $CFClient::UI::TOOLTIP->{owner};
381 my ($self, $ev, $x, $y) = @_; 450 my ($self, $ev, $x, $y) = @_;
382 451
383 $self->focus_in; 452 $self->focus_in;
384} 453}
385 454
386sub w { $_[0]{w} = $_[1] if @_ > 1; $_[0]{w} } 455sub find_widget {
387sub h { $_[0]{h} = $_[1] if @_ > 1; $_[0]{h} } 456 my ($self, $x, $y) = @_;
388sub x { $_[0]{x} = $_[1] if @_ > 1; $_[0]{x} } 457
389sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} } 458 return () unless $self->{can_events};
390sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} } 459
460 return $self
461 if $x >= $self->{x} && $x < $self->{x} + $self->{w}
462 && $y >= $self->{y} && $y < $self->{y} + $self->{h};
463
464 ()
465}
466
467sub set_parent {
468 my ($self, $parent) = @_;
469
470 Scalar::Util::weaken ($self->{parent} = $parent);
471 $self->set_visible if $parent->{visible};
472}
473
474sub connect {
475 my ($self, $signal, $cb) = @_;
476
477 push @{ $self->{signal_cb}{$signal} }, $cb;
478}
479
480sub _emit {
481 my ($self, $signal, @args) = @_;
482
483 List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}
484}
485
486sub emit {
487 my ($self, $signal, @args) = @_;
488
489 $self->_emit ($signal, @args)
490 || $self->$signal (@args);
491}
492
493sub visibility_change {
494 #my ($self, $visible) = @_;
495}
496
497sub realloc {
498 my ($self) = @_;
499
500 if ($self->{visible}) {
501 return if $self->{root}{realloc}{$self+0};
502
503 $self->{root}{realloc}{$self+0} = $self;
504 $self->{root}->update;
505 } else {
506 delete $self->{req_w};
507 delete $self->{req_h};
508 }
509}
510
511sub update {
512 my ($self) = @_;
513
514 $self->{parent}->update
515 if $self->{parent};
516}
517
518sub reconfigure {
519 my ($self) = @_;
520
521 $self->realloc;
522 $self->update;
523}
524
525# using global variables seems a bit hacky, but passing through all drawing
526# functions seems pointless.
527our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn
391 528
392sub draw { 529sub draw {
393 my ($self) = @_; 530 my ($self) = @_;
394 531
395 return unless $self->{h} && $self->{w}; 532 return unless $self->{h} && $self->{w};
533
534 # update screen rectangle
535 local $draw_x = $draw_x + $self->{x};
536 local $draw_y = $draw_y + $self->{y};
537 local $draw_w = $draw_x + $self->{w};
538 local $draw_h = $draw_y + $self->{h};
539
540 # skip widgets that are entirely outside the drawing area
541 return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w)
542 || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h);
396 543
397 glPushMatrix; 544 glPushMatrix;
398 glTranslate $self->{x}, $self->{y}, 0; 545 glTranslate $self->{x}, $self->{y}, 0;
399 $self->_draw; 546 $self->_draw;
400 glPopMatrix; 547 glPopMatrix;
412 glVertex $x , $y + $self->{h}; 559 glVertex $x , $y + $self->{h};
413 glEnd; 560 glEnd;
414 glDisable GL_BLEND; 561 glDisable GL_BLEND;
415 } 562 }
416 563
417 if ($ENV{PCLIENT_DEBUG}) { 564 if ($ENV{CFPLUS_DEBUG} & 1) {
418 glPushMatrix; 565 glPushMatrix;
419 glColor 1, 1, 0, 1; 566 glColor 1, 1, 0, 1;
420 glTranslate $self->{x} + 0.375, $self->{y} + 0.375; 567 glTranslate $self->{x} + 0.375, $self->{y} + 0.375;
421 glBegin GL_LINE_LOOP; 568 glBegin GL_LINE_LOOP;
422 glVertex 0 , 0; 569 glVertex 0 , 0;
423 glVertex $self->{w}, 0; 570 glVertex $self->{w} - 1, 0;
424 glVertex $self->{w}, $self->{h}; 571 glVertex $self->{w} - 1, $self->{h} - 1;
425 glVertex 0 , $self->{h}; 572 glVertex 0 , $self->{h} - 1;
426 glEnd; 573 glEnd;
427 glPopMatrix; 574 glPopMatrix;
428 #CFClient::UI::Label->new (w => $self->{w}, h => $self->{h}, text => $self, fontsize => 0)->_draw; 575 #CFClient::UI::Label->new (w => $self->{w}, h => $self->{h}, text => $self, fontsize => 0)->_draw;
429 } 576 }
430} 577}
431 578
432sub _draw { 579sub _draw {
433 my ($self) = @_; 580 my ($self) = @_;
434 581
435 warn "no draw defined for $self\n"; 582 warn "no draw defined for $self\n";
436}
437
438sub find_widget {
439 my ($self, $x, $y) = @_;
440
441 return () unless $self->{can_events};
442
443 return $self
444 if $x >= $self->{x} && $x < $self->{x} + $self->{w}
445 && $y >= $self->{y} && $y < $self->{y} + $self->{h};
446
447 ()
448}
449
450sub set_parent {
451 my ($self, $parent) = @_;
452
453 Scalar::Util::weaken ($self->{parent} = $parent);
454
455 # TODO: req_w _does_change after ->reconfigure
456 $self->check_size
457 unless exists $self->{req_w};
458
459 $self->show;
460}
461
462sub check_size {
463 my ($self, $forced) = @_;
464
465 $self->{force_alloc} = 1 if $forced;
466 $CFClient::UI::ROOT->{check_size}{$self} = $self;
467}
468
469sub update {
470 my ($self) = @_;
471
472 $self->{parent}->update
473 if $self->{parent};
474}
475
476sub connect {
477 my ($self, $signal, $cb) = @_;
478
479 push @{ $self->{signal_cb}{$signal} }, $cb;
480}
481
482sub _emit {
483 my ($self, $signal, @args) = @_;
484
485 List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}
486}
487
488sub emit {
489 my ($self, $signal, @args) = @_;
490
491 $self->_emit ($signal, @args)
492 || $self->$signal (@args);
493} 583}
494 584
495sub DESTROY { 585sub DESTROY {
496 my ($self) = @_; 586 my ($self) = @_;
497 587
555 my ($class, %arg) = @_; 645 my ($class, %arg) = @_;
556 $class->SUPER::new (can_events => 0, %arg); 646 $class->SUPER::new (can_events => 0, %arg);
557} 647}
558 648
559sub size_request { 649sub size_request {
560 (0, 0) 650 my ($self) = @_;
651
652 ($self->{w} + 0, $self->{h} + 0)
561} 653}
562 654
563sub draw { } 655sub draw { }
564 656
565############################################################################# 657#############################################################################
594 $self->{children} = [ 686 $self->{children} = [
595 sort { $a->{z} <=> $b->{z} } 687 sort { $a->{z} <=> $b->{z} }
596 @{$self->{children}}, @widgets 688 @{$self->{children}}, @widgets
597 ]; 689 ];
598 690
599 $self->check_size (1); 691 $self->realloc;
600 $self->update;
601} 692}
602 693
603sub children { 694sub children {
604 @{ $_[0]{children} } 695 @{ $_[0]{children} }
605} 696}
610 delete $child->{parent}; 701 delete $child->{parent};
611 $child->hide; 702 $child->hide;
612 703
613 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; 704 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ];
614 705
615 $self->check_size; 706 $self->realloc;
616 $self->update;
617} 707}
618 708
619sub clear { 709sub clear {
620 my ($self) = @_; 710 my ($self) = @_;
621 711
625 for (@$children) { 715 for (@$children) {
626 delete $_->{parent}; 716 delete $_->{parent};
627 $_->hide; 717 $_->hide;
628 } 718 }
629 719
630 $self->check_size; 720 $self->realloc;
631 $self->update;
632} 721}
633 722
634sub find_widget { 723sub find_widget {
635 my ($self, $x, $y) = @_; 724 my ($self, $x, $y) = @_;
636 725
723 $self->SUPER::size_allocate ($w, $h); 812 $self->SUPER::size_allocate ($w, $h);
724 $self->update; 813 $self->update;
725} 814}
726 815
727sub _render { 816sub _render {
817 my ($self) = @_;
818
728 $_[0]{children}[0]->draw; 819 $self->{children}[0]->draw;
729} 820}
730 821
731sub render_child { 822sub render_child {
732 my ($self) = @_; 823 my ($self) = @_;
733 824
734 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub { 825 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub {
735 glClearColor 0, 0, 0, 0; 826 glClearColor 0, 0, 0, 0;
736 glClear GL_COLOR_BUFFER_BIT; 827 glClear GL_COLOR_BUFFER_BIT;
737 828
829 {
830 package CFClient::UI::Base;
831
832 ($draw_x, $draw_y, $draw_w, $draw_h) =
833 (0, 0, $self->{w}, $self->{h});
834 }
835
738 $self->_render; 836 $self->_render;
739 }; 837 };
740} 838}
741 839
742sub _draw { 840sub _draw {
743 my ($self) = @_; 841 my ($self) = @_;
744 842
745 my ($w, $h) = ($self->w, $self->h); 843 my ($w, $h) = @$self{qw(w h)};
746 844
747 my $tex = $self->{texture} 845 my $tex = $self->{texture}
748 or return; 846 or return;
749 847
750 glEnable GL_TEXTURE_2D; 848 glEnable GL_TEXTURE_2D;
760 858
761package CFClient::UI::ViewPort; 859package CFClient::UI::ViewPort;
762 860
763our @ISA = CFClient::UI::Window::; 861our @ISA = CFClient::UI::Window::;
764 862
863sub new {
864 my $class = shift;
865
866 $class->SUPER::new (
867 scroll_x => 0,
868 scroll_y => 1,
869 @_,
870 )
871}
872
765sub size_request { 873sub size_request {
766 my ($self) = @_; 874 my ($self) = @_;
767 875
768 @$self{qw(child_w child_h)} = @{$self->child}{qw(req_w req_h)}; 876 my ($w, $h) = @{$self->child}{qw(req_w req_h)};
769 877
770 @$self{qw(child_w child_h)} 878 $w = 10 if $self->{scroll_x};
879 $h = 10 if $self->{scroll_y};
880
881 ($w, $h)
771} 882}
772 883
773sub size_allocate { 884sub size_allocate {
774 my ($self, $w, $h) = @_; 885 my ($self, $w, $h) = @_;
775 886
776 my ($cw, $ch) = @$self{qw(child_w child_h)}; 887 my $child = $self->child;
777# $w = $self->{w}; 888
889 $w = $child->{req_w} if $self->{scroll_x} && $child->{req_w};
890 $h = $child->{req_h} if $self->{scroll_y} && $child->{req_h};
891
778 $self->child->configure (0, 0, $cw, $ch); 892 $self->child->configure (0, 0, $w, $h);
779 $self->update; 893 $self->update;
780} 894}
781 895
782sub set_offset { 896sub set_offset {
783 my ($self, $x, $y) = @_; 897 my ($self, $x, $y) = @_;
817} 931}
818 932
819sub _render { 933sub _render {
820 my ($self) = @_; 934 my ($self) = @_;
821 935
936 local $CFClient::UI::Base::draw_x = $CFClient::UI::Base::draw_x - $self->{view_x};
937 local $CFClient::UI::Base::draw_y = $CFClient::UI::Base::draw_y - $self->{view_y};
938
822 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y}; 939 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y};
823 940
824 $self->SUPER::_render; 941 $self->SUPER::_render;
825} 942}
826 943
834 my $class = shift; 951 my $class = shift;
835 952
836 my $self; 953 my $self;
837 954
838 my $slider = new CFClient::UI::Slider 955 my $slider = new CFClient::UI::Slider
839 vertical => 1, 956 vertical => 1,
840 range => [0, 0, 1, 0.01], # HACK fix 957 range => [0, 0, 1, 0.01], # HACK fix
841 connect_changed => sub { 958 on_changed => sub {
842 $self->{vp}->set_offset (0, $_[1]); 959 $self->{vp}->set_offset (0, $_[1]);
843 }, 960 },
844 ; 961 ;
845 962
846 $self = $class->SUPER::new ( 963 $self = $class->SUPER::new (
852 $self->{vp}->add ($self->{scrolled}); 969 $self->{vp}->add ($self->{scrolled});
853 $self->add ($self->{vp}); 970 $self->add ($self->{vp});
854 $self->add ($self->{slider}); 971 $self->add ($self->{slider});
855 972
856 $self 973 $self
974}
975
976sub update {
977 my ($self) = @_;
978
979 $self->SUPER::update;
980
981 # todo: overwrite size_allocate of child
982 my $child = $self->{vp}->child;
983 $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]);
857} 984}
858 985
859sub size_allocate { 986sub size_allocate {
860 my ($self, $w, $h) = @_; 987 my ($self, $w, $h) = @_;
861 988
914 1041
915our @ISA = CFClient::UI::Bin::; 1042our @ISA = CFClient::UI::Bin::;
916 1043
917use CFClient::OpenGL; 1044use CFClient::OpenGL;
918 1045
919my @tex = 1046my $bg =
1047 new_from_file CFClient::Texture CFClient::find_rcfile "d1_bg.png",
1048 mipmap => 1, wrap => 1;
1049
1050my @border =
920 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 1051 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 }
921 qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); 1052 qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
922 1053
923sub new { 1054sub new {
924 my $class = shift; 1055 my ($class, %arg) = @_;
925 1056
926 # TODO: user_x, user_y, overwrite moveto? 1057 my $title = delete $arg{title};
927 1058
928 my $self = $class->SUPER::new ( 1059 my $self = $class->SUPER::new (
929 bg => [1, 1, 1, 1], 1060 bg => [1, 1, 1, 1],
930 border_bg => [1, 1, 1, 1], 1061 border_bg => [1, 1, 1, 1],
931 border => 0.6, 1062 border => 0.6,
932 is_toplevel => 1,
933 can_events => 1, 1063 can_events => 1,
934 @_ 1064 min_w => 16,
1065 min_h => 16,
1066 %arg,
935 ); 1067 );
936 1068
937 $self->{title} &&= new CFClient::UI::Label 1069 $self->{title} = new CFClient::UI::Label
938 align => 0, 1070 align => 0,
939 valign => 1, 1071 valign => 1,
940 text => $self->{title}, 1072 text => $title,
941 fontsize => $self->{border}; 1073 fontsize => $self->{border}
1074 if defined $title;
942 1075
943 $self 1076 $self
1077}
1078
1079sub add {
1080 my ($self, @widgets) = @_;
1081
1082 $self->SUPER::add (@widgets);
1083 $self->CFClient::UI::Container::add ($self->{title}) if $self->{title};
944} 1084}
945 1085
946sub border { 1086sub border {
947 int $_[0]{border} * $::FONTSIZE 1087 int $_[0]{border} * $::FONTSIZE
948} 1088}
949 1089
950sub size_request { 1090sub size_request {
951 my ($self) = @_; 1091 my ($self) = @_;
1092
1093 $self->{title}->size_request
1094 if $self->{title};
952 1095
953 my ($w, $h) = $self->SUPER::size_request; 1096 my ($w, $h) = $self->SUPER::size_request;
954 1097
955 ( 1098 (
956 $w + $self->border * 2, 1099 $w + $self->border * 2,
959} 1102}
960 1103
961sub size_allocate { 1104sub size_allocate {
962 my ($self, $w, $h) = @_; 1105 my ($self, $w, $h) = @_;
963 1106
1107 if ($self->{title}) {
1108 $self->{title}{w} = $w;
1109 $self->{title}{h} = $h;
1110 $self->{title}->size_allocate ($w, $h);
1111 }
1112
1113 my $border = $self->border;
1114
964 $h -= List::Util::max 0, $self->border * 2; 1115 $h -= List::Util::max 0, $border * 2;
965 $w -= List::Util::max 0, $self->border * 2; 1116 $w -= List::Util::max 0, $border * 2;
966 1117
967 $self->{title}->configure ($self->border, int $self->border - $::FONTSIZE * 2, $w, int $::FONTSIZE * 2)
968 if $self->{title};
969
970 $self->child->configure ($self->border, $self->border, $w, $h); 1118 $self->child->configure ($border, $border, $w, $h);
971} 1119}
972 1120
973sub button_down { 1121sub button_down {
974 my ($self, $ev, $x, $y) = @_; 1122 my ($self, $ev, $x, $y) = @_;
975 1123
991 my ($ev, $x, $y) = @_; 1139 my ($ev, $x, $y) = @_;
992 1140
993 my $dx = $ev->{x} - $ox; 1141 my $dx = $ev->{x} - $ox;
994 my $dy = $ev->{y} - $oy; 1142 my $dy = $ev->{y} - $oy;
995 1143
996 $self->{user_w} = $bw + $dx * ($mx ? -1 : 1); 1144 $self->{force_w} = $bw + $dx * ($mx ? -1 : 1);
997 $self->{user_h} = $bh + $dy * ($my ? -1 : 1); 1145 $self->{force_h} = $bh + $dy * ($my ? -1 : 1);
1146
1147 $self->realloc;
998 $self->move ($wx + $dx * $mx, $wy + $dy * $my); 1148 $self->move_abs ($wx + $dx * $mx, $wy + $dy * $my);
999 $self->check_size;
1000 }; 1149 };
1001 1150
1002 } elsif ($lr ^ $td) { 1151 } elsif ($lr ^ $td) {
1003 my ($ox, $oy) = ($ev->{x}, $ev->{y}); 1152 my ($ox, $oy) = ($ev->{x}, $ev->{y});
1004 my ($bx, $by) = ($self->{x}, $self->{y}); 1153 my ($bx, $by) = ($self->{x}, $self->{y});
1006 $self->{motion} = sub { 1155 $self->{motion} = sub {
1007 my ($ev, $x, $y) = @_; 1156 my ($ev, $x, $y) = @_;
1008 1157
1009 ($x, $y) = ($ev->{x}, $ev->{y}); 1158 ($x, $y) = ($ev->{x}, $ev->{y});
1010 1159
1011 $self->move ($bx + $x - $ox, $by + $y - $oy); 1160 $self->move_abs ($bx + $x - $ox, $by + $y - $oy);
1012 $self->update;
1013 }; 1161 };
1014 } 1162 }
1015} 1163}
1016 1164
1017sub button_up { 1165sub button_up {
1027} 1175}
1028 1176
1029sub _draw { 1177sub _draw {
1030 my ($self) = @_; 1178 my ($self) = @_;
1031 1179
1180 my $child = $self->{children}[0];
1181
1032 my ($w, $h ) = ($self->{w}, $self->{h}); 1182 my ($w, $h ) = ($self->{w}, $self->{h});
1033 my ($cw, $ch) = ($self->child->{w}, $self->child->{h}); 1183 my ($cw, $ch) = ($child->{w}, $child->{h});
1034 1184
1035 glEnable GL_TEXTURE_2D; 1185 glEnable GL_TEXTURE_2D;
1036 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE; 1186 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1037 1187
1038 my $border = $self->border; 1188 my $border = $self->border;
1039 1189
1040 glColor @{ $self->{border_bg} }; 1190 glColor @{ $self->{border_bg} };
1041 $tex[1]->draw_quad_alpha (0, 0, $w, $border); 1191 $border[0]->draw_quad_alpha (0, 0, $w, $border);
1042 $tex[3]->draw_quad_alpha (0, $border, $border, $ch); 1192 $border[1]->draw_quad_alpha (0, $border, $border, $ch);
1043 $tex[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); 1193 $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch);
1044 $tex[4]->draw_quad_alpha (0, $h - $border, $w, $border); 1194 $border[3]->draw_quad_alpha (0, $h - $border, $w, $border);
1045 1195
1046 if (@{$self->{bg}} < 4 || $self->{bg}[3]) { 1196 if (@{$self->{bg}} < 4 || $self->{bg}[3]) {
1047 my $bg = $tex[0]; 1197 glColor @{ $self->{bg} };
1048 1198
1049 # TODO: repeat texture not scale 1199 # TODO: repeat texture not scale
1200 # solve this better(?)
1050 my $rep_x = $cw / $bg->{w}; 1201 $bg->{s} = $cw / $bg->{w};
1051 my $rep_y = $ch / $bg->{h}; 1202 $bg->{t} = $ch / $bg->{h};
1052
1053 glColor @{ $self->{bg} };
1054
1055 $bg->{s} = $rep_x;
1056 $bg->{t} = $rep_y;
1057 $bg->{wrap_mode} = 1;
1058 $bg->draw_quad_alpha ($border, $border, $cw, $ch); 1203 $bg->draw_quad_alpha ($border, $border, $cw, $ch);
1059 } 1204 }
1060 1205
1061 glDisable GL_TEXTURE_2D; 1206 glDisable GL_TEXTURE_2D;
1062 1207
1063 $self->{title}->draw if $self->{title};
1064
1065 $self->child->draw; 1208 $child->draw;
1209
1210 if ($self->{title}) {
1211 glTranslate 0, $border - $self->{h};
1212 $self->{title}->_draw;
1213 }
1066} 1214}
1067 1215
1068############################################################################# 1216#############################################################################
1069 1217
1070package CFClient::UI::Table; 1218package CFClient::UI::Table;
1078sub new { 1226sub new {
1079 my $class = shift; 1227 my $class = shift;
1080 1228
1081 $class->SUPER::new ( 1229 $class->SUPER::new (
1082 col_expand => [], 1230 col_expand => [],
1083 @_ 1231 @_,
1084 ) 1232 )
1233}
1234
1235sub children {
1236 grep $_, map @$_, grep $_, @{ $_[0]{children} }
1085} 1237}
1086 1238
1087sub add { 1239sub add {
1088 my ($self, $x, $y, $child) = @_; 1240 my ($self, $x, $y, $child) = @_;
1089 1241
1090 $child->set_parent ($self); 1242 $child->set_parent ($self);
1091 $self->{children}[$y][$x] = $child; 1243 $self->{children}[$y][$x] = $child;
1092 1244
1093 $child->check_size; 1245 $self->realloc;
1094} 1246}
1095 1247
1096sub children {
1097 grep $_, map @$_, grep $_, @{ $_[0]{children} }
1098}
1099
1100# TODO: move to container class maybe? send childs a signal on removal? 1248# TODO: move to container class maybe? send children a signal on removal?
1101sub clear { 1249sub clear {
1102 my ($self) = @_; 1250 my ($self) = @_;
1103 1251
1104 my @children = $self->children; 1252 my @children = $self->children;
1105 delete $self->{children}; 1253 delete $self->{children};
1107 for (@children) { 1255 for (@children) {
1108 delete $_->{parent}; 1256 delete $_->{parent};
1109 $_->hide; 1257 $_->hide;
1110 } 1258 }
1111 1259
1112 $self->update; 1260 $self->realloc;
1113} 1261}
1114 1262
1115sub get_wh { 1263sub get_wh {
1116 my ($self) = @_; 1264 my ($self) = @_;
1117 1265
1148sub size_allocate { 1296sub size_allocate {
1149 my ($self, $w, $h) = @_; 1297 my ($self, $w, $h) = @_;
1150 1298
1151 my ($ws, $hs) = $self->get_wh; 1299 my ($ws, $hs) = $self->get_wh;
1152 1300
1153 my $req_w = sum @$ws; 1301 my $req_w = (sum @$ws) || 1;
1154 my $req_h = sum @$hs; 1302 my $req_h = (sum @$hs) || 1;
1155 1303
1156 # TODO: nicer code && do row_expand 1304 # TODO: nicer code && do row_expand
1157 my @col_expand = @{$self->{col_expand}}; 1305 my @col_expand = @{$self->{col_expand}};
1158 @col_expand = (1) x @$ws unless @col_expand; 1306 @col_expand = (1) x @$ws unless @col_expand;
1159 my $col_expand = (sum @col_expand) || 1; 1307 my $col_expand = (sum @col_expand) || 1;
1213 } 1361 }
1214} 1362}
1215 1363
1216############################################################################# 1364#############################################################################
1217 1365
1218package CFClient::UI::HBox; 1366package CFClient::UI::Box;
1219
1220# TODO: wrap into common Box base class
1221 1367
1222our @ISA = CFClient::UI::Container::; 1368our @ISA = CFClient::UI::Container::;
1223 1369
1224sub size_request { 1370sub size_request {
1225 my ($self) = @_; 1371 my ($self) = @_;
1226 1372
1227 my @alloc = map [$_->size_request], @{$self->{children}}; 1373 $self->{vertical}
1228 1374 ? (
1229 ( 1375 (List::Util::max map $_->{req_w}, @{$self->{children}}),
1230 (List::Util::sum map $_->[0], @alloc), 1376 (List::Util::sum map $_->{req_h}, @{$self->{children}}),
1231 (List::Util::max map $_->[1], @alloc), 1377 )
1232 ) 1378 : (
1379 (List::Util::sum map $_->{req_w}, @{$self->{children}}),
1380 (List::Util::max map $_->{req_h}, @{$self->{children}}),
1381 )
1233} 1382}
1234 1383
1235sub size_allocate { 1384sub size_allocate {
1236 my ($self, $w, $h) = @_; 1385 my ($self, $w, $h) = @_;
1237 1386
1238 ($h, $w) = ($w, $h); 1387 my $space = $self->{vertical} ? $h : $w;
1239
1240 my $children = $self->{children}; 1388 my $children = $self->{children};
1241 1389
1242 my @h = map $_->{req_w}, @$children; 1390 my @req;
1243 1391
1244 my $req_h = List::Util::sum @h; 1392 if ($self->{homogeneous}) {
1245 1393 @req = ($space / (@$children || 1)) x @$children;
1246 if ($req_h > $h) {
1247 # ah well, not enough space
1248 $_ *= $h / $req_h for @h;
1249 } else { 1394 } else {
1395 @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @$children;
1396 my $req = List::Util::sum @req;
1397
1398 if ($req > $space) {
1399 # ah well, not enough space
1400 $_ *= $space / $req for @req;
1401 } else {
1250 my $exp = List::Util::sum map $_->{expand}, @$children; 1402 my $expand = (List::Util::sum map $_->{expand}, @$children) || 1;
1251 $exp ||= 1;
1252 1403
1404 $space = ($space - $req) / $expand; # remaining space to give away
1405
1406 $req[$_] += $space * $children->[$_]{expand}
1253 for (0 .. $#$children) { 1407 for 0 .. $#$children;
1254 my $child = $children->[$_];
1255
1256 my $alloc_h = $h[$_];
1257 $alloc_h += ($h - $req_h) * $child->{expand} / $exp;
1258 $h[$_] = $alloc_h;
1259 } 1408 }
1260 } 1409 }
1261 1410
1262 CFClient::UI::harmonize \@h; 1411 CFClient::UI::harmonize \@req;
1263 1412
1264 my $y = 0; 1413 my $pos = 0;
1265 for (0 .. $#$children) { 1414 for (0 .. $#$children) {
1266 my $child = $children->[$_];
1267 my $h = $h[$_]; 1415 my $alloc = $req[$_];
1268 $child->configure ($y, 0, $h, $w); 1416 $children->[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h));
1269 1417
1270 $y += $h; 1418 $pos += $alloc;
1271 } 1419 }
1272 1420
1273 1 1421 1
1274} 1422}
1275 1423
1276############################################################################# 1424#############################################################################
1277 1425
1426package CFClient::UI::HBox;
1427
1428our @ISA = CFClient::UI::Box::;
1429
1430sub new {
1431 my $class = shift;
1432
1433 $class->SUPER::new (
1434 vertical => 0,
1435 @_,
1436 )
1437}
1438
1439#############################################################################
1440
1278package CFClient::UI::VBox; 1441package CFClient::UI::VBox;
1279 1442
1280# TODO: wrap into common Box base class
1281
1282our @ISA = CFClient::UI::Container::; 1443our @ISA = CFClient::UI::Box::;
1283 1444
1284sub size_request { 1445sub new {
1285 my ($self) = @_; 1446 my $class = shift;
1286 1447
1287 my @alloc = map [$_->size_request], @{$self->{children}}; 1448 $class->SUPER::new (
1288 1449 vertical => 1,
1289 ( 1450 @_,
1290 (List::Util::max map $_->[0], @alloc),
1291 (List::Util::sum map $_->[1], @alloc),
1292 ) 1451 )
1293}
1294
1295sub size_allocate {
1296 my ($self, $w, $h) = @_;
1297
1298 Carp::confess "negative size" if $w < 0 || $h < 0;#d#
1299
1300 my $children = $self->{children};
1301
1302 my @h = map $_->{req_h}, @$children;
1303
1304 my $req_h = List::Util::sum @h;
1305
1306 if ($req_h > $h) {
1307 # ah well, not enough space
1308 $_ *= $h / $req_h for @h;
1309 } else {
1310 my $exp = List::Util::sum map $_->{expand}, @$children;
1311 $exp ||= 1;
1312
1313 for (0 .. $#$children) {
1314 my $child = $children->[$_];
1315
1316 $h[$_] += ($h - $req_h) * $child->{expand} / $exp;
1317 }
1318 }
1319
1320 CFClient::UI::harmonize \@h;
1321
1322 my $y = 0;
1323 for (0 .. $#$children) {
1324 my $child = $children->[$_];
1325 my $h = $h[$_];
1326 $child->configure (0, $y, $w, $h);
1327
1328 $y += $h;
1329 }
1330
1331 1
1332} 1452}
1333 1453
1334############################################################################# 1454#############################################################################
1335 1455
1336package CFClient::UI::Label; 1456package CFClient::UI::Label;
1353 ellipsise => 3, # end 1473 ellipsise => 3, # end
1354 layout => (new CFClient::Layout), 1474 layout => (new CFClient::Layout),
1355 fontsize => 1, 1475 fontsize => 1,
1356 align => -1, 1476 align => -1,
1357 valign => -1, 1477 valign => -1,
1358 padding => 2, 1478 padding_x => 2,
1479 padding_y => 2,
1359 can_events => 0, 1480 can_events => 0,
1360 %arg 1481 %arg
1361 ); 1482 );
1362 1483
1363 if (exists $self->{template}) { 1484 if (exists $self->{template}) {
1399 $self->{text} = "T$text"; 1520 $self->{text} = "T$text";
1400 1521
1401 $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba; 1522 $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba;
1402 $self->{layout}->set_text ($text); 1523 $self->{layout}->set_text ($text);
1403 1524
1525 $self->realloc;
1404 $self->update; 1526 $self->update;
1405 $self->check_size;
1406} 1527}
1407 1528
1408sub set_markup { 1529sub set_markup {
1409 my ($self, $markup) = @_; 1530 my ($self, $markup) = @_;
1410 1531
1414 my $rgba = $markup =~ /span.*(?:foreground|background)/; 1535 my $rgba = $markup =~ /span.*(?:foreground|background)/;
1415 1536
1416 $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba; 1537 $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba;
1417 $self->{layout}->set_markup ($markup); 1538 $self->{layout}->set_markup ($markup);
1418 1539
1540 $self->realloc;
1419 $self->update; 1541 $self->update;
1420 $self->check_size;
1421} 1542}
1422 1543
1423sub size_request { 1544sub size_request {
1424 my ($self) = @_; 1545 my ($self) = @_;
1425 1546
1439 1560
1440 $w = List::Util::max $w, $w2; 1561 $w = List::Util::max $w, $w2;
1441 $h = List::Util::max $h, $h2; 1562 $h = List::Util::max $h, $h2;
1442 } 1563 }
1443 1564
1444 ( 1565 ($w, $h)
1445 $w + $self->{padding} * 2,
1446 $h + $self->{padding} * 2,
1447 )
1448} 1566}
1449 1567
1450sub size_allocate { 1568sub size_allocate {
1451 my ($self, $w, $h) = @_; 1569 my ($self, $w, $h) = @_;
1452 1570
1571 delete $self->{ox};
1572
1453 delete $self->{texture}; 1573 delete $self->{texture}
1574 unless $w >= $self->{req_w} && $self->{old_w} >= $self->{req_w};
1454} 1575}
1455 1576
1456sub set_fontsize { 1577sub set_fontsize {
1457 my ($self, $fontsize) = @_; 1578 my ($self, $fontsize) = @_;
1458 1579
1459 $self->{fontsize} = $fontsize; 1580 $self->{fontsize} = $fontsize;
1460 delete $self->{texture}; 1581 delete $self->{texture};
1461 1582
1462 $self->update; 1583 $self->realloc;
1463 $self->check_size;
1464} 1584}
1465 1585
1466sub _draw { 1586sub _draw {
1467 my ($self) = @_; 1587 my ($self) = @_;
1468 1588
1474 $self->{layout}->set_width ($self->{w}); 1594 $self->{layout}->set_width ($self->{w});
1475 $self->{layout}->set_ellipsise ($self->{ellipsise}); 1595 $self->{layout}->set_ellipsise ($self->{ellipsise});
1476 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise}); 1596 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise});
1477 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); 1597 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
1478 1598
1479 my $tex = new_from_layout CFClient::Texture $self->{layout}; 1599 new_from_layout CFClient::Texture $self->{layout}
1600 };
1480 1601
1602 unless (exists $self->{ox}) {
1481 $self->{ox} = int ($self->{align} < 0 ? $self->{padding} 1603 $self->{ox} = int ($self->{align} < 0 ? $self->{padding_x}
1482 : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding} 1604 : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding_x}
1483 : ($self->{w} - $tex->{w}) * 0.5); 1605 : ($self->{w} - $tex->{w}) * 0.5);
1484 1606
1485 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding} 1607 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding_y}
1486 : $self->{valign} > 0 ? $self->{h} - $tex->{h} - $self->{padding} 1608 : $self->{valign} > 0 ? $self->{h} - $tex->{h} - $self->{padding_y}
1487 : ($self->{h} - $tex->{h}) * 0.5); 1609 : ($self->{h} - $tex->{h}) * 0.5);
1488
1489 $tex
1490 }; 1610 };
1491 1611
1492 glEnable GL_TEXTURE_2D; 1612 glEnable GL_TEXTURE_2D;
1493 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; 1613 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1494 1614
1548sub set_text { 1668sub set_text {
1549 my ($self, $text) = @_; 1669 my ($self, $text) = @_;
1550 1670
1551 $self->{cursor} = length $text; 1671 $self->{cursor} = length $text;
1552 $self->_set_text ($text); 1672 $self->_set_text ($text);
1553 $self->update; 1673
1554 $self->check_size; 1674 $self->realloc;
1555} 1675}
1556 1676
1557sub get_text { 1677sub get_text {
1558 $_[0]{text} 1678 $_[0]{text}
1559} 1679}
1562 my ($self) = @_; 1682 my ($self) = @_;
1563 1683
1564 my ($w, $h) = $self->SUPER::size_request; 1684 my ($w, $h) = $self->SUPER::size_request;
1565 1685
1566 ($w + 1, $h) # add 1 for cursor 1686 ($w + 1, $h) # add 1 for cursor
1567}
1568
1569sub size_allocate {
1570 my ($self, $w, $h) = @_;
1571
1572 $self->_set_text (delete $self->{text});#d# don't check for == inside _set_text
1573} 1687}
1574 1688
1575sub key_down { 1689sub key_down {
1576 my ($self, $ev) = @_; 1690 my ($self, $ev) = @_;
1577 1691
1598 } elsif ($uni) { 1712 } elsif ($uni) {
1599 substr $text, $self->{cursor}++, 0, chr $uni; 1713 substr $text, $self->{cursor}++, 0, chr $uni;
1600 } 1714 }
1601 1715
1602 $self->_set_text ($text); 1716 $self->_set_text ($text);
1603 $self->update; 1717
1604 $self->check_size; 1718 $self->realloc;
1605} 1719}
1606 1720
1607sub focus_in { 1721sub focus_in {
1608 my ($self) = @_; 1722 my ($self) = @_;
1609 1723
1736 1850
1737sub new { 1851sub new {
1738 my $class = shift; 1852 my $class = shift;
1739 1853
1740 $class->SUPER::new ( 1854 $class->SUPER::new (
1741 padding => 4, 1855 padding_x => 4,
1856 padding_y => 4,
1742 fg => [1, 1, 1], 1857 fg => [1, 1, 1],
1743 active_fg => [0, 0, 1], 1858 active_fg => [0, 0, 1],
1744 can_hover => 1, 1859 can_hover => 1,
1745 align => 0, 1860 align => 0,
1746 valign => 0, 1861 valign => 0,
1793 1908
1794sub new { 1909sub new {
1795 my $class = shift; 1910 my $class = shift;
1796 1911
1797 $class->SUPER::new ( 1912 $class->SUPER::new (
1798 padding => 2, 1913 padding_x => 2,
1914 padding_y => 2,
1799 fg => [1, 1, 1], 1915 fg => [1, 1, 1],
1800 active_fg => [1, 1, 0], 1916 active_fg => [1, 1, 0],
1801 bg => [0, 0, 0, 0.2], 1917 bg => [0, 0, 0, 0.2],
1802 active_bg => [1, 1, 1, 0.5], 1918 active_bg => [1, 1, 1, 0.5],
1803 state => 0, 1919 state => 0,
1807} 1923}
1808 1924
1809sub size_request { 1925sub size_request {
1810 my ($self) = @_; 1926 my ($self) = @_;
1811 1927
1812 ($self->{padding} * 2 + 6) x 2 1928 (6) x 2
1813} 1929}
1814 1930
1815sub button_down { 1931sub button_down {
1816 my ($self, $ev, $x, $y) = @_; 1932 my ($self, $ev, $x, $y) = @_;
1817 1933
1818 if ($x >= $self->{padding} && $x < $self->{w} - $self->{padding} 1934 if ($x >= $self->{padding_x} && $x < $self->{w} - $self->{padding_x}
1819 && $y >= $self->{padding} && $y < $self->{h} - $self->{padding}) { 1935 && $y >= $self->{padding_y} && $y < $self->{h} - $self->{padding_y}) {
1820 $self->{state} = !$self->{state}; 1936 $self->{state} = !$self->{state};
1821 $self->_emit (changed => $self->{state}); 1937 $self->_emit (changed => $self->{state});
1822 } 1938 }
1823} 1939}
1824 1940
1825sub _draw { 1941sub _draw {
1826 my ($self) = @_; 1942 my ($self) = @_;
1827 1943
1828 $self->SUPER::_draw; 1944 $self->SUPER::_draw;
1829 1945
1830 glTranslate $self->{padding} + 0.375, $self->{padding} + 0.375, 0; 1946 glTranslate $self->{padding_x} + 0.375, $self->{padding_y} + 0.375, 0;
1831 1947
1832 my $s = (List::Util::min @$self{qw(w h)}) - $self->{padding} * 2; 1948 my ($w, $h) = @$self{qw(w h)};
1949
1950 my $s = List::Util::min $w - $self->{padding_x} * 2, $h - $self->{padding_y} * 2;
1833 1951
1834 glColor @{ $FOCUS == $self ? $self->{active_fg} : $self->{fg} }; 1952 glColor @{ $FOCUS == $self ? $self->{active_fg} : $self->{fg} };
1835 1953
1836 my $tex = $self->{state} ? $tex[1] : $tex[0]; 1954 my $tex = $self->{state} ? $tex[1] : $tex[0];
1837 1955
2102 fg => [1, 1, 1], 2220 fg => [1, 1, 1],
2103 active_fg => [0, 0, 0], 2221 active_fg => [0, 0, 0],
2104 bg => [0, 0, 0, 0.2], 2222 bg => [0, 0, 0, 0.2],
2105 active_bg => [1, 1, 1, 0.5], 2223 active_bg => [1, 1, 1, 0.5],
2106 range => [0, 0, 100, 10, 0], 2224 range => [0, 0, 100, 10, 0],
2107 req_w => $::WIDTH / 80, 2225 min_w => $::WIDTH / 80,
2108 req_h => $::WIDTH / 80, 2226 min_h => $::WIDTH / 80,
2109 vertical => 0, 2227 vertical => 0,
2110 can_hover => 1, 2228 can_hover => 1,
2111 inner_pad => 0.02, 2229 inner_pad => 0.02,
2112 @_ 2230 @_
2113 ); 2231 );
2116 $self->update; 2234 $self->update;
2117 2235
2118 $self 2236 $self
2119} 2237}
2120 2238
2239sub changed { }
2240
2121sub set_range { 2241sub set_range {
2122 my ($self, $range) = @_; 2242 my ($self, $range) = @_;
2123 2243
2124 $self->{range} = $range; 2244 ($range, $self->{range}) = ($self->{range}, $range);
2125 2245
2126 $self->update; 2246 $self->update
2247 if "@$range" ne "@{$self->{range}}";
2127} 2248}
2128 2249
2129sub set_value { 2250sub set_value {
2130 my ($self, $value) = @_; 2251 my ($self, $value) = @_;
2131 2252
2150} 2271}
2151 2272
2152sub size_request { 2273sub size_request {
2153 my ($self) = @_; 2274 my ($self) = @_;
2154 2275
2155 my $w = $self->{req_w}; 2276 ($self->{req_w}, $self->{req_h})
2156 my $h = $self->{req_h};
2157
2158 $self->{vertical} ? ($h, $w) : ($w, $h)
2159} 2277}
2160 2278
2161sub button_down { 2279sub button_down {
2162 my ($self, $ev, $x, $y) = @_; 2280 my ($self, $ev, $x, $y) = @_;
2163 2281
2514 2632
2515sub new { 2633sub new {
2516 my $class = shift; 2634 my $class = shift;
2517 2635
2518 my $self = $class->SUPER::new ( 2636 my $self = $class->SUPER::new (
2519 state => 0, 2637 state => 0,
2520 connect_activate => \&toggle_flopper, 2638 on_activate => \&toggle_flopper,
2521 @_ 2639 @_
2522 ); 2640 );
2523 2641
2524 if ($self->{state}) {
2525 $self->{state} = 0;
2526 $self->toggle_flopper;
2527 }
2528
2529 $self 2642 $self
2530} 2643}
2531 2644
2532sub toggle_flopper { 2645sub toggle_flopper {
2533 my ($self) = @_; 2646 my ($self) = @_;
2534 2647
2535 # TODO: use animation 2648 $self->{other}->toggle_visibility;
2536 if ($self->{state} = !$self->{state}) {
2537 $CFClient::UI::ROOT->add ($self->{other});
2538 $self->{other}->move ($self->coord2global (0, $self->{h}));
2539 $self->_emit ("open");
2540 } else {
2541 $CFClient::UI::ROOT->remove ($self->{other});
2542 $self->_emit ("close");
2543 }
2544
2545 $self->_emit (changed => $self->{state});
2546} 2649}
2547 2650
2548############################################################################# 2651#############################################################################
2549 2652
2550package CFClient::UI::Tooltip; 2653package CFClient::UI::Tooltip;
2563} 2666}
2564 2667
2565sub set_tooltip_from { 2668sub set_tooltip_from {
2566 my ($self, $widget) = @_; 2669 my ($self, $widget) = @_;
2567 2670
2671 my $tooltip = $widget->{tooltip};
2672
2673 if ($ENV{CFPLUS_DEBUG} & 2) {
2674 $tooltip .= "\n\n" . (ref $widget) . "\n"
2675 . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n"
2676 . "req $widget->{req_w} $widget->{req_h}\n"
2677 . "visible $widget->{visible}";
2678 }
2679
2568 $self->add (new CFClient::UI::Label 2680 $self->add (new CFClient::UI::Label
2569 markup => $widget->{tooltip}, 2681 markup => $tooltip,
2570 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH, 2682 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH,
2571 fontsize => 0.8, 2683 fontsize => 0.8,
2572 fg => [0, 0, 0, 1], 2684 fg => [0, 0, 0, 1],
2573 ellipsise => 0, 2685 ellipsise => 0,
2574 font => ($widget->{tooltip_font} || $::FONT_PROP), 2686 font => ($widget->{tooltip_font} || $::FONT_PROP),
2585 2697
2586sub size_allocate { 2698sub size_allocate {
2587 my ($self, $w, $h) = @_; 2699 my ($self, $w, $h) = @_;
2588 2700
2589 $self->SUPER::size_allocate ($w - 4, $h - 4); 2701 $self->SUPER::size_allocate ($w - 4, $h - 4);
2702}
2703
2704sub visibility_change {
2705 my ($self, $visible) = @_;
2706
2707 return unless $visible;
2708
2709 $self->{root}->on_post_alloc ("move_$self" => sub {
2710 my $widget = $self->{owner}
2711 or return;
2712
2713 my ($x, $y) = $widget->coord2global ($widget->{w}, 0);
2714
2715 ($x, $y) = $widget->coord2global (-$self->{w}, 0)
2716 if $x + $self->{w} > $::WIDTH;
2717
2718 $self->move_abs ($x, $y);
2719 });
2590} 2720}
2591 2721
2592sub _draw { 2722sub _draw {
2593 my ($self) = @_; 2723 my ($self) = @_;
2594 2724
2611 glVertex $w, $h; 2741 glVertex $w, $h;
2612 glVertex $w, 0; 2742 glVertex $w, 0;
2613 glEnd; 2743 glEnd;
2614 2744
2615 glTranslate 2 - 0.375, 2 - 0.375; 2745 glTranslate 2 - 0.375, 2 - 0.375;
2746
2616 $self->SUPER::_draw; 2747 $self->SUPER::_draw;
2617} 2748}
2618 2749
2619############################################################################# 2750#############################################################################
2620 2751
2626 2757
2627sub new { 2758sub new {
2628 my $class = shift; 2759 my $class = shift;
2629 2760
2630 my $self = $class->SUPER::new ( 2761 my $self = $class->SUPER::new (
2631 aspect => 1, 2762 aspect => 1,
2763 can_events => 0,
2632 @_, 2764 @_,
2633 ); 2765 );
2634 2766
2635 if ($self->{anim} && $self->{animspeed}) { 2767 if ($self->{anim} && $self->{animspeed}) {
2636 Scalar::Util::weaken (my $widget = $self); 2768 Scalar::Util::weaken (my $widget = $self);
2695 $self->SUPER::DESTROY; 2827 $self->SUPER::DESTROY;
2696} 2828}
2697 2829
2698############################################################################# 2830#############################################################################
2699 2831
2700package CFClient::UI::InventoryItem;
2701
2702our @ISA = CFClient::UI::HBox::;
2703
2704sub _item_to_desc {
2705 my ($item) = @_;
2706
2707 my $desc =
2708 $item->{nrof} < 2
2709 ? $item->{name}
2710 : "$item->{nrof} × $item->{name_pl}";
2711
2712 $item->{flags} & Crossfire::Protocol::F_OPEN
2713 and $desc .= " (open)";
2714 $item->{flags} & Crossfire::Protocol::F_APPLIED
2715 and $desc .= " (applied)";
2716 $item->{flags} & Crossfire::Protocol::F_UNPAID
2717 and $desc .= " (unpaid)";
2718 $item->{flags} & Crossfire::Protocol::F_MAGIC
2719 and $desc .= " (magic)";
2720 $item->{flags} & Crossfire::Protocol::F_CURSED
2721 and $desc .= " (cursed)";
2722 $item->{flags} & Crossfire::Protocol::F_DAMNED
2723 and $desc .= " (damned)";
2724 $item->{flags} & Crossfire::Protocol::F_LOCKED
2725 and $desc .= " *";
2726
2727 $desc
2728}
2729
2730sub new {
2731 my $class = shift;
2732
2733 my %args = @_;
2734
2735 my $item = delete $args{item};
2736
2737 my $desc = _item_to_desc ($item);
2738
2739 my $self = $class->SUPER::new (
2740 can_hover => 1,
2741 can_events => 1,
2742 tooltip => ((CFClient::UI::Label::escape $desc)
2743 . "\n<small>leftclick - examine\nshift+leftclick - move/pickup/drop\nmiddle click - apply\nrightclick - menu</small>"),
2744 connect_button_down => sub {
2745 my ($self, $ev, $x, $y) = @_;
2746
2747 # todo: maybe put examine on 1? but should just be a tooltip :(
2748 if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) {
2749 my $targ = $::CONN->{player}{tag};
2750
2751 if ($item->{container} == $::CONN->{player}{tag}) {
2752 $targ = $main::OPENCONT;
2753 }
2754
2755 $::CONN->send ("move $targ $item->{tag} 0");
2756 } elsif ($ev->{button} == 1) {
2757 $::CONN->send ("examine $item->{tag}");
2758 } elsif ($ev->{button} == 2) {
2759 $::CONN->send ("apply $item->{tag}");
2760 } elsif ($ev->{button} == 3) {
2761 my @menu_items = (
2762 ["examine", sub { $::CONN->send ("examine $item->{tag}") }],
2763 ["mark", sub { $::CONN->send ("mark ". pack "N", $item->{tag}) }],
2764 ["apply", sub { $::CONN->send ("apply $item->{tag}") }],
2765 (
2766 $item->{flags} & Crossfire::Protocol::F_LOCKED
2767 ? (
2768 ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $item->{tag}) }],
2769 )
2770 : (
2771 ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $item->{tag}) }],
2772 ["drop", sub { $::CONN->send ("move $main::OPENCONT $item->{tag} 0") }],
2773 )
2774 ),
2775 );
2776
2777 CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev);
2778 }
2779
2780 1
2781 },
2782 %args
2783 );
2784
2785
2786 $self->add (new CFClient::UI::Face
2787 can_events => 0,
2788 face => $item->{face},
2789 anim => $item->{anim},
2790 animspeed => $item->{animspeed},
2791 );
2792
2793 $self->add ($self->{name_lbl} = new CFClient::UI::Label can_events => 0);
2794
2795 $self->{item} = $item;
2796
2797 $self->update_item;
2798
2799 $self
2800}
2801
2802sub update_item {
2803 my ($self) = @_;
2804
2805 my $desc = _item_to_desc ($self->{item});
2806
2807 $self->{name_lbl}->set_text ($desc);
2808}
2809
2810#############################################################################
2811
2812package CFClient::UI::Inventory;
2813
2814our @ISA = CFClient::UI::ScrolledWindow::;
2815
2816sub new {
2817 my $class = shift;
2818
2819 my $self = $class->SUPER::new (
2820 scrolled => (new CFClient::UI::Table),
2821 @_,
2822 );
2823
2824 $self
2825}
2826
2827sub set_items {
2828 my ($self, $items) = @_;
2829
2830 $self->{scrolled}->clear;
2831 return unless $items;
2832
2833 my @items = sort {
2834 ($a->{type} <=> $b->{type})
2835 or ($a->{name} cmp $b->{name})
2836 } @$items;
2837
2838 $self->{real_items} = \@items;
2839
2840 for my $item (@items) {
2841 $item->{item} = $item;
2842 $item = $item->{widget} ||= new CFClient::UI::InventoryItem item => $item;
2843 $item->update_item ();
2844 }
2845
2846 my $i = 0;
2847 for (@items) {
2848 $self->{scrolled}->add (0, $i, $_);
2849 my $nrof = $_->{item}->{nrof} || 1;
2850 $self->{scrolled}->add (1, $i++, new CFClient::UI::Label text => ($_->{item}->{weight} * $nrof) / 1000);
2851 }
2852
2853# $range->{range} = [$self->{pos}, 0, $self->{max_pos}, $page];
2854}
2855
2856sub size_request {
2857 my ($self) = @_;
2858 ($self->{req_w}, $self->{req_h});
2859}
2860
2861#############################################################################
2862
2863package CFClient::UI::Menu; 2832package CFClient::UI::Menu;
2864 2833
2865our @ISA = CFClient::UI::FancyFrame::; 2834our @ISA = CFClient::UI::FancyFrame::;
2866 2835
2867use CFClient::OpenGL; 2836use CFClient::OpenGL;
2905 # maybe save $GRAB? must be careful about events... 2874 # maybe save $GRAB? must be careful about events...
2906 $GRAB = $self; 2875 $GRAB = $self;
2907 $self->{button} = $ev->{button}; 2876 $self->{button} = $ev->{button};
2908 2877
2909 $self->show; 2878 $self->show;
2910 $self->move ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5); 2879 $self->move_abs ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5);
2911} 2880}
2912 2881
2913sub mouse_motion { 2882sub mouse_motion {
2914 my ($self, $ev, $x, $y) = @_; 2883 my ($self, $ev, $x, $y) = @_;
2915 2884
2995sub add { 2964sub add {
2996 my ($self, $text, %arg) = @_; 2965 my ($self, $text, %arg) = @_;
2997 2966
2998 $text =~ s/^\s+//; 2967 $text =~ s/^\s+//;
2999 $text =~ s/\s+$//; 2968 $text =~ s/\s+$//;
2969
2970 return unless $text;
3000 2971
3001 my $timeout = time + ((delete $arg{timeout}) || 60); 2972 my $timeout = time + ((delete $arg{timeout}) || 60);
3002 2973
3003 my $group = exists $arg{group} ? $arg{group} : ++$self->{id}; 2974 my $group = exists $arg{group} ? $arg{group} : ++$self->{id};
3004 2975
3038 $self->SUPER::reconfigure; 3009 $self->SUPER::reconfigure;
3039} 3010}
3040 3011
3041############################################################################# 3012#############################################################################
3042 3013
3043package CFClient::UI::Root; 3014package CFClient::UI::Inventory;
3044 3015
3045our @ISA = CFClient::UI::Container::; 3016our @ISA = CFClient::UI::ScrolledWindow::;
3046
3047use CFClient::OpenGL;
3048 3017
3049sub new { 3018sub new {
3050 my $class = shift; 3019 my $class = shift;
3051 3020
3052 $class->SUPER::new ( 3021 my $self = $class->SUPER::new (
3022 scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]),
3053 @_, 3023 @_,
3054 ) 3024 );
3055}
3056 3025
3057sub configure { 3026 $self
3027}
3028
3029sub set_items {
3030 my ($self, $items) = @_;
3031
3032 $self->{scrolled}->clear;
3033 return unless $items;
3034
3035 my @items = sort {
3036 ($a->{type} <=> $b->{type})
3037 or ($a->{name} cmp $b->{name})
3038 } @$items;
3039
3040 $self->{real_items} = \@items;
3041
3042 my $row = 0;
3043 for my $item (@items) {
3044 CFClient::Item::update_widgets $item;
3045
3046 $self->{scrolled}->add (0, $row, $item->{face_widget});
3047 $self->{scrolled}->add (1, $row, $item->{desc_widget});
3048 $self->{scrolled}->add (2, $row, $item->{weight_widget});
3049
3050 $row++;
3051 }
3052}
3053
3054#############################################################################
3055
3056package CFClient::UI::BindEditor;
3057
3058our @ISA = CFClient::UI::FancyFrame::;
3059
3060sub new {
3061 my $class = shift;
3062
3063 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3064
3065 $self->add (my $vb = new CFClient::UI::VBox);
3066
3067
3068 $vb->add ($self->{rec_btn} = new CFClient::UI::Button
3069 text => "start recording",
3070 tooltip => "Start/Stops recording of actions."
3071 ."All subsequent actions after the recording started will be captured."
3072 ."The actions are displayed after the record was stopped."
3073 ."To bind the action you have to click on the 'Bind' button",
3074 on_activate => sub {
3075 unless ($self->{recording}) {
3076 $self->start;
3077 } else {
3078 $self->stop;
3079 }
3080 });
3081
3082 $vb->add (new CFClient::UI::Label text => "Actions:");
3083 $vb->add ($self->{cmdbox} = new CFClient::UI::VBox);
3084
3085 $vb->add (new CFClient::UI::Label text => "Bound to: ");
3086 $vb->add (my $hb = new CFClient::UI::HBox);
3087 $hb->add ($self->{keylbl} = new CFClient::UI::Label expand => 1);
3088 $hb->add (new CFClient::UI::Button
3089 text => "bind",
3090 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
3091 on_activate => sub {
3092 $self->ask_for_bind;
3093 });
3094
3095 $vb->add (my $hb = new CFClient::UI::HBox);
3096 $hb->add (new CFClient::UI::Button
3097 text => "ok",
3098 expand => 1,
3099 tooltip => "This closes the binding editor and saves the binding",
3100 on_activate => sub {
3101 $self->hide;
3102 $self->commit;
3103 });
3104
3105 $hb->add (new CFClient::UI::Button
3106 text => "cancel",
3107 expand => 1,
3108 tooltip => "This closes the binding editor without saving",
3109 on_activate => sub {
3110 $self->hide;
3111 $self->{binding_cancel}->()
3112 if $self->{binding_cancel};
3113 });
3114
3115 $self->update_binding_widgets;
3116
3117 $self
3118}
3119
3120sub commit {
3121 my ($self) = @_;
3122 my ($mod, $sym, $cmds) = $self->get_binding;
3123 if ($sym != 0 && @$cmds > 0) {
3124 $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym)
3125 ."'. Don't forget 'Save Config'!");
3126 $self->{binding_change}->($mod, $sym, $cmds)
3127 if $self->{binding_change};
3128 } else {
3129 $::STATUSBOX->add ("No action bound, no key or action specified!");
3130 $self->{binding_cancel}->()
3131 if $self->{binding_cancel};
3132 }
3133}
3134
3135sub start {
3136 my ($self) = @_;
3137
3138 $self->{rec_btn}->set_text ("stop recording");
3139 $self->{recording} = 1;
3140 $self->clear_command_list;
3141 $::CONN->start_record if $::CONN;
3142}
3143
3144sub stop {
3145 my ($self) = @_;
3146
3147 $self->{rec_btn}->set_text ("start recording");
3148 $self->{recording} = 0;
3149
3150 my $rec;
3151 $rec = $::CONN->stop_record if $::CONN;
3152 return unless ref $rec eq 'ARRAY';
3153 $self->set_command_list ($rec);
3154}
3155
3156# if $commit is true, the binding will be set after the user entered a key combo
3157sub ask_for_bind {
3158 my ($self, $commit) = @_;
3159
3160 CFClient::Binder::open_binding_dialog (sub {
3161 my ($mod, $sym) = @_;
3162 $self->{binding} = [$mod, $sym]; # XXX: how to stop that memleak?
3163 $self->update_binding_widgets;
3164 $self->commit if $commit;
3165 });
3166}
3167
3168# $mod and $sym are the modifiers and key symbol
3169# $cmds is a array ref of strings (the commands)
3170# $cb is the callback that is executed on OK
3171# $ccb is the callback that is executed on CANCEL and
3172# when the binding was unsuccessful on OK
3173sub set_binding {
3058 my ($self, $x, $y, $w, $h) = @_; 3174 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
3059 3175
3060 $self->{w} = $w; 3176 $self->clear_command_list;
3061 $self->{h} = $h; 3177 $self->{recording} = 0;
3062} 3178 $self->{rec_btn}->set_text ("start recording");
3063 3179
3064sub check_size { 3180 $self->{binding} = [$mod, $sym];
3181 $self->{commands} = $cmds;
3182
3183 $self->{binding_change} = $cb;
3184 $self->{binding_cancel} = $ccb;
3185
3186 $self->update_binding_widgets;
3187}
3188
3189# this is a shortcut method that asks for a binding
3190# and then just binds it.
3191sub do_quick_binding {
3065 my ($self) = @_; 3192 my ($self, $cmds) = @_;
3193 $self->set_binding (undef, undef, $cmds, sub {
3194 $::CFG->{bindings}->{$_[0]}->{$_[1]} = $_[2];
3195 });
3196 $self->ask_for_bind (1);
3197}
3066 3198
3067 $self->size_allocate ($self->{w}, $self->{h}) 3199sub update_binding_widgets {
3068 if $self->{w}; 3200 my ($self) = @_;
3201 my ($mod, $sym, $cmds) = $self->get_binding;
3202 $self->{keylbl}->set_text (CFClient::Binder::keycombo_to_name ($mod, $sym));
3203 $self->set_command_list ($cmds);
3204}
3205
3206sub get_binding {
3207 my ($self) = @_;
3208 return (
3209 $self->{binding}->[0],
3210 $self->{binding}->[1],
3211 [ grep { defined $_ } @{$self->{commands}} ]
3212 );
3213}
3214
3215sub clear_command_list {
3216 my ($self) = @_;
3217 $self->{cmdbox}->clear ();
3218}
3219
3220sub set_command_list {
3221 my ($self, $cmds) = @_;
3222
3223 $self->{cmdbox}->clear ();
3224 $self->{commands} = $cmds;
3225
3226 my $idx = 0;
3227
3228 for (@$cmds) {
3229 $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox);
3230
3231 my $i = $idx;
3232 $hb->add (new CFClient::UI::Label text => $_);
3233 $hb->add (new CFClient::UI::Button
3234 text => "delete",
3235 tooltip => "Deletes the action from the record",
3236 on_activate => sub {
3237 $self->{cmdbox}->remove ($hb);
3238 $cmds->[$i] = undef;
3239 });
3240
3241
3242 $idx++
3243 }
3244}
3245
3246#############################################################################
3247
3248package CFClient::UI::SpellList;
3249
3250our @ISA = CFClient::UI::FancyFrame::;
3251
3252sub new {
3253 my $class = shift;
3254
3255 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3256
3257 $self->add (new CFClient::UI::ScrolledWindow
3258 scrolled => $self->{spellbox} = new CFClient::UI::Table);
3259
3260 $self;
3261}
3262
3263# XXX: Do sorting? Argl...
3264sub add_spell {
3265 my ($self, $spell) = @_;
3266 $self->{spells}->{$spell->{name}} = $spell;
3267
3268 $self->{spellbox}->add (0, $self->{tbl_idx}, new CFClient::UI::Face
3269 face => $spell->{face},
3270 can_hover => 1,
3271 can_events => 1,
3272 tooltip => $spell->{message});
3273
3274 $self->{spellbox}->add (1, $self->{tbl_idx}, new CFClient::UI::Label
3275 text => $spell->{name},
3276 can_hover => 1,
3277 can_events => 1,
3278 tooltip => $spell->{message},
3279 expand => 1);
3280
3281 $self->{spellbox}->add (2, $self->{tbl_idx}, new CFClient::UI::Label
3282 text => (sprintf "lvl: %2d sp: %2d dmg: %2d",
3283 $spell->{level}, ($spell->{mana} || $spell->{grace}), $spell->{damage}),
3284 expand => 1);
3285
3286 $self->{spellbox}->add (3, $self->{tbl_idx}++, new CFClient::UI::Button
3287 text => "bind to key",
3288 on_activate => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) });
3289}
3290
3291sub rebuild_spell_list {
3292 my ($self) = @_;
3293 $self->{tbl_idx} = 0;
3294 $self->add_spell ($_) for values %{$self->{spells}};
3295}
3296
3297sub remove_spell {
3298 my ($self, $spell) = @_;
3299 delete $self->{spells}->{$spell->{name}};
3300 $self->rebuild_spell_list;
3301}
3302
3303#############################################################################
3304
3305package CFClient::UI::Root;
3306
3307our @ISA = CFClient::UI::Container::;
3308
3309use CFClient::OpenGL;
3310
3311sub new {
3312 my $class = shift;
3313
3314 my $self = $class->SUPER::new (
3315 visible => 1,
3316 @_,
3317 );
3318
3319 Scalar::Util::weaken ($self->{root} = $self);
3320
3321 $self
3069} 3322}
3070 3323
3071sub size_request { 3324sub size_request {
3072 my ($self) = @_; 3325 my ($self) = @_;
3073 3326
3074 ($self->{w}, $self->{h}) 3327 ($self->{w}, $self->{h})
3328}
3329
3330sub _to_pixel {
3331 my ($coord, $size, $max) = @_;
3332
3333 $coord =
3334 $coord eq "center" ? ($max - $size) * 0.5
3335 : $coord eq "max" ? $max
3336 : $coord;
3337
3338 $coord = 0 if $coord < 0;
3339 $coord = $max - $size if $coord > $max - $size;
3340
3341 int $coord + 0.5
3075} 3342}
3076 3343
3077sub size_allocate { 3344sub size_allocate {
3078 my ($self, $w, $h) = @_; 3345 my ($self, $w, $h) = @_;
3079 3346
3080 for my $child ($self->children) { 3347 for my $child ($self->children) {
3081 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)}; 3348 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)};
3082 3349
3083 $X = $child->{req_x} > 0 ? $child->{req_x} : $w - $W - $child->{req_x} + 1 3350 $X = $child->{force_x} if exists $child->{force_x};
3084 if exists $child->{req_x}; 3351 $Y = $child->{force_y} if exists $child->{force_y};
3085 3352
3086 $Y = $child->{req_y} > 0 ? $child->{req_y} : $h - $H - $child->{req_y} + 1 3353 $X = _to_pixel $X, $W, $self->{w};
3087 if exists $child->{req_y}; 3354 $Y = _to_pixel $Y, $H, $self->{h};
3088
3089 $X = List::Util::max 0, List::Util::min $w - $W, int $X + 0.5;
3090 $Y = List::Util::max 0, List::Util::min $h - $H, int $Y + 0.5;
3091 3355
3092 $child->configure ($X, $Y, $W, $H); 3356 $child->configure ($X, $Y, $W, $H);
3093 } 3357 }
3094} 3358}
3095 3359
3106} 3370}
3107 3371
3108sub update { 3372sub update {
3109 my ($self) = @_; 3373 my ($self) = @_;
3110 3374
3111 $self->check_size;
3112 $::WANT_REFRESH++; 3375 $::WANT_REFRESH++;
3113} 3376}
3114 3377
3115sub add { 3378sub add {
3116 my ($self, @children) = @_; 3379 my ($self, @children) = @_;
3117 3380
3118 for (my @widgets = @children; my $w = pop @widgets; ) {
3119 push @widgets, $w->children;
3120 $w->{root} = $self;
3121 $w->{visible} = 1;
3122 }
3123
3124 for my $child (@children) {
3125 $child->{is_toplevel} = 1; 3381 $_->{is_toplevel} = 1
3126 3382 for @children;
3127 # integerise window positions
3128 $child->{x} = int $child->{x};
3129 $child->{y} = int $child->{y};
3130 }
3131 3383
3132 $self->SUPER::add (@children); 3384 $self->SUPER::add (@children);
3133} 3385}
3134 3386
3135sub remove { 3387sub remove {
3136 my ($self, @children) = @_; 3388 my ($self, @children) = @_;
3137 3389
3138 $self->SUPER::remove (@children); 3390 $self->SUPER::remove (@children);
3391
3392 delete $self->{is_toplevel}
3393 for @children;
3139 3394
3140 while (@children) { 3395 while (@children) {
3141 my $w = pop @children; 3396 my $w = pop @children;
3142 push @children, $w->children; 3397 push @children, $w->children;
3143 $w->set_invisible; 3398 $w->set_invisible;
3162 while ($self->{refresh_hook}) { 3417 while ($self->{refresh_hook}) {
3163 $_->() 3418 $_->()
3164 for values %{delete $self->{refresh_hook}}; 3419 for values %{delete $self->{refresh_hook}};
3165 } 3420 }
3166 3421
3167 if ($self->{check_size}) { 3422 if ($self->{realloc}) {
3168 my @queue = ([], []); 3423 my %queue;
3424 my @queue;
3425 my $widget;
3169 3426
3170 for (;;) { 3427 outer:
3171 if ($self->{check_size}) { 3428 while () {
3172 # heuristic: check containers last 3429 if (my $realloc = delete $self->{realloc}) {
3173 push @{ $queue[ ! ! $_->isa ("CFClient::UI::Container") ] }, $_ 3430 for $widget (values %$realloc) {
3174 for values %{delete $self->{check_size}} 3431 $widget->{visible} or next; # do not resize invisible widgets
3432
3433 $queue{$widget+0}++ and next; # duplicates are common
3434
3435 push @{ $queue[$widget->{visible}] }, $widget;
3436 }
3175 } 3437 }
3176 3438
3177 my $widget = (pop @{ $queue[0] }) || (pop @{ $queue[1] }) || last; 3439 while () {
3440 @queue or last outer;
3178 3441
3179 my ($w, $h) = $widget->{user_w} && $widget->{user_h} 3442 $widget = pop @{ $queue[-1] || [] }
3180 ? @$widget{qw(user_w user_h)} 3443 and last;
3181 : $widget->size_request;
3182
3183 if (delete $widget->{force_alloc}
3184 or $w != $widget->{req_w} or $h != $widget->{req_h}) {
3185 Carp::confess "$widget: size_request is negative" if $w < 0 || $h < 0;#d#
3186 3444
3445 pop @queue;
3446 }
3447
3448 delete $queue{$widget+0};
3449
3450 my ($w, $h) = $widget->size_request;
3451
3452 $w = List::Util::max $widget->{min_w}, $w + $widget->{padding_x} * 2;
3453 $h = List::Util::max $widget->{min_h}, $h + $widget->{padding_y} * 2;
3454
3455 $w = $widget->{force_w} if exists $widget->{force_w};
3456 $h = $widget->{force_h} if exists $widget->{force_h};
3457
3458 if ($widget->{req_w} != $w || $widget->{req_h} != $h
3459 || delete $widget->{force_realloc}) {
3187 $widget->{req_w} = $w; 3460 $widget->{req_w} = $w;
3188 $widget->{req_h} = $h; 3461 $widget->{req_h} = $h;
3189 3462
3190 $self->{size_alloc}{$widget} = [$widget, $widget->{w} || $w, $widget->{h} || $h]; 3463 $self->{size_alloc}{$widget+0} = $widget;
3191 3464
3192 $widget->{parent}->check_size
3193 if $widget->{parent}; 3465 if (my $parent = $widget->{parent}) {
3466 $self->{realloc}{$parent+0} = $parent
3467 unless $queue{$parent+0};
3468
3469 $parent->{force_size_alloc} = 1;
3470 $self->{size_alloc}{$parent+0} = $parent;
3471 }
3194 } 3472 }
3473
3474 delete $self->{realloc}{$widget+0};
3195 } 3475 }
3196 } 3476 }
3197 3477
3198 while ($self->{size_alloc}) { 3478 while (my $size_alloc = delete $self->{size_alloc}) {
3199 for (values %{delete $self->{size_alloc}}) { 3479 my @queue = sort { $b->{visible} <=> $a->{visible} }
3200 my ($widget, $w, $h) = @$_; 3480 values %$size_alloc;
3481
3482 while () {
3483 my $widget = pop @queue || last;
3484
3485 my ($w, $h) = @$widget{qw(alloc_w alloc_h)};
3201 3486
3202 $w = 0 if $w < 0; 3487 $w = 0 if $w < 0;
3203 $h = 0 if $h < 0; 3488 $h = 0 if $h < 0;
3204 3489
3490 $w = int $w + 0.5;
3491 $h = int $h + 0.5;
3492
3493 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) {
3494 $widget->{old_w} = $widget->{w};
3495 $widget->{old_h} = $widget->{h};
3496
3205 $widget->{w} = $w; 3497 $widget->{w} = $w;
3206 $widget->{h} = $h; 3498 $widget->{h} = $h;
3499
3207 $widget->emit (size_allocate => $w, $h); 3500 $widget->emit (size_allocate => $w, $h);
3501 }
3208 } 3502 }
3209 } 3503 }
3210 3504
3211 while ($self->{post_alloc_hook}) { 3505 while ($self->{post_alloc_hook}) {
3212 $_->() 3506 $_->()
3213 for values %{delete $self->{post_alloc_hook}}; 3507 for values %{delete $self->{post_alloc_hook}};
3214 } 3508 }
3509
3215 3510
3216 glViewport 0, 0, $::WIDTH, $::HEIGHT; 3511 glViewport 0, 0, $::WIDTH, $::HEIGHT;
3217 glClearColor +($::CFG->{fow_intensity}) x 3, 1; 3512 glClearColor +($::CFG->{fow_intensity}) x 3, 1;
3218 glClear GL_COLOR_BUFFER_BIT; 3513 glClear GL_COLOR_BUFFER_BIT;
3219 3514
3221 glLoadIdentity; 3516 glLoadIdentity;
3222 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000; 3517 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000;
3223 glMatrixMode GL_MODELVIEW; 3518 glMatrixMode GL_MODELVIEW;
3224 glLoadIdentity; 3519 glLoadIdentity;
3225 3520
3521 {
3522 package CFClient::UI::Base;
3523
3524 ($draw_x, $draw_y, $draw_w, $draw_h) =
3525 (0, 0, $self->{w}, $self->{h});
3526 }
3527
3226 $self->_draw; 3528 $self->_draw;
3227} 3529}
3228 3530
3229############################################################################# 3531#############################################################################
3230 3532

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines