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.239 by root, Thu May 25 22:29:57 2006 UTC vs.
Revision 1.271 by root, Fri Jun 2 22:13:47 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
238 CFClient::UI::check_tooltip 282 CFClient::UI::check_tooltip
239 if $CFClient::UI::TOOLTIP->{owner} == $self; 283 if $TOOLTIP->{owner} == $self;
240 284
241 $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;
242} 305}
243 306
244sub hide { 307sub hide {
245 my ($self) = @_; 308 my ($self) = @_;
246 309
248 311
249 $self->{parent}->remove ($self) 312 $self->{parent}->remove ($self)
250 if $self->{parent}; 313 if $self->{parent};
251} 314}
252 315
253sub move { 316sub move_abs {
254 my ($self, $x, $y, $z) = @_; 317 my ($self, $x, $y, $z) = @_;
255 318
256 $self->{x} = int $x; 319 $self->{x} = List::Util::max 0, int $x;
257 $self->{y} = int $y; 320 $self->{y} = List::Util::max 0, int $y;
258 $self->{z} = $z if defined $z; 321 $self->{z} = $z if defined $z;
259 322
260 $self->update; 323 $self->update;
261} 324}
262 325
263sub set_size { 326sub set_size {
264 my ($self, $w, $h) = @_; 327 my ($self, $w, $h) = @_;
265 328
266 $self->{user_w} = $w; 329 $self->{force_w} = $w;
267 $self->{user_h} = $h; 330 $self->{force_h} = $h;
268 331
269 $self->check_size; 332 $self->realloc;
270} 333}
271 334
272sub size_request { 335sub size_request {
273 require Carp; 336 require Carp;
274 Carp::confess "size_request is abstract"; 337 Carp::confess "size_request is abstract";
276 339
277sub configure { 340sub configure {
278 my ($self, $x, $y, $w, $h) = @_; 341 my ($self, $x, $y, $w, $h) = @_;
279 342
280 if ($self->{aspect}) { 343 if ($self->{aspect}) {
344 my ($ow, $oh) = ($w, $h);
345
281 my $w2 = List::Util::min $w, int $h * $self->{aspect}; 346 $w = List::Util::min $w, int $h * $self->{aspect};
282 my $h2 = List::Util::min $h, int $w / $self->{aspect}; 347 $h = List::Util::min $h, int $w / $self->{aspect};
283 348
284 # use alignment to adjust x, y 349 # use alignment to adjust x, y
285 350
286 $x += int +($w - $w2) * 0.5; 351 $x += int 0.5 * ($ow - $w);
287 $y += int +($h - $h2) * 0.5; 352 $y += int 0.5 * ($oh - $h);
288
289 ($w, $h) = ($w2, $h2);
290 } 353 }
291 354
292 if ($self->{x} != $x || $self->{y} != $y) { 355 if ($self->{x} ne $x || $self->{y} ne $y) {
293 $self->{x} = $x; 356 $self->{x} = $x;
294 $self->{y} = $y; 357 $self->{y} = $y;
295 $self->update; 358 $self->update;
296 } 359 }
297 360
298 if ($self->{w} != $w || $self->{h} != $h) { 361 if ($self->{alloc_w} != $w || $self->{alloc_h} != $h) {
299 $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;
300 } 368 }
301} 369}
302 370
303sub size_allocate { 371sub size_allocate {
304 # nothing to be done 372 # nothing to be done
305}
306
307sub reconfigure {
308 my ($self) = @_;
309
310 $self->check_size (1);
311 $self->update;
312} 373}
313 374
314sub children { 375sub children {
315} 376}
316 377
378 439
379 $::MAPWIDGET->focus_in #d# focus mapwidget if no other widget has focus 440 $::MAPWIDGET->focus_in #d# focus mapwidget if no other widget has focus
380 unless $FOCUS; 441 unless $FOCUS;
381} 442}
382 443
383sub mouse_motion { } 444sub mouse_motion { 0 }
384sub button_up { } 445sub button_up { 0 }
385sub key_down { } 446sub key_down { 0 }
386sub key_up { } 447sub key_up { 0 }
387 448
388sub button_down { 449sub button_down {
389 my ($self, $ev, $x, $y) = @_; 450 my ($self, $ev, $x, $y) = @_;
390 451
391 $self->focus_in; 452 $self->focus_in;
392}
393 453
394sub w { $_[0]{w} = $_[1] if @_ > 1; $_[0]{w} } 454 0
395sub h { $_[0]{h} = $_[1] if @_ > 1; $_[0]{h} } 455}
396sub x { $_[0]{x} = $_[1] if @_ > 1; $_[0]{x} } 456
397sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} } 457sub find_widget {
398sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} } 458 my ($self, $x, $y) = @_;
459
460 return () unless $self->{can_events};
461
462 return $self
463 if $x >= $self->{x} && $x < $self->{x} + $self->{w}
464 && $y >= $self->{y} && $y < $self->{y} + $self->{h};
465
466 ()
467}
468
469sub set_parent {
470 my ($self, $parent) = @_;
471
472 Scalar::Util::weaken ($self->{parent} = $parent);
473 $self->set_visible if $parent->{visible};
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}
494
495sub visibility_change {
496 #my ($self, $visible) = @_;
497}
498
499sub realloc {
500 my ($self) = @_;
501
502 if ($self->{visible}) {
503 return if $self->{root}{realloc}{$self+0};
504
505 $self->{root}{realloc}{$self+0} = $self;
506 $self->{root}->update;
507 } else {
508 delete $self->{req_w};
509 delete $self->{req_h};
510 }
511}
512
513sub update {
514 my ($self) = @_;
515
516 $self->{parent}->update
517 if $self->{parent};
518}
519
520sub reconfigure {
521 my ($self) = @_;
522
523 $self->realloc;
524 $self->update;
525}
526
527# using global variables seems a bit hacky, but passing through all drawing
528# functions seems pointless.
529our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn
399 530
400sub draw { 531sub draw {
401 my ($self) = @_; 532 my ($self) = @_;
402 533
403 return unless $self->{h} && $self->{w}; 534 return unless $self->{h} && $self->{w};
535
536 # update screen rectangle
537 local $draw_x = $draw_x + $self->{x};
538 local $draw_y = $draw_y + $self->{y};
539 local $draw_w = $draw_x + $self->{w};
540 local $draw_h = $draw_y + $self->{h};
541
542 # skip widgets that are entirely outside the drawing area
543 return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w)
544 || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h);
404 545
405 glPushMatrix; 546 glPushMatrix;
406 glTranslate $self->{x}, $self->{y}, 0; 547 glTranslate $self->{x}, $self->{y}, 0;
407 $self->_draw; 548 $self->_draw;
408 glPopMatrix; 549 glPopMatrix;
420 glVertex $x , $y + $self->{h}; 561 glVertex $x , $y + $self->{h};
421 glEnd; 562 glEnd;
422 glDisable GL_BLEND; 563 glDisable GL_BLEND;
423 } 564 }
424 565
425 if ($ENV{PCLIENT_DEBUG}) { 566 if ($ENV{CFPLUS_DEBUG} & 1) {
426 glPushMatrix; 567 glPushMatrix;
427 glColor 1, 1, 0, 1; 568 glColor 1, 1, 0, 1;
428 glTranslate $self->{x} + 0.375, $self->{y} + 0.375; 569 glTranslate $self->{x} + 0.375, $self->{y} + 0.375;
429 glBegin GL_LINE_LOOP; 570 glBegin GL_LINE_LOOP;
430 glVertex 0 , 0; 571 glVertex 0 , 0;
441 my ($self) = @_; 582 my ($self) = @_;
442 583
443 warn "no draw defined for $self\n"; 584 warn "no draw defined for $self\n";
444} 585}
445 586
446sub find_widget {
447 my ($self, $x, $y) = @_;
448
449 return () unless $self->{can_events};
450
451 return $self
452 if $x >= $self->{x} && $x < $self->{x} + $self->{w}
453 && $y >= $self->{y} && $y < $self->{y} + $self->{h};
454
455 ()
456}
457
458sub set_parent {
459 my ($self, $parent) = @_;
460
461 Scalar::Util::weaken ($self->{parent} = $parent);
462
463 $self->{root} = $parent->{root};
464 $self->{visible} = $parent->{visible};
465
466 # TODO: req_w _does_change after ->reconfigure
467 $self->check_size
468 unless exists $self->{req_w};
469
470 $self->show;
471}
472
473sub check_size {
474 my ($self, $forced) = @_;
475
476 $self->{force_alloc} = 1 if $forced;
477 $CFClient::UI::ROOT->{check_size}{$self} = $self;
478}
479
480sub update {
481 my ($self) = @_;
482
483 $self->{parent}->update
484 if $self->{parent};
485}
486
487sub connect {
488 my ($self, $signal, $cb) = @_;
489
490 push @{ $self->{signal_cb}{$signal} }, $cb;
491}
492
493sub _emit {
494 my ($self, $signal, @args) = @_;
495
496 List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}
497}
498
499sub emit {
500 my ($self, $signal, @args) = @_;
501
502 $self->_emit ($signal, @args)
503 || $self->$signal (@args);
504}
505
506sub DESTROY { 587sub DESTROY {
507 my ($self) = @_; 588 my ($self) = @_;
508 589
509 delete $WIDGET{$self+0}; 590 delete $WIDGET{$self+0};
510 #$self->deactivate; 591 #$self->deactivate;
566 my ($class, %arg) = @_; 647 my ($class, %arg) = @_;
567 $class->SUPER::new (can_events => 0, %arg); 648 $class->SUPER::new (can_events => 0, %arg);
568} 649}
569 650
570sub size_request { 651sub size_request {
571 (0, 0) 652 my ($self) = @_;
653
654 ($self->{w} + 0, $self->{h} + 0)
572} 655}
573 656
574sub draw { } 657sub draw { }
575 658
576############################################################################# 659#############################################################################
605 $self->{children} = [ 688 $self->{children} = [
606 sort { $a->{z} <=> $b->{z} } 689 sort { $a->{z} <=> $b->{z} }
607 @{$self->{children}}, @widgets 690 @{$self->{children}}, @widgets
608 ]; 691 ];
609 692
610 $self->check_size (1); 693 $self->realloc;
611 $self->update;
612} 694}
613 695
614sub children { 696sub children {
615 @{ $_[0]{children} } 697 @{ $_[0]{children} }
616} 698}
621 delete $child->{parent}; 703 delete $child->{parent};
622 $child->hide; 704 $child->hide;
623 705
624 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; 706 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ];
625 707
626 $self->check_size (1); 708 $self->realloc;
627 $self->update;
628} 709}
629 710
630sub clear { 711sub clear {
631 my ($self) = @_; 712 my ($self) = @_;
632 713
636 for (@$children) { 717 for (@$children) {
637 delete $_->{parent}; 718 delete $_->{parent};
638 $_->hide; 719 $_->hide;
639 } 720 }
640 721
641 $self->check_size; 722 $self->realloc;
642 $self->update;
643} 723}
644 724
645sub find_widget { 725sub find_widget {
646 my ($self, $x, $y) = @_; 726 my ($self, $x, $y) = @_;
647 727
734 $self->SUPER::size_allocate ($w, $h); 814 $self->SUPER::size_allocate ($w, $h);
735 $self->update; 815 $self->update;
736} 816}
737 817
738sub _render { 818sub _render {
819 my ($self) = @_;
820
739 $_[0]{children}[0]->draw; 821 $self->{children}[0]->draw;
740} 822}
741 823
742sub render_child { 824sub render_child {
743 my ($self) = @_; 825 my ($self) = @_;
744 826
745 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub { 827 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub {
746 glClearColor 0, 0, 0, 0; 828 glClearColor 0, 0, 0, 0;
747 glClear GL_COLOR_BUFFER_BIT; 829 glClear GL_COLOR_BUFFER_BIT;
748 830
831 {
832 package CFClient::UI::Base;
833
834 ($draw_x, $draw_y, $draw_w, $draw_h) =
835 (0, 0, $self->{w}, $self->{h});
836 }
837
749 $self->_render; 838 $self->_render;
750 }; 839 };
751} 840}
752 841
753sub _draw { 842sub _draw {
754 my ($self) = @_; 843 my ($self) = @_;
755 844
756 my ($w, $h) = ($self->w, $self->h); 845 my ($w, $h) = @$self{qw(w h)};
757 846
758 my $tex = $self->{texture} 847 my $tex = $self->{texture}
759 or return; 848 or return;
760 849
761 glEnable GL_TEXTURE_2D; 850 glEnable GL_TEXTURE_2D;
784} 873}
785 874
786sub size_request { 875sub size_request {
787 my ($self) = @_; 876 my ($self) = @_;
788 877
789 @$self{qw(child_w child_h)} = @{$self->child}{qw(req_w req_h)}; 878 my ($w, $h) = @{$self->child}{qw(req_w req_h)};
790 879
791 @$self{qw(child_w child_h)} 880 $w = 10 if $self->{scroll_x};
881 $h = 10 if $self->{scroll_y};
882
883 ($w, $h)
792} 884}
793 885
794sub size_allocate { 886sub size_allocate {
795 my ($self, $w, $h) = @_; 887 my ($self, $w, $h) = @_;
796 888
889 my $child = $self->child;
890
797 $w = $self->{child_w} if $self->{scroll_x} && $self->{child_w}; 891 $w = $child->{req_w} if $self->{scroll_x} && $child->{req_w};
798 $h = $self->{child_h} if $self->{scroll_y} && $self->{child_h}; 892 $h = $child->{req_h} if $self->{scroll_y} && $child->{req_h};
799 893
800 $self->child->configure (0, 0, $w, $h); 894 $self->child->configure (0, 0, $w, $h);
801 $self->update; 895 $self->update;
802} 896}
803 897
839} 933}
840 934
841sub _render { 935sub _render {
842 my ($self) = @_; 936 my ($self) = @_;
843 937
938 local $CFClient::UI::Base::draw_x = $CFClient::UI::Base::draw_x - $self->{view_x};
939 local $CFClient::UI::Base::draw_y = $CFClient::UI::Base::draw_y - $self->{view_y};
940
844 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y}; 941 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y};
845 942
846 $self->SUPER::_render; 943 $self->SUPER::_render;
847} 944}
848 945
856 my $class = shift; 953 my $class = shift;
857 954
858 my $self; 955 my $self;
859 956
860 my $slider = new CFClient::UI::Slider 957 my $slider = new CFClient::UI::Slider
861 vertical => 1, 958 vertical => 1,
862 range => [0, 0, 1, 0.01], # HACK fix 959 range => [0, 0, 1, 0.01], # HACK fix
863 connect_changed => sub { 960 on_changed => sub {
864 $self->{vp}->set_offset (0, $_[1]); 961 $self->{vp}->set_offset (0, $_[1]);
865 }, 962 },
866 ; 963 ;
867 964
868 $self = $class->SUPER::new ( 965 $self = $class->SUPER::new (
946 1043
947our @ISA = CFClient::UI::Bin::; 1044our @ISA = CFClient::UI::Bin::;
948 1045
949use CFClient::OpenGL; 1046use CFClient::OpenGL;
950 1047
951my @tex = 1048my $bg =
1049 new_from_file CFClient::Texture CFClient::find_rcfile "d1_bg.png",
1050 mipmap => 1, wrap => 1;
1051
1052my @border =
952 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 1053 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 }
953 qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); 1054 qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
954 1055
955sub new { 1056sub new {
956 my $class = shift; 1057 my ($class, %arg) = @_;
957 1058
958 # TODO: user_x, user_y, overwrite moveto? 1059 my $title = delete $arg{title};
959 1060
960 my $self = $class->SUPER::new ( 1061 my $self = $class->SUPER::new (
961 bg => [1, 1, 1, 1], 1062 bg => [1, 1, 1, 1],
962 border_bg => [1, 1, 1, 1], 1063 border_bg => [1, 1, 1, 1],
963 border => 0.6, 1064 border => 0.6,
964 is_toplevel => 1,
965 can_events => 1, 1065 can_events => 1,
966 @_ 1066 min_w => 16,
1067 min_h => 16,
1068 %arg,
967 ); 1069 );
968 1070
969 $self->{title} &&= new CFClient::UI::Label 1071 $self->{title} = new CFClient::UI::Label
970 align => 0, 1072 align => 0,
971 valign => 1, 1073 valign => 1,
972 text => $self->{title}, 1074 text => $title,
973 fontsize => $self->{border}; 1075 fontsize => $self->{border}
1076 if defined $title;
974 1077
975 $self 1078 $self
1079}
1080
1081sub add {
1082 my ($self, @widgets) = @_;
1083
1084 $self->SUPER::add (@widgets);
1085 $self->CFClient::UI::Container::add ($self->{title}) if $self->{title};
976} 1086}
977 1087
978sub border { 1088sub border {
979 int $_[0]{border} * $::FONTSIZE 1089 int $_[0]{border} * $::FONTSIZE
980} 1090}
981 1091
982sub size_request { 1092sub size_request {
983 my ($self) = @_; 1093 my ($self) = @_;
1094
1095 $self->{title}->size_request
1096 if $self->{title};
984 1097
985 my ($w, $h) = $self->SUPER::size_request; 1098 my ($w, $h) = $self->SUPER::size_request;
986 1099
987 ( 1100 (
988 $w + $self->border * 2, 1101 $w + $self->border * 2,
991} 1104}
992 1105
993sub size_allocate { 1106sub size_allocate {
994 my ($self, $w, $h) = @_; 1107 my ($self, $w, $h) = @_;
995 1108
1109 if ($self->{title}) {
1110 $self->{title}{w} = $w;
1111 $self->{title}{h} = $h;
1112 $self->{title}->size_allocate ($w, $h);
1113 }
1114
1115 my $border = $self->border;
1116
996 $h -= List::Util::max 0, $self->border * 2; 1117 $h -= List::Util::max 0, $border * 2;
997 $w -= List::Util::max 0, $self->border * 2; 1118 $w -= List::Util::max 0, $border * 2;
998 1119
999 $self->{title}->configure ($self->border, int $self->border - $::FONTSIZE * 2, $w, int $::FONTSIZE * 2)
1000 if $self->{title};
1001
1002 $self->child->configure ($self->border, $self->border, $w, $h); 1120 $self->child->configure ($border, $border, $w, $h);
1003} 1121}
1004 1122
1005sub button_down { 1123sub button_down {
1006 my ($self, $ev, $x, $y) = @_; 1124 my ($self, $ev, $x, $y) = @_;
1007 1125
1023 my ($ev, $x, $y) = @_; 1141 my ($ev, $x, $y) = @_;
1024 1142
1025 my $dx = $ev->{x} - $ox; 1143 my $dx = $ev->{x} - $ox;
1026 my $dy = $ev->{y} - $oy; 1144 my $dy = $ev->{y} - $oy;
1027 1145
1028 $self->{user_w} = $bw + $dx * ($mx ? -1 : 1); 1146 $self->{force_w} = $bw + $dx * ($mx ? -1 : 1);
1029 $self->{user_h} = $bh + $dy * ($my ? -1 : 1); 1147 $self->{force_h} = $bh + $dy * ($my ? -1 : 1);
1148
1149 $self->realloc;
1030 $self->move ($wx + $dx * $mx, $wy + $dy * $my); 1150 $self->move_abs ($wx + $dx * $mx, $wy + $dy * $my);
1031 $self->check_size;
1032 }; 1151 };
1033 1152
1034 } elsif ($lr ^ $td) { 1153 } elsif ($lr ^ $td) {
1035 my ($ox, $oy) = ($ev->{x}, $ev->{y}); 1154 my ($ox, $oy) = ($ev->{x}, $ev->{y});
1036 my ($bx, $by) = ($self->{x}, $self->{y}); 1155 my ($bx, $by) = ($self->{x}, $self->{y});
1038 $self->{motion} = sub { 1157 $self->{motion} = sub {
1039 my ($ev, $x, $y) = @_; 1158 my ($ev, $x, $y) = @_;
1040 1159
1041 ($x, $y) = ($ev->{x}, $ev->{y}); 1160 ($x, $y) = ($ev->{x}, $ev->{y});
1042 1161
1043 $self->move ($bx + $x - $ox, $by + $y - $oy); 1162 $self->move_abs ($bx + $x - $ox, $by + $y - $oy);
1044 $self->update;
1045 }; 1163 };
1164 } else {
1165 return 0;
1166 }
1167
1046 } 1168 1
1047} 1169}
1048 1170
1049sub button_up { 1171sub button_up {
1050 my ($self, $ev, $x, $y) = @_; 1172 my ($self, $ev, $x, $y) = @_;
1051 1173
1052 delete $self->{motion}; 1174 !!delete $self->{motion}
1053} 1175}
1054 1176
1055sub mouse_motion { 1177sub mouse_motion {
1056 my ($self, $ev, $x, $y) = @_; 1178 my ($self, $ev, $x, $y) = @_;
1057 1179
1058 $self->{motion}->($ev, $x, $y) if $self->{motion}; 1180 $self->{motion}->($ev, $x, $y) if $self->{motion};
1181
1182 !!$self->{motion}
1059} 1183}
1060 1184
1061sub _draw { 1185sub _draw {
1062 my ($self) = @_; 1186 my ($self) = @_;
1063 1187
1188 my $child = $self->{children}[0];
1189
1064 my ($w, $h ) = ($self->{w}, $self->{h}); 1190 my ($w, $h ) = ($self->{w}, $self->{h});
1065 my ($cw, $ch) = ($self->child->{w}, $self->child->{h}); 1191 my ($cw, $ch) = ($child->{w}, $child->{h});
1066 1192
1067 glEnable GL_TEXTURE_2D; 1193 glEnable GL_TEXTURE_2D;
1068 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE; 1194 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1069 1195
1070 my $border = $self->border; 1196 my $border = $self->border;
1071 1197
1072 glColor @{ $self->{border_bg} }; 1198 glColor @{ $self->{border_bg} };
1073 $tex[1]->draw_quad_alpha (0, 0, $w, $border); 1199 $border[0]->draw_quad_alpha (0, 0, $w, $border);
1074 $tex[3]->draw_quad_alpha (0, $border, $border, $ch); 1200 $border[1]->draw_quad_alpha (0, $border, $border, $ch);
1075 $tex[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); 1201 $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch);
1076 $tex[4]->draw_quad_alpha (0, $h - $border, $w, $border); 1202 $border[3]->draw_quad_alpha (0, $h - $border, $w, $border);
1077 1203
1078 if (@{$self->{bg}} < 4 || $self->{bg}[3]) { 1204 if (@{$self->{bg}} < 4 || $self->{bg}[3]) {
1079 my $bg = $tex[0]; 1205 glColor @{ $self->{bg} };
1080 1206
1081 # TODO: repeat texture not scale 1207 # TODO: repeat texture not scale
1208 # solve this better(?)
1082 my $rep_x = $cw / $bg->{w}; 1209 $bg->{s} = $cw / $bg->{w};
1083 my $rep_y = $ch / $bg->{h}; 1210 $bg->{t} = $ch / $bg->{h};
1084
1085 glColor @{ $self->{bg} };
1086
1087 $bg->{s} = $rep_x;
1088 $bg->{t} = $rep_y;
1089 $bg->{wrap_mode} = 1;
1090 $bg->draw_quad_alpha ($border, $border, $cw, $ch); 1211 $bg->draw_quad_alpha ($border, $border, $cw, $ch);
1091 } 1212 }
1092 1213
1093 glDisable GL_TEXTURE_2D; 1214 glDisable GL_TEXTURE_2D;
1094 1215
1095 $self->{title}->draw if $self->{title};
1096
1097 $self->child->draw; 1216 $child->draw;
1217
1218 if ($self->{title}) {
1219 glTranslate 0, $border - $self->{h};
1220 $self->{title}->_draw;
1221 }
1098} 1222}
1099 1223
1100############################################################################# 1224#############################################################################
1101 1225
1102package CFClient::UI::Table; 1226package CFClient::UI::Table;
1124 my ($self, $x, $y, $child) = @_; 1248 my ($self, $x, $y, $child) = @_;
1125 1249
1126 $child->set_parent ($self); 1250 $child->set_parent ($self);
1127 $self->{children}[$y][$x] = $child; 1251 $self->{children}[$y][$x] = $child;
1128 1252
1129 $self->check_size (1); 1253 $self->realloc;
1130} 1254}
1131 1255
1132# TODO: move to container class maybe? send children a signal on removal? 1256# TODO: move to container class maybe? send children a signal on removal?
1133sub clear { 1257sub clear {
1134 my ($self) = @_; 1258 my ($self) = @_;
1139 for (@children) { 1263 for (@children) {
1140 delete $_->{parent}; 1264 delete $_->{parent};
1141 $_->hide; 1265 $_->hide;
1142 } 1266 }
1143 1267
1144 $self->check_size (1); 1268 $self->realloc;
1145 $self->update;
1146} 1269}
1147 1270
1148sub get_wh { 1271sub get_wh {
1149 my ($self) = @_; 1272 my ($self) = @_;
1150 1273
1246 } 1369 }
1247} 1370}
1248 1371
1249############################################################################# 1372#############################################################################
1250 1373
1251package CFClient::UI::HBox; 1374package CFClient::UI::Box;
1252
1253# TODO: wrap into common Box base class
1254 1375
1255our @ISA = CFClient::UI::Container::; 1376our @ISA = CFClient::UI::Container::;
1256 1377
1257sub size_request { 1378sub size_request {
1258 my ($self) = @_; 1379 my ($self) = @_;
1259 1380
1260 my @alloc = map [$_->size_request], @{$self->{children}}; 1381 $self->{vertical}
1261 1382 ? (
1262 ( 1383 (List::Util::max map $_->{req_w}, @{$self->{children}}),
1263 (List::Util::sum map $_->[0], @alloc), 1384 (List::Util::sum map $_->{req_h}, @{$self->{children}}),
1264 (List::Util::max map $_->[1], @alloc), 1385 )
1265 ) 1386 : (
1387 (List::Util::sum map $_->{req_w}, @{$self->{children}}),
1388 (List::Util::max map $_->{req_h}, @{$self->{children}}),
1389 )
1266} 1390}
1267 1391
1268sub size_allocate { 1392sub size_allocate {
1269 my ($self, $w, $h) = @_; 1393 my ($self, $w, $h) = @_;
1270 1394
1271 ($h, $w) = ($w, $h); 1395 my $space = $self->{vertical} ? $h : $w;
1272
1273 my $children = $self->{children}; 1396 my $children = $self->{children};
1274 1397
1275 my @h = map $_->{req_w}, @$children; 1398 my @req;
1276 1399
1277 my $req_h = List::Util::sum @h; 1400 if ($self->{homogeneous}) {
1278 1401 @req = ($space / (@$children || 1)) x @$children;
1279 if ($req_h > $h) {
1280 # ah well, not enough space
1281 $_ *= $h / $req_h for @h;
1282 } else { 1402 } else {
1403 @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @$children;
1404 my $req = List::Util::sum @req;
1405
1406 if ($req > $space) {
1407 # ah well, not enough space
1408 $_ *= $space / $req for @req;
1409 } else {
1283 my $exp = List::Util::sum map $_->{expand}, @$children; 1410 my $expand = (List::Util::sum map $_->{expand}, @$children) || 1;
1284 $exp ||= 1;
1285 1411
1412 $space = ($space - $req) / $expand; # remaining space to give away
1413
1414 $req[$_] += $space * $children->[$_]{expand}
1286 for (0 .. $#$children) { 1415 for 0 .. $#$children;
1287 my $child = $children->[$_];
1288
1289 my $alloc_h = $h[$_];
1290 $alloc_h += ($h - $req_h) * $child->{expand} / $exp;
1291 $h[$_] = $alloc_h;
1292 } 1416 }
1293 } 1417 }
1294 1418
1295 CFClient::UI::harmonize \@h; 1419 CFClient::UI::harmonize \@req;
1296 1420
1297 my $y = 0; 1421 my $pos = 0;
1298 for (0 .. $#$children) { 1422 for (0 .. $#$children) {
1299 my $child = $children->[$_];
1300 my $h = $h[$_]; 1423 my $alloc = $req[$_];
1301 $child->configure ($y, 0, $h, $w); 1424 $children->[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h));
1302 1425
1303 $y += $h; 1426 $pos += $alloc;
1304 } 1427 }
1305 1428
1306 1 1429 1
1307} 1430}
1308 1431
1309############################################################################# 1432#############################################################################
1310 1433
1434package CFClient::UI::HBox;
1435
1436our @ISA = CFClient::UI::Box::;
1437
1438sub new {
1439 my $class = shift;
1440
1441 $class->SUPER::new (
1442 vertical => 0,
1443 @_,
1444 )
1445}
1446
1447#############################################################################
1448
1311package CFClient::UI::VBox; 1449package CFClient::UI::VBox;
1312 1450
1313# TODO: wrap into common Box base class
1314
1315our @ISA = CFClient::UI::Container::; 1451our @ISA = CFClient::UI::Box::;
1316 1452
1317sub size_request { 1453sub new {
1318 my ($self) = @_; 1454 my $class = shift;
1319 1455
1320 my @alloc = map [$_->size_request], @{$self->{children}}; 1456 $class->SUPER::new (
1321 1457 vertical => 1,
1322 ( 1458 @_,
1323 (List::Util::max map $_->[0], @alloc),
1324 (List::Util::sum map $_->[1], @alloc),
1325 ) 1459 )
1326}
1327
1328sub size_allocate {
1329 my ($self, $w, $h) = @_;
1330
1331 Carp::confess "negative size" if $w < 0 || $h < 0;#d#
1332
1333 my $children = $self->{children};
1334
1335 my @h = map $_->{req_h}, @$children;
1336
1337 my $req_h = List::Util::sum @h;
1338
1339 if ($req_h > $h) {
1340 # ah well, not enough space
1341 $_ *= $h / $req_h for @h;
1342 } else {
1343 my $exp = List::Util::sum map $_->{expand}, @$children;
1344 $exp ||= 1;
1345
1346 for (0 .. $#$children) {
1347 my $child = $children->[$_];
1348
1349 $h[$_] += ($h - $req_h) * $child->{expand} / $exp;
1350 }
1351 }
1352
1353 CFClient::UI::harmonize \@h;
1354
1355 my $y = 0;
1356 for (0 .. $#$children) {
1357 my $child = $children->[$_];
1358 my $h = $h[$_];
1359 $child->configure (0, $y, $w, $h);
1360
1361 $y += $h;
1362 }
1363
1364 1
1365} 1460}
1366 1461
1367############################################################################# 1462#############################################################################
1368 1463
1369package CFClient::UI::Label; 1464package CFClient::UI::Label;
1386 ellipsise => 3, # end 1481 ellipsise => 3, # end
1387 layout => (new CFClient::Layout), 1482 layout => (new CFClient::Layout),
1388 fontsize => 1, 1483 fontsize => 1,
1389 align => -1, 1484 align => -1,
1390 valign => -1, 1485 valign => -1,
1391 padding => 2, 1486 padding_x => 2,
1487 padding_y => 2,
1392 can_events => 0, 1488 can_events => 0,
1393 %arg 1489 %arg
1394 ); 1490 );
1395 1491
1396 if (exists $self->{template}) { 1492 if (exists $self->{template}) {
1432 $self->{text} = "T$text"; 1528 $self->{text} = "T$text";
1433 1529
1434 $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba; 1530 $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba;
1435 $self->{layout}->set_text ($text); 1531 $self->{layout}->set_text ($text);
1436 1532
1533 $self->realloc;
1437 $self->update; 1534 $self->update;
1438 $self->check_size;
1439} 1535}
1440 1536
1441sub set_markup { 1537sub set_markup {
1442 my ($self, $markup) = @_; 1538 my ($self, $markup) = @_;
1443 1539
1447 my $rgba = $markup =~ /span.*(?:foreground|background)/; 1543 my $rgba = $markup =~ /span.*(?:foreground|background)/;
1448 1544
1449 $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba; 1545 $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba;
1450 $self->{layout}->set_markup ($markup); 1546 $self->{layout}->set_markup ($markup);
1451 1547
1548 $self->realloc;
1452 $self->update; 1549 $self->update;
1453 $self->check_size;
1454} 1550}
1455 1551
1456sub size_request { 1552sub size_request {
1457 my ($self) = @_; 1553 my ($self) = @_;
1458 1554
1472 1568
1473 $w = List::Util::max $w, $w2; 1569 $w = List::Util::max $w, $w2;
1474 $h = List::Util::max $h, $h2; 1570 $h = List::Util::max $h, $h2;
1475 } 1571 }
1476 1572
1477 ( 1573 ($w, $h)
1478 $w + $self->{padding} * 2,
1479 $h + $self->{padding} * 2,
1480 )
1481} 1574}
1482 1575
1483sub size_allocate { 1576sub size_allocate {
1484 my ($self, $w, $h) = @_; 1577 my ($self, $w, $h) = @_;
1485 1578
1579 delete $self->{ox};
1580
1486 delete $self->{texture}; 1581 delete $self->{texture}
1582 unless $w >= $self->{req_w} && $self->{old_w} >= $self->{req_w};
1487} 1583}
1488 1584
1489sub set_fontsize { 1585sub set_fontsize {
1490 my ($self, $fontsize) = @_; 1586 my ($self, $fontsize) = @_;
1491 1587
1492 $self->{fontsize} = $fontsize; 1588 $self->{fontsize} = $fontsize;
1493 delete $self->{texture}; 1589 delete $self->{texture};
1494 1590
1495 $self->update; 1591 $self->realloc;
1496 $self->check_size;
1497} 1592}
1498 1593
1499sub _draw { 1594sub _draw {
1500 my ($self) = @_; 1595 my ($self) = @_;
1501 1596
1507 $self->{layout}->set_width ($self->{w}); 1602 $self->{layout}->set_width ($self->{w});
1508 $self->{layout}->set_ellipsise ($self->{ellipsise}); 1603 $self->{layout}->set_ellipsise ($self->{ellipsise});
1509 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise}); 1604 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise});
1510 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); 1605 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
1511 1606
1512 my $tex = new_from_layout CFClient::Texture $self->{layout}; 1607 new_from_layout CFClient::Texture $self->{layout}
1608 };
1513 1609
1610 unless (exists $self->{ox}) {
1514 $self->{ox} = int ($self->{align} < 0 ? $self->{padding} 1611 $self->{ox} = int ($self->{align} < 0 ? $self->{padding_x}
1515 : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding} 1612 : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding_x}
1516 : ($self->{w} - $tex->{w}) * 0.5); 1613 : ($self->{w} - $tex->{w}) * 0.5);
1517 1614
1518 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding} 1615 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding_y}
1519 : $self->{valign} > 0 ? $self->{h} - $tex->{h} - $self->{padding} 1616 : $self->{valign} > 0 ? $self->{h} - $tex->{h} - $self->{padding_y}
1520 : ($self->{h} - $tex->{h}) * 0.5); 1617 : ($self->{h} - $tex->{h}) * 0.5);
1521
1522 $tex
1523 }; 1618 };
1524 1619
1525 glEnable GL_TEXTURE_2D; 1620 glEnable GL_TEXTURE_2D;
1526 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; 1621 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1527 1622
1581sub set_text { 1676sub set_text {
1582 my ($self, $text) = @_; 1677 my ($self, $text) = @_;
1583 1678
1584 $self->{cursor} = length $text; 1679 $self->{cursor} = length $text;
1585 $self->_set_text ($text); 1680 $self->_set_text ($text);
1586 $self->update; 1681
1587 $self->check_size; 1682 $self->realloc;
1588} 1683}
1589 1684
1590sub get_text { 1685sub get_text {
1591 $_[0]{text} 1686 $_[0]{text}
1592} 1687}
1622 $self->{cursor} = length $text; 1717 $self->{cursor} = length $text;
1623 } elsif ($uni == 27) { 1718 } elsif ($uni == 27) {
1624 $self->_emit ('escape'); 1719 $self->_emit ('escape');
1625 } elsif ($uni) { 1720 } elsif ($uni) {
1626 substr $text, $self->{cursor}++, 0, chr $uni; 1721 substr $text, $self->{cursor}++, 0, chr $uni;
1722 } else {
1723 return 0;
1627 } 1724 }
1628 1725
1629 $self->_set_text ($text); 1726 $self->_set_text ($text);
1630 $self->update; 1727
1631 $self->check_size; 1728 $self->realloc;
1729
1730 1
1632} 1731}
1633 1732
1634sub focus_in { 1733sub focus_in {
1635 my ($self) = @_; 1734 my ($self) = @_;
1636 1735
1651 utf8::encode $text; 1750 utf8::encode $text;
1652 $self->{cursor} = length substr $text, 0, $idx; 1751 $self->{cursor} = length substr $text, 0, $idx;
1653 1752
1654 $self->_set_text ($self->{text}); 1753 $self->_set_text ($self->{text});
1655 $self->update; 1754 $self->update;
1755
1756 1
1656} 1757}
1657 1758
1658sub mouse_motion { 1759sub mouse_motion {
1659 my ($self, $ev, $x, $y) = @_; 1760 my ($self, $ev, $x, $y) = @_;
1660# printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d# 1761# printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
1762
1763 0
1661} 1764}
1662 1765
1663sub _draw { 1766sub _draw {
1664 my ($self) = @_; 1767 my ($self) = @_;
1665 1768
1742 } else { 1845 } else {
1743 $self->set_text ($self->{history_saveback}); 1846 $self->set_text ($self->{history_saveback});
1744 } 1847 }
1745 1848
1746 } else { 1849 } else {
1747 $self->SUPER::key_down ($ev); 1850 return $self->SUPER::key_down ($ev)
1851 }
1852
1748 } 1853 1
1749
1750} 1854}
1751 1855
1752############################################################################# 1856#############################################################################
1753 1857
1754package CFClient::UI::Button; 1858package CFClient::UI::Button;
1763 1867
1764sub new { 1868sub new {
1765 my $class = shift; 1869 my $class = shift;
1766 1870
1767 $class->SUPER::new ( 1871 $class->SUPER::new (
1768 padding => 4, 1872 padding_x => 4,
1873 padding_y => 4,
1769 fg => [1, 1, 1], 1874 fg => [1, 1, 1],
1770 active_fg => [0, 0, 1], 1875 active_fg => [0, 0, 1],
1771 can_hover => 1, 1876 can_hover => 1,
1772 align => 0, 1877 align => 0,
1773 valign => 0, 1878 valign => 0,
1782 my ($self, $ev, $x, $y) = @_; 1887 my ($self, $ev, $x, $y) = @_;
1783 1888
1784 $self->emit ("activate") 1889 $self->emit ("activate")
1785 if $x >= 0 && $x < $self->{w} 1890 if $x >= 0 && $x < $self->{w}
1786 && $y >= 0 && $y < $self->{h}; 1891 && $y >= 0 && $y < $self->{h};
1892
1893 1
1787} 1894}
1788 1895
1789sub _draw { 1896sub _draw {
1790 my ($self) = @_; 1897 my ($self) = @_;
1791 1898
1820 1927
1821sub new { 1928sub new {
1822 my $class = shift; 1929 my $class = shift;
1823 1930
1824 $class->SUPER::new ( 1931 $class->SUPER::new (
1825 padding => 2, 1932 padding_x => 2,
1933 padding_y => 2,
1826 fg => [1, 1, 1], 1934 fg => [1, 1, 1],
1827 active_fg => [1, 1, 0], 1935 active_fg => [1, 1, 0],
1828 bg => [0, 0, 0, 0.2], 1936 bg => [0, 0, 0, 0.2],
1829 active_bg => [1, 1, 1, 0.5], 1937 active_bg => [1, 1, 1, 0.5],
1830 state => 0, 1938 state => 0,
1834} 1942}
1835 1943
1836sub size_request { 1944sub size_request {
1837 my ($self) = @_; 1945 my ($self) = @_;
1838 1946
1839 ($self->{padding} * 2 + 6) x 2 1947 (6) x 2
1840} 1948}
1841 1949
1842sub button_down { 1950sub button_down {
1843 my ($self, $ev, $x, $y) = @_; 1951 my ($self, $ev, $x, $y) = @_;
1844 1952
1845 if ($x >= $self->{padding} && $x < $self->{w} - $self->{padding} 1953 if ($x >= $self->{padding_x} && $x < $self->{w} - $self->{padding_x}
1846 && $y >= $self->{padding} && $y < $self->{h} - $self->{padding}) { 1954 && $y >= $self->{padding_y} && $y < $self->{h} - $self->{padding_y}) {
1847 $self->{state} = !$self->{state}; 1955 $self->{state} = !$self->{state};
1848 $self->_emit (changed => $self->{state}); 1956 $self->_emit (changed => $self->{state});
1957 } else {
1958 return 0
1959 }
1960
1849 } 1961 1
1850} 1962}
1851 1963
1852sub _draw { 1964sub _draw {
1853 my ($self) = @_; 1965 my ($self) = @_;
1854 1966
1855 $self->SUPER::_draw; 1967 $self->SUPER::_draw;
1856 1968
1857 glTranslate $self->{padding} + 0.375, $self->{padding} + 0.375, 0; 1969 glTranslate $self->{padding_x} + 0.375, $self->{padding_y} + 0.375, 0;
1858 1970
1859 my $s = (List::Util::min @$self{qw(w h)}) - $self->{padding} * 2; 1971 my ($w, $h) = @$self{qw(w h)};
1972
1973 my $s = List::Util::min $w - $self->{padding_x} * 2, $h - $self->{padding_y} * 2;
1860 1974
1861 glColor @{ $FOCUS == $self ? $self->{active_fg} : $self->{fg} }; 1975 glColor @{ $FOCUS == $self ? $self->{active_fg} : $self->{fg} };
1862 1976
1863 my $tex = $self->{state} ? $tex[1] : $tex[0]; 1977 my $tex = $self->{state} ? $tex[1] : $tex[0];
1864 1978
2129 fg => [1, 1, 1], 2243 fg => [1, 1, 1],
2130 active_fg => [0, 0, 0], 2244 active_fg => [0, 0, 0],
2131 bg => [0, 0, 0, 0.2], 2245 bg => [0, 0, 0, 0.2],
2132 active_bg => [1, 1, 1, 0.5], 2246 active_bg => [1, 1, 1, 0.5],
2133 range => [0, 0, 100, 10, 0], 2247 range => [0, 0, 100, 10, 0],
2134 req_w => $::WIDTH / 80, 2248 min_w => $::WIDTH / 80,
2135 req_h => $::WIDTH / 80, 2249 min_h => $::WIDTH / 80,
2136 vertical => 0, 2250 vertical => 0,
2137 can_hover => 1, 2251 can_hover => 1,
2138 inner_pad => 0.02, 2252 inner_pad => 0.02,
2139 @_ 2253 @_
2140 ); 2254 );
2143 $self->update; 2257 $self->update;
2144 2258
2145 $self 2259 $self
2146} 2260}
2147 2261
2262sub changed { }
2263
2148sub set_range { 2264sub set_range {
2149 my ($self, $range) = @_; 2265 my ($self, $range) = @_;
2150 2266
2151 ($range, $self->{range}) = ($self->{range}, $range); 2267 ($range, $self->{range}) = ($self->{range}, $range);
2152 2268
2178} 2294}
2179 2295
2180sub size_request { 2296sub size_request {
2181 my ($self) = @_; 2297 my ($self) = @_;
2182 2298
2183 my $w = $self->{req_w}; 2299 ($self->{req_w}, $self->{req_h})
2184 my $h = $self->{req_h};
2185
2186 $self->{vertical} ? ($h, $w) : ($w, $h)
2187} 2300}
2188 2301
2189sub button_down { 2302sub button_down {
2190 my ($self, $ev, $x, $y) = @_; 2303 my ($self, $ev, $x, $y) = @_;
2191 2304
2192 $self->SUPER::button_down ($ev, $x, $y); 2305 $self->SUPER::button_down ($ev, $x, $y);
2193 2306
2194 $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x]; 2307 $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x];
2195 2308
2196 $self->mouse_motion ($ev, $x, $y); 2309 $self->mouse_motion ($ev, $x, $y)
2197} 2310}
2198 2311
2199sub mouse_motion { 2312sub mouse_motion {
2200 my ($self, $ev, $x, $y) = @_; 2313 my ($self, $ev, $x, $y) = @_;
2201 2314
2205 my (undef, $lo, $hi, $page) = @{$self->{range}}; 2318 my (undef, $lo, $hi, $page) = @{$self->{range}};
2206 2319
2207 $x = ($x - $self->{click}[1]) / ($w * $self->{scale}); 2320 $x = ($x - $self->{click}[1]) / ($w * $self->{scale});
2208 2321
2209 $self->set_value ($self->{click}[0] + $x * ($hi - $page - $lo)); 2322 $self->set_value ($self->{click}[0] + $x * ($hi - $page - $lo));
2323 } else {
2324 return 0;
2325 }
2326
2210 } 2327 1
2211} 2328}
2212 2329
2213sub update { 2330sub update {
2214 my ($self) = @_; 2331 my ($self) = @_;
2215 2332
2542 2659
2543sub new { 2660sub new {
2544 my $class = shift; 2661 my $class = shift;
2545 2662
2546 my $self = $class->SUPER::new ( 2663 my $self = $class->SUPER::new (
2547 state => 0, 2664 state => 0,
2548 connect_activate => \&toggle_flopper, 2665 on_activate => \&toggle_flopper,
2549 @_ 2666 @_
2550 ); 2667 );
2551 2668
2552 if ($self->{state}) {
2553 $self->{state} = 0;
2554 $self->toggle_flopper;
2555 }
2556
2557 $self 2669 $self
2558} 2670}
2559 2671
2560sub toggle_flopper { 2672sub toggle_flopper {
2561 my ($self) = @_; 2673 my ($self) = @_;
2562 2674
2563 # TODO: use animation 2675 $self->{other}->toggle_visibility;
2564 if ($self->{state} = !$self->{state}) {
2565 $CFClient::UI::ROOT->add ($self->{other});
2566 $self->{other}->move ($self->coord2global (0, $self->{h}));
2567 $self->_emit ("open");
2568 } else {
2569 $CFClient::UI::ROOT->remove ($self->{other});
2570 $self->_emit ("close");
2571 }
2572
2573 $self->_emit (changed => $self->{state});
2574} 2676}
2575 2677
2576############################################################################# 2678#############################################################################
2577 2679
2578package CFClient::UI::Tooltip; 2680package CFClient::UI::Tooltip;
2591} 2693}
2592 2694
2593sub set_tooltip_from { 2695sub set_tooltip_from {
2594 my ($self, $widget) = @_; 2696 my ($self, $widget) = @_;
2595 2697
2698 my $tooltip = $widget->{tooltip};
2699
2700 if ($ENV{CFPLUS_DEBUG} & 2) {
2701 $tooltip .= "\n\n" . (ref $widget) . "\n"
2702 . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n"
2703 . "req $widget->{req_w} $widget->{req_h}\n"
2704 . "visible $widget->{visible}";
2705 }
2706
2596 $self->add (new CFClient::UI::Label 2707 $self->add (new CFClient::UI::Label
2597 markup => $widget->{tooltip}, 2708 markup => $tooltip,
2598 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH, 2709 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH,
2599 fontsize => 0.8, 2710 fontsize => 0.8,
2600 fg => [0, 0, 0, 1], 2711 fg => [0, 0, 0, 1],
2601 ellipsise => 0, 2712 ellipsise => 0,
2602 font => ($widget->{tooltip_font} || $::FONT_PROP), 2713 font => ($widget->{tooltip_font} || $::FONT_PROP),
2613 2724
2614sub size_allocate { 2725sub size_allocate {
2615 my ($self, $w, $h) = @_; 2726 my ($self, $w, $h) = @_;
2616 2727
2617 $self->SUPER::size_allocate ($w - 4, $h - 4); 2728 $self->SUPER::size_allocate ($w - 4, $h - 4);
2729}
2730
2731sub visibility_change {
2732 my ($self, $visible) = @_;
2733
2734 return unless $visible;
2735
2736 $self->{root}->on_post_alloc ("move_$self" => sub {
2737 my $widget = $self->{owner}
2738 or return;
2739
2740 my ($x, $y) = $widget->coord2global ($widget->{w}, 0);
2741
2742 ($x, $y) = $widget->coord2global (-$self->{w}, 0)
2743 if $x + $self->{w} > $::WIDTH;
2744
2745 $self->move_abs ($x, $y);
2746 });
2618} 2747}
2619 2748
2620sub _draw { 2749sub _draw {
2621 my ($self) = @_; 2750 my ($self) = @_;
2622 2751
2639 glVertex $w, $h; 2768 glVertex $w, $h;
2640 glVertex $w, 0; 2769 glVertex $w, 0;
2641 glEnd; 2770 glEnd;
2642 2771
2643 glTranslate 2 - 0.375, 2 - 0.375; 2772 glTranslate 2 - 0.375, 2 - 0.375;
2773
2644 $self->SUPER::_draw; 2774 $self->SUPER::_draw;
2645} 2775}
2646 2776
2647############################################################################# 2777#############################################################################
2648 2778
2724 $self->SUPER::DESTROY; 2854 $self->SUPER::DESTROY;
2725} 2855}
2726 2856
2727############################################################################# 2857#############################################################################
2728 2858
2729package CFClient::UI::Inventory;
2730
2731our @ISA = CFClient::UI::ScrolledWindow::;
2732
2733sub new {
2734 my $class = shift;
2735
2736 my $self = $class->SUPER::new (
2737 scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]),
2738 @_,
2739 );
2740
2741 $self
2742}
2743
2744sub set_items {
2745 my ($self, $items) = @_;
2746
2747 $self->{scrolled}->clear;
2748 return unless $items;
2749
2750 my @items = sort {
2751 ($a->{type} <=> $b->{type})
2752 or ($a->{name} cmp $b->{name})
2753 } @$items;
2754
2755 $self->{real_items} = \@items;
2756
2757 my $row = 0;
2758 for my $item (@items) {
2759 CFClient::Item::update_widgets $item;
2760
2761 $self->{scrolled}->add (0, $row, $item->{face_widget});
2762 $self->{scrolled}->add (1, $row, $item->{desc_widget});
2763 $self->{scrolled}->add (2, $row, $item->{weight_widget});
2764
2765 $row++;
2766 }
2767}
2768
2769sub size_request {
2770 my ($self) = @_;
2771 ($self->{req_w}, $self->{req_h});
2772}
2773
2774#############################################################################
2775
2776package CFClient::UI::Menu; 2859package CFClient::UI::Menu;
2777 2860
2778our @ISA = CFClient::UI::FancyFrame::; 2861our @ISA = CFClient::UI::FancyFrame::;
2779 2862
2780use CFClient::OpenGL; 2863use CFClient::OpenGL;
2818 # maybe save $GRAB? must be careful about events... 2901 # maybe save $GRAB? must be careful about events...
2819 $GRAB = $self; 2902 $GRAB = $self;
2820 $self->{button} = $ev->{button}; 2903 $self->{button} = $ev->{button};
2821 2904
2822 $self->show; 2905 $self->show;
2823 $self->move ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5); 2906 $self->move_abs ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5);
2824} 2907}
2825 2908
2826sub mouse_motion { 2909sub mouse_motion {
2827 my ($self, $ev, $x, $y) = @_; 2910 my ($self, $ev, $x, $y) = @_;
2828 2911
2829 # TODO: should use vbox->find_widget or so 2912 # TODO: should use vbox->find_widget or so
2830 $HOVER = $ROOT->find_widget ($ev->{x}, $ev->{y}); 2913 $HOVER = $ROOT->find_widget ($ev->{x}, $ev->{y});
2831 $self->{hover} = $self->{item}{$HOVER}; 2914 $self->{hover} = $self->{item}{$HOVER};
2915
2916 0
2832} 2917}
2833 2918
2834sub button_up { 2919sub button_up {
2835 my ($self, $ev, $x, $y) = @_; 2920 my ($self, $ev, $x, $y) = @_;
2836 2921
2838 undef $GRAB; 2923 undef $GRAB;
2839 $self->hide; 2924 $self->hide;
2840 2925
2841 $self->_emit ("popdown"); 2926 $self->_emit ("popdown");
2842 $self->{hover}[1]->() if $self->{hover}; 2927 $self->{hover}[1]->() if $self->{hover};
2928 } else {
2929 return 0
2930 }
2931
2843 } 2932 1
2844} 2933}
2845 2934
2846############################################################################# 2935#############################################################################
2847 2936
2848package CFClient::UI::Statusbox; 2937package CFClient::UI::Statusbox;
2953 $self->SUPER::reconfigure; 3042 $self->SUPER::reconfigure;
2954} 3043}
2955 3044
2956############################################################################# 3045#############################################################################
2957 3046
2958package CFClient::UI::Root; 3047package CFClient::UI::Inventory;
2959 3048
2960our @ISA = CFClient::UI::Container::; 3049our @ISA = CFClient::UI::ScrolledWindow::;
2961
2962use CFClient::OpenGL;
2963 3050
2964sub new { 3051sub new {
2965 my $class = shift; 3052 my $class = shift;
2966 3053
2967 $class->SUPER::new ( 3054 my $self = $class->SUPER::new (
3055 scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]),
2968 @_, 3056 @_,
2969 ) 3057 );
2970}
2971 3058
2972sub configure { 3059 $self
3060}
3061
3062sub set_items {
3063 my ($self, $items) = @_;
3064
3065 $self->{scrolled}->clear;
3066 return unless $items;
3067
3068 my @items = sort {
3069 ($a->{type} <=> $b->{type})
3070 or ($a->{name} cmp $b->{name})
3071 } @$items;
3072
3073 $self->{real_items} = \@items;
3074
3075 my $row = 0;
3076 for my $item (@items) {
3077 CFClient::Item::update_widgets $item;
3078
3079 $self->{scrolled}->add (0, $row, $item->{face_widget});
3080 $self->{scrolled}->add (1, $row, $item->{desc_widget});
3081 $self->{scrolled}->add (2, $row, $item->{weight_widget});
3082
3083 $row++;
3084 }
3085}
3086
3087#############################################################################
3088
3089package CFClient::UI::BindEditor;
3090
3091our @ISA = CFClient::UI::FancyFrame::;
3092
3093sub new {
3094 my $class = shift;
3095
3096 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3097
3098 $self->add (my $vb = new CFClient::UI::VBox);
3099
3100
3101 $vb->add ($self->{rec_btn} = new CFClient::UI::Button
3102 text => "start recording",
3103 tooltip => "Start/Stops recording of actions."
3104 ."All subsequent actions after the recording started will be captured."
3105 ."The actions are displayed after the record was stopped."
3106 ."To bind the action you have to click on the 'Bind' button",
3107 on_activate => sub {
3108 unless ($self->{recording}) {
3109 $self->start;
3110 } else {
3111 $self->stop;
3112 }
3113 });
3114
3115 $vb->add (new CFClient::UI::Label text => "Actions:");
3116 $vb->add ($self->{cmdbox} = new CFClient::UI::VBox);
3117
3118 $vb->add (new CFClient::UI::Label text => "Bound to: ");
3119 $vb->add (my $hb = new CFClient::UI::HBox);
3120 $hb->add ($self->{keylbl} = new CFClient::UI::Label expand => 1);
3121 $hb->add (new CFClient::UI::Button
3122 text => "bind",
3123 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
3124 on_activate => sub {
3125 $self->ask_for_bind;
3126 });
3127
3128 $vb->add (my $hb = new CFClient::UI::HBox);
3129 $hb->add (new CFClient::UI::Button
3130 text => "ok",
3131 expand => 1,
3132 tooltip => "This closes the binding editor and saves the binding",
3133 on_activate => sub {
3134 $self->hide;
3135 $self->commit;
3136 });
3137
3138 $hb->add (new CFClient::UI::Button
3139 text => "cancel",
3140 expand => 1,
3141 tooltip => "This closes the binding editor without saving",
3142 on_activate => sub {
3143 $self->hide;
3144 $self->{binding_cancel}->()
3145 if $self->{binding_cancel};
3146 });
3147
3148 $self->update_binding_widgets;
3149
3150 $self
3151}
3152
3153sub commit {
3154 my ($self) = @_;
3155 my ($mod, $sym, $cmds) = $self->get_binding;
3156 if ($sym != 0 && @$cmds > 0) {
3157 $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym)
3158 ."'. Don't forget 'Save Config'!");
3159 $self->{binding_change}->($mod, $sym, $cmds)
3160 if $self->{binding_change};
3161 } else {
3162 $::STATUSBOX->add ("No action bound, no key or action specified!");
3163 $self->{binding_cancel}->()
3164 if $self->{binding_cancel};
3165 }
3166}
3167
3168sub start {
3169 my ($self) = @_;
3170
3171 $self->{rec_btn}->set_text ("stop recording");
3172 $self->{recording} = 1;
3173 $self->clear_command_list;
3174 $::CONN->start_record if $::CONN;
3175}
3176
3177sub stop {
3178 my ($self) = @_;
3179
3180 $self->{rec_btn}->set_text ("start recording");
3181 $self->{recording} = 0;
3182
3183 my $rec;
3184 $rec = $::CONN->stop_record if $::CONN;
3185 return unless ref $rec eq 'ARRAY';
3186 $self->set_command_list ($rec);
3187}
3188
3189
3190sub ask_for_bind_and_commit {
3191 my ($self) = @_;
3192 $self->ask_for_bind (1);
3193}
3194
3195sub ask_for_bind {
3196 my ($self, $commit) = @_;
3197
3198 CFClient::Binder::open_binding_dialog (sub {
3199 my ($mod, $sym) = @_;
3200 $self->{binding} = [$mod, $sym]; # XXX: how to stop that memleak?
3201 $self->update_binding_widgets;
3202 $self->commit if $commit;
3203 });
3204}
3205
3206# $mod and $sym are the modifiers and key symbol
3207# $cmds is a array ref of strings (the commands)
3208# $cb is the callback that is executed on OK
3209# $ccb is the callback that is executed on CANCEL and
3210# when the binding was unsuccessful on OK
3211sub set_binding {
2973 my ($self, $x, $y, $w, $h) = @_; 3212 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
2974 3213
2975 $self->{w} = $w; 3214 $self->clear_command_list;
2976 $self->{h} = $h; 3215 $self->{recording} = 0;
2977} 3216 $self->{rec_btn}->set_text ("start recording");
2978 3217
2979sub check_size { 3218 $self->{binding} = [$mod, $sym];
3219 $self->{commands} = $cmds;
3220
3221 $self->{binding_change} = $cb;
3222 $self->{binding_cancel} = $ccb;
3223
3224 $self->update_binding_widgets;
3225}
3226
3227# this is a shortcut method that asks for a binding
3228# and then just binds it.
3229sub do_quick_binding {
2980 my ($self) = @_; 3230 my ($self, $cmds) = @_;
3231 $self->set_binding (undef, undef, $cmds, sub {
3232 $::CFG->{bindings}->{$_[0]}->{$_[1]} = $_[2];
3233 });
3234 $self->ask_for_bind (1);
3235}
2981 3236
2982 $self->size_allocate ($self->{w}, $self->{h}) 3237sub update_binding_widgets {
2983 if $self->{w}; 3238 my ($self) = @_;
3239 my ($mod, $sym, $cmds) = $self->get_binding;
3240 $self->{keylbl}->set_text (CFClient::Binder::keycombo_to_name ($mod, $sym));
3241 $self->set_command_list ($cmds);
3242}
3243
3244sub get_binding {
3245 my ($self) = @_;
3246 return (
3247 $self->{binding}->[0],
3248 $self->{binding}->[1],
3249 [ grep { defined $_ } @{$self->{commands}} ]
3250 );
3251}
3252
3253sub clear_command_list {
3254 my ($self) = @_;
3255 $self->{cmdbox}->clear ();
3256}
3257
3258sub set_command_list {
3259 my ($self, $cmds) = @_;
3260
3261 $self->{cmdbox}->clear ();
3262 $self->{commands} = $cmds;
3263
3264 my $idx = 0;
3265
3266 for (@$cmds) {
3267 $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox);
3268
3269 my $i = $idx;
3270 $hb->add (new CFClient::UI::Label text => $_);
3271 $hb->add (new CFClient::UI::Button
3272 text => "delete",
3273 tooltip => "Deletes the action from the record",
3274 on_activate => sub {
3275 $self->{cmdbox}->remove ($hb);
3276 $cmds->[$i] = undef;
3277 });
3278
3279
3280 $idx++
3281 }
3282}
3283
3284#############################################################################
3285
3286package CFClient::UI::SpellList;
3287
3288our @ISA = CFClient::UI::FancyFrame::;
3289
3290sub new {
3291 my $class = shift;
3292
3293 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3294
3295 $self->add (new CFClient::UI::ScrolledWindow
3296 scrolled => $self->{spellbox} = new CFClient::UI::Table);
3297
3298 $self;
3299}
3300
3301# XXX: Do sorting? Argl...
3302sub add_spell {
3303 my ($self, $spell) = @_;
3304 $self->{spells}->{$spell->{name}} = $spell;
3305
3306 $self->{spellbox}->add (0, $self->{tbl_idx}, new CFClient::UI::Face
3307 face => $spell->{face},
3308 can_hover => 1,
3309 can_events => 1,
3310 tooltip => $spell->{message});
3311
3312 $self->{spellbox}->add (1, $self->{tbl_idx}, new CFClient::UI::Label
3313 text => $spell->{name},
3314 can_hover => 1,
3315 can_events => 1,
3316 tooltip => $spell->{message},
3317 expand => 1);
3318
3319 $self->{spellbox}->add (2, $self->{tbl_idx}, new CFClient::UI::Label
3320 text => (sprintf "lvl: %2d sp: %2d dmg: %2d",
3321 $spell->{level}, ($spell->{mana} || $spell->{grace}), $spell->{damage}),
3322 expand => 1);
3323
3324 $self->{spellbox}->add (3, $self->{tbl_idx}++, new CFClient::UI::Button
3325 text => "bind to key",
3326 on_activate => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) });
3327}
3328
3329sub rebuild_spell_list {
3330 my ($self) = @_;
3331 $self->{tbl_idx} = 0;
3332 $self->add_spell ($_) for values %{$self->{spells}};
3333}
3334
3335sub remove_spell {
3336 my ($self, $spell) = @_;
3337 delete $self->{spells}->{$spell->{name}};
3338 $self->rebuild_spell_list;
3339}
3340
3341#############################################################################
3342
3343package CFClient::UI::Root;
3344
3345our @ISA = CFClient::UI::Container::;
3346
3347use CFClient::OpenGL;
3348
3349sub new {
3350 my $class = shift;
3351
3352 my $self = $class->SUPER::new (
3353 visible => 1,
3354 @_,
3355 );
3356
3357 Scalar::Util::weaken ($self->{root} = $self);
3358
3359 $self
2984} 3360}
2985 3361
2986sub size_request { 3362sub size_request {
2987 my ($self) = @_; 3363 my ($self) = @_;
2988 3364
2989 ($self->{w}, $self->{h}) 3365 ($self->{w}, $self->{h})
3366}
3367
3368sub _to_pixel {
3369 my ($coord, $size, $max) = @_;
3370
3371 $coord =
3372 $coord eq "center" ? ($max - $size) * 0.5
3373 : $coord eq "max" ? $max
3374 : $coord;
3375
3376 $coord = 0 if $coord < 0;
3377 $coord = $max - $size if $coord > $max - $size;
3378
3379 int $coord + 0.5
2990} 3380}
2991 3381
2992sub size_allocate { 3382sub size_allocate {
2993 my ($self, $w, $h) = @_; 3383 my ($self, $w, $h) = @_;
2994 3384
2995 for my $child ($self->children) { 3385 for my $child ($self->children) {
2996 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)}; 3386 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)};
2997 3387
2998 $X = $child->{req_x} > 0 ? $child->{req_x} : $w - $W - $child->{req_x} + 1 3388 $X = $child->{force_x} if exists $child->{force_x};
2999 if exists $child->{req_x}; 3389 $Y = $child->{force_y} if exists $child->{force_y};
3000 3390
3001 $Y = $child->{req_y} > 0 ? $child->{req_y} : $h - $H - $child->{req_y} + 1 3391 $X = _to_pixel $X, $W, $self->{w};
3002 if exists $child->{req_y}; 3392 $Y = _to_pixel $Y, $H, $self->{h};
3003
3004 $X = List::Util::max 0, List::Util::min $w - $W, int $X + 0.5;
3005 $Y = List::Util::max 0, List::Util::min $h - $H, int $Y + 0.5;
3006 3393
3007 $child->configure ($X, $Y, $W, $H); 3394 $child->configure ($X, $Y, $W, $H);
3008 } 3395 }
3009} 3396}
3010 3397
3021} 3408}
3022 3409
3023sub update { 3410sub update {
3024 my ($self) = @_; 3411 my ($self) = @_;
3025 3412
3026 $self->check_size;
3027 $::WANT_REFRESH++; 3413 $::WANT_REFRESH++;
3028} 3414}
3029 3415
3030sub add { 3416sub add {
3031 my ($self, @children) = @_; 3417 my ($self, @children) = @_;
3032 3418
3033 for (my @widgets = @children; my $w = pop @widgets; ) {
3034 push @widgets, $w->children;
3035 $w->{root} = $self;
3036 $w->{visible} = 1;
3037 }
3038
3039 for my $child (@children) {
3040 $child->{is_toplevel} = 1; 3419 $_->{is_toplevel} = 1
3041 3420 for @children;
3042 # integerise window positions
3043 $child->{x} = int $child->{x};
3044 $child->{y} = int $child->{y};
3045 }
3046 3421
3047 $self->SUPER::add (@children); 3422 $self->SUPER::add (@children);
3048} 3423}
3049 3424
3050sub remove { 3425sub remove {
3051 my ($self, @children) = @_; 3426 my ($self, @children) = @_;
3052 3427
3053 $self->SUPER::remove (@children); 3428 $self->SUPER::remove (@children);
3429
3430 delete $self->{is_toplevel}
3431 for @children;
3054 3432
3055 while (@children) { 3433 while (@children) {
3056 my $w = pop @children; 3434 my $w = pop @children;
3057 push @children, $w->children; 3435 push @children, $w->children;
3058 $w->set_invisible; 3436 $w->set_invisible;
3077 while ($self->{refresh_hook}) { 3455 while ($self->{refresh_hook}) {
3078 $_->() 3456 $_->()
3079 for values %{delete $self->{refresh_hook}}; 3457 for values %{delete $self->{refresh_hook}};
3080 } 3458 }
3081 3459
3082 if ($self->{check_size}) { 3460 if ($self->{realloc}) {
3083 my @queue = ([], []); 3461 my %queue;
3462 my @queue;
3463 my $widget;
3084 3464
3085 for (;;) { 3465 outer:
3086 if ($self->{check_size}) { 3466 while () {
3087 # heuristic: check containers last 3467 if (my $realloc = delete $self->{realloc}) {
3088 push @{ $queue[ ! ! $_->isa ("CFClient::UI::Container") ] }, $_ 3468 for $widget (values %$realloc) {
3089 for values %{delete $self->{check_size}} 3469 $widget->{visible} or next; # do not resize invisible widgets
3470
3471 $queue{$widget+0}++ and next; # duplicates are common
3472
3473 push @{ $queue[$widget->{visible}] }, $widget;
3474 }
3090 } 3475 }
3091 3476
3092 my $widget = (pop @{ $queue[0] }) || (pop @{ $queue[1] }) || last; 3477 while () {
3478 @queue or last outer;
3093 3479
3094 my ($w, $h) = $widget->{user_w} && $widget->{user_h} 3480 $widget = pop @{ $queue[-1] || [] }
3095 ? @$widget{qw(user_w user_h)} 3481 and last;
3096 : $widget->size_request;
3097
3098 if (delete $widget->{force_alloc}
3099 or $w != $widget->{req_w} or $h != $widget->{req_h}) {
3100 Carp::confess "$widget: size_request is negative" if $w < 0 || $h < 0;#d#
3101 3482
3483 pop @queue;
3484 }
3485
3486 delete $queue{$widget+0};
3487
3488 my ($w, $h) = $widget->size_request;
3489
3490 $w = List::Util::max $widget->{min_w}, $w + $widget->{padding_x} * 2;
3491 $h = List::Util::max $widget->{min_h}, $h + $widget->{padding_y} * 2;
3492
3493 $w = $widget->{force_w} if exists $widget->{force_w};
3494 $h = $widget->{force_h} if exists $widget->{force_h};
3495
3496 if ($widget->{req_w} != $w || $widget->{req_h} != $h
3497 || delete $widget->{force_realloc}) {
3102 $widget->{req_w} = $w; 3498 $widget->{req_w} = $w;
3103 $widget->{req_h} = $h; 3499 $widget->{req_h} = $h;
3104 3500
3105 $self->{size_alloc}{$widget} = [$widget, $widget->{w} || $w, $widget->{h} || $h]; 3501 $self->{size_alloc}{$widget+0} = $widget;
3106 3502
3107 $widget->{parent}->check_size
3108 if $widget->{parent}; 3503 if (my $parent = $widget->{parent}) {
3504 $self->{realloc}{$parent+0} = $parent
3505 unless $queue{$parent+0};
3506
3507 $parent->{force_size_alloc} = 1;
3508 $self->{size_alloc}{$parent+0} = $parent;
3509 }
3109 } 3510 }
3511
3512 delete $self->{realloc}{$widget+0};
3110 } 3513 }
3111 } 3514 }
3112 3515
3113 while ($self->{size_alloc}) { 3516 while (my $size_alloc = delete $self->{size_alloc}) {
3114 for (values %{delete $self->{size_alloc}}) { 3517 my @queue = sort { $b->{visible} <=> $a->{visible} }
3115 my ($widget, $w, $h) = @$_; 3518 values %$size_alloc;
3519
3520 while () {
3521 my $widget = pop @queue || last;
3522
3523 my ($w, $h) = @$widget{qw(alloc_w alloc_h)};
3116 3524
3117 $w = 0 if $w < 0; 3525 $w = 0 if $w < 0;
3118 $h = 0 if $h < 0; 3526 $h = 0 if $h < 0;
3119 3527
3528 $w = int $w + 0.5;
3529 $h = int $h + 0.5;
3530
3531 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) {
3532 $widget->{old_w} = $widget->{w};
3533 $widget->{old_h} = $widget->{h};
3534
3120 $widget->{w} = $w; 3535 $widget->{w} = $w;
3121 $widget->{h} = $h; 3536 $widget->{h} = $h;
3537
3122 $widget->emit (size_allocate => $w, $h); 3538 $widget->emit (size_allocate => $w, $h);
3539 }
3123 } 3540 }
3124 } 3541 }
3125 3542
3126 while ($self->{post_alloc_hook}) { 3543 while ($self->{post_alloc_hook}) {
3127 $_->() 3544 $_->()
3128 for values %{delete $self->{post_alloc_hook}}; 3545 for values %{delete $self->{post_alloc_hook}};
3129 } 3546 }
3547
3130 3548
3131 glViewport 0, 0, $::WIDTH, $::HEIGHT; 3549 glViewport 0, 0, $::WIDTH, $::HEIGHT;
3132 glClearColor +($::CFG->{fow_intensity}) x 3, 1; 3550 glClearColor +($::CFG->{fow_intensity}) x 3, 1;
3133 glClear GL_COLOR_BUFFER_BIT; 3551 glClear GL_COLOR_BUFFER_BIT;
3134 3552
3136 glLoadIdentity; 3554 glLoadIdentity;
3137 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000; 3555 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000;
3138 glMatrixMode GL_MODELVIEW; 3556 glMatrixMode GL_MODELVIEW;
3139 glLoadIdentity; 3557 glLoadIdentity;
3140 3558
3559 {
3560 package CFClient::UI::Base;
3561
3562 ($draw_x, $draw_y, $draw_w, $draw_h) =
3563 (0, 0, $self->{w}, $self->{h});
3564 }
3565
3141 $self->_draw; 3566 $self->_draw;
3142} 3567}
3143 3568
3144############################################################################# 3569#############################################################################
3145 3570

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines