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.302 by root, Mon Jun 12 13:26:14 2006 UTC vs.
Revision 1.442 by root, Mon Oct 8 17:01:03 2007 UTC

1package CFClient::UI; 1package CFPlus::UI;
2 2
3use utf8; 3use utf8;
4use strict; 4use strict;
5 5
6use Scalar::Util ();
7use List::Util (); 6use List::Util ();
8use Event; 7use Event;
9 8
10use CFClient; 9use CFPlus;
10use CFPlus::Pod;
11use CFClient::Texture; 11use CFPlus::Texture;
12 12
13our ($FOCUS, $HOVER, $GRAB); # various widgets 13our ($FOCUS, $HOVER, $GRAB); # various widgets
14 14
15our $LAYOUT; 15our $LAYOUT;
16our $ROOT; 16our $ROOT;
22our $TOOLTIP_WATCHER = Event->idle (min => 1/60, cb => sub { 22our $TOOLTIP_WATCHER = Event->idle (min => 1/60, cb => sub {
23 if (!$GRAB) { 23 if (!$GRAB) {
24 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) { 24 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) {
25 if (length $widget->{tooltip}) { 25 if (length $widget->{tooltip}) {
26 if ($TOOLTIP->{owner} != $widget) { 26 if ($TOOLTIP->{owner} != $widget) {
27 $TOOLTIP->{owner}->emit ("tooltip_hide") if $TOOLTIP->{owner};
27 $TOOLTIP->hide; 28 $TOOLTIP->hide;
28 29
29 $TOOLTIP->{owner} = $widget; 30 $TOOLTIP->{owner} = $widget;
31 $TOOLTIP->{owner}->emit ("tooltip_show") if $TOOLTIP->{owner};
30 32
31 return if $ENV{CFPLUS_DEBUG} & 8; 33 return if $ENV{CFPLUS_DEBUG} & 8;
32 34
33 my $tip = $widget->{tooltip};
34
35 $tip = $tip->($widget) if CODE:: eq ref $tip;
36
37 $TOOLTIP->set_tooltip_from ($widget); 35 $TOOLTIP->set_tooltip_from ($widget);
38 $TOOLTIP->show; 36 $TOOLTIP->show;
39 } 37 }
40 38
41 return; 39 return;
42 } 40 }
43 } 41 }
44 } 42 }
45 43
46 $TOOLTIP->hide; 44 $TOOLTIP->hide;
45 $TOOLTIP->{owner}->emit ("tooltip_hide") if $TOOLTIP->{owner};
47 delete $TOOLTIP->{owner}; 46 delete $TOOLTIP->{owner};
48}); 47});
49 48
50sub get_layout { 49sub get_layout {
51 my $layout; 50 my $layout;
79sub feed_sdl_key_up_event { 78sub feed_sdl_key_up_event {
80 $FOCUS->emit (key_up => $_[0]) 79 $FOCUS->emit (key_up => $_[0])
81 if $FOCUS; 80 if $FOCUS;
82} 81}
83 82
83sub check_hover {
84 my ($widget) = @_;
85
86 if ($widget != $HOVER) {
87 my $hover = $HOVER; $HOVER = $widget;
88
89 $hover->update if $hover && $hover->{can_hover};
90 $HOVER->update if $HOVER && $HOVER->{can_hover};
91
92 $TOOLTIP_WATCHER->start;
93 }
94}
95
84sub feed_sdl_button_down_event { 96sub feed_sdl_button_down_event {
85 my ($ev) = @_; 97 my ($ev) = @_;
86 my ($x, $y) = ($ev->{x}, $ev->{y}); 98 my ($x, $y) = ($ev->{x}, $ev->{y});
87 99
88 if (!$BUTTON_STATE) { 100 $BUTTON_STATE |= 1 << ($ev->{button} - 1);
101
102 unless ($GRAB) {
89 my $widget = $ROOT->find_widget ($x, $y); 103 my $widget = $ROOT->find_widget ($x, $y);
90 104
91 $GRAB = $widget; 105 $GRAB = $widget;
92 $GRAB->update if $GRAB; 106 $GRAB->update if $GRAB;
93 107
94 $TOOLTIP_WATCHER->cb->(); 108 $TOOLTIP_WATCHER->cb->();
95 } 109 }
96 110
97 $BUTTON_STATE |= 1 << ($ev->{button} - 1); 111 if ($GRAB) {
112 if ($ev->{button} == 4 || $ev->{button} == 5) {
113 # mousewheel
114 my $delta = $ev->{button} * 2 - 9;
115 my $shift = $ev->{mod} & CFPlus::KMOD_SHIFT;
98 116
99 $GRAB->emit (button_down => $ev, $GRAB->coord2local ($x, $y)) 117 $ev->{dx} = $shift ? $delta : 0;
100 if $GRAB; 118 $ev->{dy} = $shift ? 0 : $delta;
119
120 $GRAB->emit (mouse_wheel => $ev);
121 } else {
122 $GRAB->emit (button_down => $ev)
123 }
124 }
101} 125}
102 126
103sub feed_sdl_button_up_event { 127sub feed_sdl_button_up_event {
104 my ($ev) = @_; 128 my ($ev) = @_;
105 my ($x, $y) = ($ev->{x}, $ev->{y});
106 129
107 my $widget = $GRAB || $ROOT->find_widget ($x, $y); 130 my $widget = $GRAB || $ROOT->find_widget ($ev->{x}, $ev->{y});
108 131
109 $BUTTON_STATE &= ~(1 << ($ev->{button} - 1)); 132 $BUTTON_STATE &= ~(1 << ($ev->{button} - 1));
110 133
111 $GRAB->emit (button_up => $ev, $GRAB->coord2local ($x, $y)) 134 $GRAB->emit (button_up => $ev)
112 if $GRAB; 135 if $GRAB && $ev->{button} != 4 && $ev->{button} != 5;
113 136
114 if (!$BUTTON_STATE) { 137 unless ($BUTTON_STATE) {
115 my $grab = $GRAB; undef $GRAB; 138 my $grab = $GRAB; undef $GRAB;
116 $grab->update if $grab; 139 $grab->update if $grab;
117 $GRAB->update if $GRAB; 140 $GRAB->update if $GRAB;
118 141
142 check_hover $widget;
119 $TOOLTIP_WATCHER->cb->(); 143 $TOOLTIP_WATCHER->cb->();
120 } 144 }
121} 145}
122 146
123sub feed_sdl_motion_event { 147sub feed_sdl_motion_event {
124 my ($ev) = @_; 148 my ($ev) = @_;
125 my ($x, $y) = ($ev->{x}, $ev->{y}); 149 my ($x, $y) = ($ev->{x}, $ev->{y});
126 150
127 my $widget = $GRAB || $ROOT->find_widget ($x, $y); 151 my $widget = $GRAB || $ROOT->find_widget ($x, $y);
128 152
129 if ($widget != $HOVER) { 153 check_hover $widget;
130 my $hover = $HOVER; $HOVER = $widget;
131 154
132 $hover->update if $hover && $hover->{can_hover}; 155 $HOVER->emit (mouse_motion => $ev)
133 $HOVER->update if $HOVER && $HOVER->{can_hover};
134
135 $TOOLTIP_WATCHER->start;
136 }
137
138 $HOVER->emit (mouse_motion => $ev, $HOVER->coord2local ($x, $y))
139 if $HOVER; 156 if $HOVER;
140} 157}
141 158
142# convert position array to integers 159# convert position array to integers
143sub harmonize { 160sub harmonize {
193 reconfigure_widgets; 210 reconfigure_widgets;
194} 211}
195 212
196############################################################################# 213#############################################################################
197 214
215package CFPlus::UI::Event;
216
217sub xy {
218 $_[1]->coord2local ($_[0]{x}, $_[0]{y})
219}
220
221#############################################################################
222
198package CFClient::UI::Base; 223package CFPlus::UI::Base;
199 224
200use strict; 225use strict;
201 226
202use CFClient::OpenGL; 227use CFPlus::OpenGL;
203 228
204sub new { 229sub new {
205 my $class = shift; 230 my $class = shift;
206 231
207 my $self = bless { 232 my $self = bless {
212 h => undef, 237 h => undef,
213 can_events => 1, 238 can_events => 1,
214 @_ 239 @_
215 }, $class; 240 }, $class;
216 241
217 Scalar::Util::weaken ($CFClient::UI::WIDGET{$self+0} = $self); 242 CFPlus::weaken ($CFPlus::UI::WIDGET{$self+0} = $self);
218 243
219 for (keys %$self) { 244 for (keys %$self) {
220 if (/^on_(.*)$/) { 245 if (/^on_(.*)$/) {
221 $self->connect ($1 => delete $self->{$_}); 246 $self->connect ($1 => delete $self->{$_});
222 } 247 }
223 } 248 }
224 249
225 if (my $layout = $CFClient::UI::LAYOUT->{$self->{name}}) { 250 if (my $layout = $CFPlus::UI::LAYOUT->{$self->{name}}) {
226 $self->{x} = $layout->{x} * $CFClient::UI::ROOT->{alloc_w} if exists $layout->{x}; 251 $self->{x} = $layout->{x} * $CFPlus::UI::ROOT->{alloc_w} if exists $layout->{x};
227 $self->{y} = $layout->{y} * $CFClient::UI::ROOT->{alloc_h} if exists $layout->{y}; 252 $self->{y} = $layout->{y} * $CFPlus::UI::ROOT->{alloc_h} if exists $layout->{y};
228 $self->{force_w} = $layout->{w} * $CFClient::UI::ROOT->{alloc_w} if exists $layout->{w}; 253 $self->{force_w} = $layout->{w} * $CFPlus::UI::ROOT->{alloc_w} if exists $layout->{w};
229 $self->{force_h} = $layout->{h} * $CFClient::UI::ROOT->{alloc_h} if exists $layout->{h}; 254 $self->{force_h} = $layout->{h} * $CFPlus::UI::ROOT->{alloc_h} if exists $layout->{h};
230 255
231 $self->{x} -= $self->{force_w} * 0.5 if exists $layout->{x}; 256 $self->{x} -= $self->{force_w} * 0.5 if exists $layout->{x};
232 $self->{y} -= $self->{force_h} * 0.5 if exists $layout->{y}; 257 $self->{y} -= $self->{force_h} * 0.5 if exists $layout->{y};
233 258
234 $self->show if $layout->{show}; 259 $self->show if $layout->{show};
239 264
240sub destroy { 265sub destroy {
241 my ($self) = @_; 266 my ($self) = @_;
242 267
243 $self->hide; 268 $self->hide;
269 $self->emit ("destroy");
244 %$self = (); 270 %$self = ();
245} 271}
246 272
273sub TO_JSON {
274 { "\fw" => $_[0]{s_id} }
275}
276
247sub show { 277sub show {
248 my ($self) = @_; 278 my ($self) = @_;
249 279
250 return if $self->{parent}; 280 return if $self->{parent};
251 281
252 $CFClient::UI::ROOT->add ($self); 282 $CFPlus::UI::ROOT->add ($self);
253} 283}
254 284
255sub set_visible { 285sub set_visible {
256 my ($self) = @_; 286 my ($self) = @_;
257 287
278 delete $self->{root}; 308 delete $self->{root};
279 309
280 undef $GRAB if $GRAB == $self; 310 undef $GRAB if $GRAB == $self;
281 undef $HOVER if $HOVER == $self; 311 undef $HOVER if $HOVER == $self;
282 312
283 $CFClient::UI::TOOLTIP_WATCHER->cb->() 313 $CFPlus::UI::TOOLTIP_WATCHER->cb->()
284 if $TOOLTIP->{owner} == $self; 314 if $TOOLTIP->{owner} == $self;
285 315
286 $self->focus_out; 316 $self->emit ("focus_out");
287
288 $self->emit (visibility_change => 0); 317 $self->emit (visibility_change => 0);
289} 318}
290 319
291sub set_visibility { 320sub set_visibility {
292 my ($self, $visible) = @_; 321 my ($self, $visible) = @_;
293 322
294 return if $self->{visible} == $visible; 323 return if $self->{visible} == $visible;
295 324
296 $visible ? $self->hide 325 $visible ? $self->show
297 : $self->show; 326 : $self->hide;
298} 327}
299 328
300sub toggle_visibility { 329sub toggle_visibility {
301 my ($self) = @_; 330 my ($self) = @_;
302 331
331 $self->{force_h} = $h; 360 $self->{force_h} = $h;
332 361
333 $self->realloc; 362 $self->realloc;
334} 363}
335 364
365# traverse the widget chain up to find the maximum "physical" size constraints
366sub get_max_wh {
367 my ($self) = @_;
368
369 return $self->{parent}->get_max_wh
370 if $self->{parent};
371
372 ($::WIDTH, $::HEIGHT)
373}
374
336sub size_request { 375sub size_request {
337 require Carp; 376 require Carp;
338 Carp::confess "size_request is abstract"; 377 Carp::confess "size_request is abstract";
339} 378}
340 379
380sub baseline_shift {
381 0
382}
383
341sub configure { 384sub configure {
342 my ($self, $x, $y, $w, $h) = @_; 385 my ($self, $x, $y, $w, $h) = @_;
343 386
344 if ($self->{aspect}) { 387 if ($self->{aspect}) {
345 my ($ow, $oh) = ($w, $h); 388 my ($ow, $oh) = ($w, $h);
346 389
347 $w = List::Util::min $w, int $h * $self->{aspect}; 390 $w = List::Util::min $w, CFPlus::ceil $h * $self->{aspect};
348 $h = List::Util::min $h, int $w / $self->{aspect}; 391 $h = List::Util::min $h, CFPlus::ceil $w / $self->{aspect};
349 392
350 # use alignment to adjust x, y 393 # use alignment to adjust x, y
351 394
352 $x += int 0.5 * ($ow - $w); 395 $x += int 0.5 * ($ow - $w);
353 $y += int 0.5 * ($oh - $h); 396 $y += int 0.5 * ($oh - $h);
367 410
368 $self->{root}{size_alloc}{$self+0} = $self; 411 $self->{root}{size_alloc}{$self+0} = $self;
369 } 412 }
370} 413}
371 414
372sub size_allocate {
373 # nothing to be done
374}
375
376sub children { 415sub children {
377 # nop 416 # nop
378} 417}
379 418
380sub visible_children { 419sub visible_children {
398 437
399 return if $self->{tooltip} eq $tooltip; 438 return if $self->{tooltip} eq $tooltip;
400 439
401 $self->{tooltip} = $tooltip; 440 $self->{tooltip} = $tooltip;
402 441
403 if ($CFClient::UI::TOOLTIP->{owner} == $self) { 442 if ($CFPlus::UI::TOOLTIP->{owner} == $self) {
404 delete $CFClient::UI::TOOLTIP->{owner}; 443 delete $CFPlus::UI::TOOLTIP->{owner};
405 $CFClient::UI::TOOLTIP_WATCHER->cb->(); 444 $CFPlus::UI::TOOLTIP_WATCHER->cb->();
406 } 445 }
407} 446}
408 447
409# translate global coordinates to local coordinate system 448# translate global coordinates to local coordinate system
410sub coord2local { 449sub coord2local {
411 my ($self, $x, $y) = @_; 450 my ($self, $x, $y) = @_;
412 451
452 Carp::confess unless $self->{parent};#d#
453
413 $self->{parent}->coord2local ($x - $self->{x}, $y - $self->{y}) 454 $self->{parent}->coord2local ($x - $self->{x}, $y - $self->{y})
414} 455}
415 456
416# translate local coordinates to global coordinate system 457# translate local coordinates to global coordinate system
417sub coord2global { 458sub coord2global {
418 my ($self, $x, $y) = @_; 459 my ($self, $x, $y) = @_;
419 460
461 Carp::confess unless $self->{parent};#d#
462
420 $self->{parent}->coord2global ($x + $self->{x}, $y + $self->{y}) 463 $self->{parent}->coord2global ($x + $self->{x}, $y + $self->{y})
421} 464}
422 465
423sub focus_in { 466sub invoke_focus_in {
424 my ($self) = @_; 467 my ($self) = @_;
425 468
426 return if $FOCUS == $self; 469 return if $FOCUS == $self;
427 return unless $self->{can_focus}; 470 return unless $self->{can_focus};
428 471
429 my $focus = $FOCUS; $FOCUS = $self; 472 $FOCUS = $self;
430 473
431 $self->_emit (focus_in => $focus); 474 $self->update;
432 475
433 $focus->update if $focus; 476 0
434 $FOCUS->update;
435} 477}
436 478
437sub focus_out { 479sub invoke_focus_out {
438 my ($self) = @_; 480 my ($self) = @_;
439 481
440 return unless $FOCUS == $self; 482 return unless $FOCUS == $self;
441 483
442 my $focus = $FOCUS; undef $FOCUS; 484 undef $FOCUS;
443 485
444 $self->_emit (focus_out => $focus); 486 $self->update;
445 487
446 $focus->update if $focus; #?
447
448 $::MAPWIDGET->focus_in #d# focus mapwidget if no other widget has focus 488 $::MAPWIDGET->grab_focus #d# focus mapwidget if no other widget has focus
449 unless $FOCUS; 489 unless $FOCUS;
450}
451 490
491 0
492}
493
494sub grab_focus {
495 my ($self) = @_;
496
497 $FOCUS->emit ("focus_out") if $FOCUS;
498 $self->emit ("focus_in");
499}
500
452sub mouse_motion { 0 } 501sub invoke_mouse_motion { 0 }
453sub button_up { 0 } 502sub invoke_button_up { 0 }
454sub key_down { 0 } 503sub invoke_key_down { 0 }
455sub key_up { 0 } 504sub invoke_key_up { 0 }
505sub invoke_mouse_wheel { 0 }
456 506
457sub button_down { 507sub invoke_button_down {
458 my ($self, $ev, $x, $y) = @_; 508 my ($self, $ev, $x, $y) = @_;
459 509
460 $self->focus_in; 510 $self->grab_focus;
461 511
462 0 512 0
463} 513}
464 514
515sub connect {
516 my ($self, $signal, $cb) = @_;
517
518 push @{ $self->{signal_cb}{$signal} }, $cb;
519
520 defined wantarray and CFPlus::guard {
521 @{ $self->{signal_cb}{$signal} } = grep $_ != $cb,
522 @{ $self->{signal_cb}{$signal} };
523 }
524}
525
526sub disconnect_all {
527 my ($self, $signal) = @_;
528
529 delete $self->{signal_cb}{$signal};
530}
531
532my %has_coords = (
533 button_down => 1,
534 button_up => 1,
535 mouse_motion => 1,
536 mouse_wheel => 1,
537);
538
539sub emit {
540 my ($self, $signal, @args) = @_;
541
542 # I do not really like this solution, but I do not like duplication
543 # and needlessly verbose code, either.
544 my @append
545 = $has_coords{$signal}
546 ? $args[0]->xy ($self)
547 : ();
548
549 #warn +(caller(1))[3] . "emit $signal on $self (parent $self->{parent})\n";#d#
550
551 for my $cb (
552 @{$self->{signal_cb}{$signal} || []}, # before
553 ($self->can ("invoke_$signal") || sub { 1 }), # closure
554 ) {
555 return $cb->($self, @args, @append) || next;
556 }
557
558 # parent
559 $self->{parent} && $self->{parent}->emit ($signal, @args)
560}
561
465sub find_widget { 562#sub find_widget {
466 my ($self, $x, $y) = @_; 563# in .xs
467
468 return () unless $self->{can_events};
469
470 return $self
471 if $x >= $self->{x} && $x < $self->{x} + $self->{w}
472 && $y >= $self->{y} && $y < $self->{y} + $self->{h};
473
474 ()
475}
476 564
477sub set_parent { 565sub set_parent {
478 my ($self, $parent) = @_; 566 my ($self, $parent) = @_;
479 567
480 Scalar::Util::weaken ($self->{parent} = $parent); 568 CFPlus::weaken ($self->{parent} = $parent);
481 $self->set_visible if $parent->{visible}; 569 $self->set_visible if $parent->{visible};
482}
483
484sub connect {
485 my ($self, $signal, $cb) = @_;
486
487 push @{ $self->{signal_cb}{$signal} }, $cb;
488}
489
490sub _emit {
491 my ($self, $signal, @args) = @_;
492
493 List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}
494}
495
496sub emit {
497 my ($self, $signal, @args) = @_;
498
499 $self->_emit ($signal, @args)
500 || $self->$signal (@args);
501}
502
503sub visibility_change {
504 #my ($self, $visible) = @_;
505} 570}
506 571
507sub realloc { 572sub realloc {
508 my ($self) = @_; 573 my ($self) = @_;
509 574
534 599
535# using global variables seems a bit hacky, but passing through all drawing 600# using global variables seems a bit hacky, but passing through all drawing
536# functions seems pointless. 601# functions seems pointless.
537our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn 602our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn
538 603
539sub draw { 604#sub draw {
540 my ($self) = @_; 605#CFPlus.xs
541
542 return unless $self->{h} && $self->{w};
543
544 # update screen rectangle
545 local $draw_x = $draw_x + $self->{x};
546 local $draw_y = $draw_y + $self->{y};
547 local $draw_w = $draw_x + $self->{w};
548 local $draw_h = $draw_y + $self->{h};
549
550 # skip widgets that are entirely outside the drawing area
551 return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w)
552 || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h);
553
554 glPushMatrix;
555 glTranslate $self->{x}, $self->{y}, 0;
556
557 if ($self == $HOVER && $self->{can_hover}) {
558 glColor 1*0.2, 0.8*0.2, 0.5*0.2, 0.2;
559 glEnable GL_BLEND;
560 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
561 glBegin GL_QUADS;
562 glVertex 0 , 0;
563 glVertex $self->{w}, 0;
564 glVertex $self->{w}, $self->{h};
565 glVertex 0 , $self->{h};
566 glEnd;
567 glDisable GL_BLEND;
568 }
569
570 if ($ENV{CFPLUS_DEBUG} & 1) {
571 glPushMatrix;
572 glColor 1, 1, 0, 1;
573 glTranslate 0.375, 0.375;
574 glBegin GL_LINE_LOOP;
575 glVertex 0 , 0;
576 glVertex $self->{w} - 1, 0;
577 glVertex $self->{w} - 1, $self->{h} - 1;
578 glVertex 0 , $self->{h} - 1;
579 glEnd;
580 glPopMatrix;
581 #CFClient::UI::Label->new (w => $self->{w}, h => $self->{h}, text => $self, fontsize => 0)->_draw;
582 }
583
584 $self->_draw;
585 glPopMatrix;
586}
587 606
588sub _draw { 607sub _draw {
589 my ($self) = @_; 608 my ($self) = @_;
590 609
591 warn "no draw defined for $self\n"; 610 warn "no draw defined for $self\n";
592} 611}
593 612
594sub DESTROY { 613sub DESTROY {
595 my ($self) = @_; 614 my ($self) = @_;
596 615
597 delete $WIDGET{$self+0}; 616 return if CFPlus::in_destruct;
598 617
618 local $@;
599 eval { $self->destroy }; 619 eval { $self->destroy };
600 warn "exception during widget destruction: $@" if $@ & $@ != /during global destruction/; 620 warn "exception during widget destruction: $@" if $@ & $@ != /during global destruction/;
621
622 delete $WIDGET{$self+0};
601} 623}
602 624
603############################################################################# 625#############################################################################
604 626
605package CFClient::UI::DrawBG; 627package CFPlus::UI::DrawBG;
606 628
607our @ISA = CFClient::UI::Base::; 629our @ISA = CFPlus::UI::Base::;
608 630
609use strict; 631use strict;
610use CFClient::OpenGL; 632use CFPlus::OpenGL;
611 633
612sub new { 634sub new {
613 my $class = shift; 635 my $class = shift;
614
615 # range [value, low, high, page]
616 636
617 $class->SUPER::new ( 637 $class->SUPER::new (
618 #bg => [0, 0, 0, 0.2], 638 #bg => [0, 0, 0, 0.2],
619 #active_bg => [1, 1, 1, 0.5], 639 #active_bg => [1, 1, 1, 0.5],
620 @_ 640 @_
632 my ($w, $h) = @$self{qw(w h)}; 652 my ($w, $h) = @$self{qw(w h)};
633 653
634 glEnable GL_BLEND; 654 glEnable GL_BLEND;
635 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; 655 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
636 glColor_premultiply @$color; 656 glColor_premultiply @$color;
637
638 glBegin GL_QUADS;
639 glVertex 0 , 0;
640 glVertex 0 , $h;
641 glVertex $w, $h; 657 glRect 0, 0, $w, $h;
642 glVertex $w, 0;
643 glEnd;
644
645 glDisable GL_BLEND; 658 glDisable GL_BLEND;
646 } 659 }
647} 660}
648 661
649############################################################################# 662#############################################################################
650 663
651package CFClient::UI::Empty; 664package CFPlus::UI::Empty;
652 665
653our @ISA = CFClient::UI::Base::; 666our @ISA = CFPlus::UI::Base::;
654 667
655sub new { 668sub new {
656 my ($class, %arg) = @_; 669 my ($class, %arg) = @_;
657 $class->SUPER::new (can_events => 0, %arg); 670 $class->SUPER::new (can_events => 0, %arg);
658} 671}
665 678
666sub draw { } 679sub draw { }
667 680
668############################################################################# 681#############################################################################
669 682
670package CFClient::UI::Container; 683package CFPlus::UI::Container;
671 684
672our @ISA = CFClient::UI::Base::; 685our @ISA = CFPlus::UI::Base::;
673 686
674sub new { 687sub new {
675 my ($class, %arg) = @_; 688 my ($class, %arg) = @_;
676 689
677 my $children = delete $arg{children}; 690 my $children = delete $arg{children};
681 can_events => 0, 694 can_events => 0,
682 %arg, 695 %arg,
683 ); 696 );
684 697
685 $self->add (@$children) 698 $self->add (@$children)
686 if $children; 699 if $children && @$children;
687 700
688 $self 701 $self
702}
703
704sub realloc {
705 my ($self) = @_;
706
707 $self->{force_realloc} = 1;
708 $self->{force_size_alloc} = 1;
709 $self->SUPER::realloc;
689} 710}
690 711
691sub add { 712sub add {
692 my ($self, @widgets) = @_; 713 my ($self, @widgets) = @_;
693 714
694 $_->set_parent ($self) 715 $_->set_parent ($self)
695 for @widgets; 716 for @widgets;
696 717
718 # TODO: only do this in widgets that need it, e.g. root, fixed
697 use sort 'stable'; 719 use sort 'stable';
698 720
699 $self->{children} = [ 721 $self->{children} = [
700 sort { $a->{z} <=> $b->{z} } 722 sort { $a->{z} <=> $b->{z} }
701 @{$self->{children}}, @widgets 723 @{$self->{children}}, @widgets
702 ]; 724 ];
703 725
704 $self->realloc; 726 $self->realloc;
727
728 $self->emit (c_add => \@widgets);
729
730 map $_+0, @widgets
705} 731}
706 732
707sub children { 733sub children {
708 @{ $_[0]{children} } 734 @{ $_[0]{children} }
709} 735}
710 736
711sub remove { 737sub remove {
712 my ($self, $child) = @_; 738 my ($self, @widgets) = @_;
713 739
740 $self->emit (c_remove => \@widgets);
741
742 for my $child (@widgets) {
714 delete $child->{parent}; 743 delete $child->{parent};
715 $child->hide; 744 $child->hide;
716
717 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; 745 $self->{children} = [ grep $_ != $child, @{ $self->{children} } ];
746 }
718 747
719 $self->realloc; 748 $self->realloc;
720} 749}
721 750
722sub clear { 751sub clear {
723 my ($self) = @_; 752 my ($self) = @_;
724 753
725 my $children = delete $self->{children}; 754 my $children = $self->{children};
726 $self->{children} = []; 755 $self->{children} = [];
727 756
728 for (@$children) { 757 for (@$children) {
729 delete $_->{parent}; 758 delete $_->{parent};
730 $_->hide; 759 $_->hide;
750} 779}
751 780
752sub _draw { 781sub _draw {
753 my ($self) = @_; 782 my ($self) = @_;
754 783
755 $_->draw for @{$self->{children}}; 784 $_->draw for $self->visible_children;
756} 785}
757 786
758############################################################################# 787#############################################################################
759 788
760package CFClient::UI::Bin; 789package CFPlus::UI::Bin;
761 790
762our @ISA = CFClient::UI::Container::; 791our @ISA = CFPlus::UI::Container::;
763 792
764sub new { 793sub new {
765 my ($class, %arg) = @_; 794 my ($class, %arg) = @_;
766 795
767 my $child = (delete $arg{child}) || new CFClient::UI::Empty::; 796 my $child = (delete $arg{child}) || new CFPlus::UI::Empty::;
768 797
769 $class->SUPER::new (children => [$child], %arg) 798 $class->SUPER::new (children => [$child], %arg)
770} 799}
771 800
772sub add { 801sub add {
773 my ($self, $child) = @_; 802 my ($self, $child) = @_;
774 803
775 $self->SUPER::remove ($_) for @{ $self->{children} }; 804 $self->clear;
776 $self->SUPER::add ($child); 805 $self->SUPER::add ($child);
777} 806}
778 807
779sub remove { 808sub remove {
780 my ($self, $widget) = @_; 809 my ($self, $widget) = @_;
781 810
782 $self->SUPER::remove ($widget); 811 $self->SUPER::remove ($widget);
783 812
784 $self->{children} = [new CFClient::UI::Empty] 813 $self->{children} = [new CFPlus::UI::Empty]
785 unless @{$self->{children}}; 814 unless @{$self->{children}};
786} 815}
787 816
788sub child { $_[0]->{children}[0] } 817sub child { $_[0]->{children}[0] }
789 818
790sub size_request { 819sub size_request {
791 $_[0]{children}[0]->size_request 820 $_[0]{children}[0]->size_request
792} 821}
793 822
794sub size_allocate { 823sub invoke_size_allocate {
795 my ($self, $w, $h) = @_; 824 my ($self, $w, $h) = @_;
796 825
797 $self->{children}[0]->configure (0, 0, $w, $h); 826 $self->{children}[0]->configure (0, 0, $w, $h);
827
828 1
798} 829}
799 830
800############################################################################# 831#############################################################################
801
802# back-buffered drawing area 832# back-buffered drawing area
803 833
804package CFClient::UI::Window; 834package CFPlus::UI::Window;
805 835
806our @ISA = CFClient::UI::Bin::; 836our @ISA = CFPlus::UI::Bin::;
807 837
808use CFClient::OpenGL; 838use CFPlus::OpenGL;
809 839
810sub new { 840sub new {
811 my ($class, %arg) = @_; 841 my ($class, %arg) = @_;
812 842
813 my $self = $class->SUPER::new (%arg); 843 my $self = $class->SUPER::new (%arg);
818 848
819 $ROOT->on_post_alloc ($self => sub { $self->render_child }); 849 $ROOT->on_post_alloc ($self => sub { $self->render_child });
820 $self->SUPER::update; 850 $self->SUPER::update;
821} 851}
822 852
823sub size_allocate { 853sub invoke_size_allocate {
824 my ($self, $w, $h) = @_; 854 my ($self, $w, $h) = @_;
825 855
826 $self->SUPER::size_allocate ($w, $h);
827 $self->update; 856 $self->update;
857
858 $self->SUPER::invoke_size_allocate ($w, $h)
828} 859}
829 860
830sub _render { 861sub _render {
831 my ($self) = @_; 862 my ($self) = @_;
832 863
834} 865}
835 866
836sub render_child { 867sub render_child {
837 my ($self) = @_; 868 my ($self) = @_;
838 869
839 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub { 870 $self->{texture} = new_from_opengl CFPlus::Texture $self->{w}, $self->{h}, sub {
840 glClearColor 0, 0, 0, 0; 871 glClearColor 0, 0, 0, 0;
841 glClear GL_COLOR_BUFFER_BIT; 872 glClear GL_COLOR_BUFFER_BIT;
842 873
843 { 874 {
844 package CFClient::UI::Base; 875 package CFPlus::UI::Base;
845 876
846 ($draw_x, $draw_y, $draw_w, $draw_h) = 877 local ($draw_x, $draw_y, $draw_w, $draw_h) =
847 (0, 0, $self->{w}, $self->{h}); 878 (0, 0, $self->{w}, $self->{h});
879
880 $self->_render;
848 } 881 }
849
850 $self->_render;
851 }; 882 };
852} 883}
853 884
854sub _draw { 885sub _draw {
855 my ($self) = @_; 886 my ($self) = @_;
856
857 my ($w, $h) = @$self{qw(w h)};
858 887
859 my $tex = $self->{texture} 888 my $tex = $self->{texture}
860 or return; 889 or return;
861 890
862 glEnable GL_TEXTURE_2D; 891 glEnable GL_TEXTURE_2D;
863 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; 892 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
864 glColor 0, 0, 0, 1; 893 glColor 0, 0, 0, 1;
865 894
866 $tex->draw_quad_alpha_premultiplied (0, 0, $w, $h); 895 $tex->draw_quad_alpha_premultiplied (0, 0);
867 896
868 glDisable GL_TEXTURE_2D; 897 glDisable GL_TEXTURE_2D;
869} 898}
870 899
871############################################################################# 900#############################################################################
872 901
873package CFClient::UI::ViewPort; 902package CFPlus::UI::ViewPort;
874 903
904use List::Util qw(min max);
905
875our @ISA = CFClient::UI::Window::; 906our @ISA = CFPlus::UI::Window::;
876 907
877sub new { 908sub new {
878 my $class = shift; 909 my $class = shift;
879 910
880 $class->SUPER::new ( 911 $class->SUPER::new (
887sub size_request { 918sub size_request {
888 my ($self) = @_; 919 my ($self) = @_;
889 920
890 my ($w, $h) = @{$self->child}{qw(req_w req_h)}; 921 my ($w, $h) = @{$self->child}{qw(req_w req_h)};
891 922
892 $w = 10 if $self->{scroll_x}; 923 $w = 1 if $self->{scroll_x};
893 $h = 10 if $self->{scroll_y}; 924 $h = 1 if $self->{scroll_y};
894 925
895 ($w, $h) 926 ($w, $h)
896} 927}
897 928
898sub size_allocate { 929sub invoke_size_allocate {
899 my ($self, $w, $h) = @_; 930 my ($self, $w, $h) = @_;
900 931
901 my $child = $self->child; 932 my $child = $self->child;
902 933
903 $w = $child->{req_w} if $self->{scroll_x} && $child->{req_w}; 934 $w = $child->{req_w} if $self->{scroll_x} && $child->{req_w};
904 $h = $child->{req_h} if $self->{scroll_y} && $child->{req_h}; 935 $h = $child->{req_h} if $self->{scroll_y} && $child->{req_h};
905 936
906 $self->child->configure (0, 0, $w, $h); 937 $self->child->configure (0, 0, $w, $h);
907 $self->update; 938 $self->update;
939
940 1
908} 941}
909 942
910sub set_offset { 943sub set_offset {
911 my ($self, $x, $y) = @_; 944 my ($self, $x, $y) = @_;
912 945
946 my $x = max 0, min $self->child->{w} - $self->{w}, int $x;
947 my $y = max 0, min $self->child->{h} - $self->{h}, int $y;
948
949 if ($x != $self->{view_x} or $y != $self->{view_y}) {
913 $self->{view_x} = int $x; 950 $self->{view_x} = $x;
914 $self->{view_y} = int $y; 951 $self->{view_y} = $y;
915 952
953 $self->emit (changed => $x, $y);
916 $self->update; 954 $self->update;
955 }
956}
957
958sub set_center {
959 my ($self, $x, $y) = @_;
960
961 $self->set_offset ($x - $self->{w} * .5, $y - $self->{h} * .5);
962}
963
964sub make_visible {
965 my ($self, $x, $y, $border) = @_;
966
967 if ( $x < $self->{view_x} + $self->{w} * $border
968 || $x > $self->{view_x} + $self->{w} * (1 - $border)
969 || $y < $self->{view_y} + $self->{h} * $border
970 || $y > $self->{view_y} + $self->{h} * (1 - $border)
971 ) {
972 $self->set_center ($x, $y);
973 }
917} 974}
918 975
919# hmm, this does not work for topleft of $self... but we should not ask for that 976# hmm, this does not work for topleft of $self... but we should not ask for that
920sub coord2local { 977sub coord2local {
921 my ($self, $x, $y) = @_; 978 my ($self, $x, $y) = @_;
936 my ($self, $x, $y) = @_; 993 my ($self, $x, $y) = @_;
937 994
938 if ( $x >= $self->{x} && $x < $self->{x} + $self->{w} 995 if ( $x >= $self->{x} && $x < $self->{x} + $self->{w}
939 && $y >= $self->{y} && $y < $self->{y} + $self->{h} 996 && $y >= $self->{y} && $y < $self->{y} + $self->{h}
940 ) { 997 ) {
941 $self->child->find_widget ($x + $self->{view_x}, $y + $self->{view_y}) 998 $self->child->find_widget ($x + $self->{view_x}, $y + $self->{view_y})
942 } else { 999 } else {
943 $self->CFClient::UI::Base::find_widget ($x, $y) 1000 $self->CFPlus::UI::Base::find_widget ($x, $y)
944 } 1001 }
945} 1002}
946 1003
947sub _render { 1004sub _render {
948 my ($self) = @_; 1005 my ($self) = @_;
949 1006
950 local $CFClient::UI::Base::draw_x = $CFClient::UI::Base::draw_x - $self->{view_x}; 1007 local $CFPlus::UI::Base::draw_x = $CFPlus::UI::Base::draw_x - $self->{view_x};
951 local $CFClient::UI::Base::draw_y = $CFClient::UI::Base::draw_y - $self->{view_y}; 1008 local $CFPlus::UI::Base::draw_y = $CFPlus::UI::Base::draw_y - $self->{view_y};
952 1009
953 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y}; 1010 CFPlus::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y};
954 1011
955 $self->SUPER::_render; 1012 $self->SUPER::_render;
956} 1013}
957 1014
958############################################################################# 1015#############################################################################
959 1016
960package CFClient::UI::ScrolledWindow; 1017package CFPlus::UI::ScrolledWindow;
961 1018
962our @ISA = CFClient::UI::HBox::; 1019our @ISA = CFPlus::UI::Table::;
963 1020
964sub new { 1021sub new {
965 my ($class, %arg) = @_; 1022 my ($class, %arg) = @_;
966 1023
967 my $child = delete $arg{child}; 1024 my $child = delete $arg{child};
968 1025
969 my $self; 1026 my $self;
970 1027
971 my $slider = new CFClient::UI::Slider 1028 my $hslider = new CFPlus::UI::Slider
1029 c_col => 0,
1030 c_row => 1,
1031 vertical => 0,
1032 range => [0, 0, 1, 0.01], # HACK fix
1033 on_changed => sub {
1034 $self->{hpos} = $_[1];
1035 $self->{vp}->set_offset ($self->{hpos}, $self->{vpos});
1036 },
1037 ;
1038
1039 my $vslider = new CFPlus::UI::Slider
1040 c_col => 1,
1041 c_row => 0,
972 vertical => 1, 1042 vertical => 1,
973 range => [0, 0, 1, 0.01], # HACK fix 1043 range => [0, 0, 1, 0.01], # HACK fix
974 on_changed => sub { 1044 on_changed => sub {
975 $self->{vp}->set_offset (0, $_[1]); 1045 $self->{vpos} = $_[1];
1046 $self->{vp}->set_offset ($self->{hpos}, $self->{vpos});
976 }, 1047 },
977 ; 1048 ;
978 1049
979 $self = $class->SUPER::new ( 1050 $self = $class->SUPER::new (
980 vp => (new CFClient::UI::ViewPort expand => 1), 1051 scroll_x => 0,
1052 scroll_y => 1,
1053 can_events => 1,
981 slider => $slider, 1054 hslider => $hslider,
1055 vslider => $vslider,
1056 col_expand => [1, 0],
1057 row_expand => [1, 0],
982 %arg, 1058 %arg,
983 ); 1059 );
984 1060
1061 $self->{vp} = new CFPlus::UI::ViewPort
1062 c_col => 0,
1063 c_row => 0,
1064 expand => 1,
1065 scroll_x => $self->{scroll_x},
1066 scroll_y => $self->{scroll_y},
1067 on_changed => sub {
1068 my ($vp, $x, $y) = @_;
1069
1070 $vp->{parent}{hslider}->set_value ($x);
1071 $vp->{parent}{vslider}->set_value ($y);
1072
1073 0
1074 },
1075 on_size_allocate => sub {
1076 my ($vp, $w, $h) = @_;
1077 $vp->{parent}->update_slider;
1078 0
1079 },
1080 ;
1081
985 $self->SUPER::add ($self->{vp}, $self->{slider}); 1082 $self->SUPER::add ($self->{vp});
1083
986 $self->add ($child) if $child; 1084 $self->add ($child) if $child;
987 1085
988 $self 1086 $self
989} 1087}
990 1088
992 my ($self, $widget) = @_; 1090 my ($self, $widget) = @_;
993 1091
994 $self->{vp}->add ($self->{child} = $widget); 1092 $self->{vp}->add ($self->{child} = $widget);
995} 1093}
996 1094
1095sub set_offset { shift->{vp}->set_offset (@_) }
1096sub set_center { shift->{vp}->set_center (@_) }
1097sub make_visible { shift->{vp}->make_visible (@_) }
1098
997sub update { 1099sub update_slider {
1100 my ($self) = @_;
1101
1102 my $child = ($self->{vp} or return)->child;
1103
1104 if ($self->{scroll_x}) {
1105 my ($w1, $w2) = ($child->{req_w}, $self->{vp}{w});
1106 $self->{hslider}->set_range ([$self->{hslider}{range}[0], 0, $w1, $w2, 1]);
1107
1108 my $visible = $w1 > $w2;
1109 if ($visible != $self->{hslider_visible}) {
1110 $self->{hslider_visible} = $visible;
1111 $visible ? $self->SUPER::add ($self->{hslider})
1112 : $self->SUPER::remove ($self->{hslider});
1113 }
1114 }
1115
1116 if ($self->{scroll_y}) {
1117 my ($h1, $h2) = ($child->{req_h}, $self->{vp}{h});
1118 $self->{vslider}->set_range ([$self->{vslider}{range}[0], 0, $h1, $h2, 1]);
1119
1120 my $visible = $h1 > $h2;
1121 if ($visible != $self->{vslider_visible}) {
1122 $self->{vslider_visible} = $visible;
1123 $visible ? $self->SUPER::add ($self->{vslider})
1124 : $self->SUPER::remove ($self->{vslider});
1125 }
1126 }
1127}
1128
1129sub start_dragging {
998 my ($self) = @_; 1130 my ($self, $ev) = @_;
999 1131
1000 $self->SUPER::update; 1132 $self->grab_focus;
1001 1133
1002 # todo: overwrite size_allocate of child 1134 my $ox = $self->{vp}{view_x};
1003 my $child = $self->{vp}->child; 1135 my $oy = $self->{vp}{view_y};
1004 $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]); 1136
1005} 1137 $self->{motion} = sub {
1138 my ($ev, $x, $y) = @_;
1006 1139
1140 $ox -= $ev->{xrel};
1141 $oy -= $ev->{yrel};
1142
1143 $self->{vp}->set_offset ($ox, $oy);
1144 };
1145}
1146
1147sub invoke_mouse_wheel {
1148 my ($self, $ev) = @_;
1149
1150 $self->{vslider}->emit (mouse_wheel => $ev) if $self->{vslider_visible};
1151 $self->{hslider}->emit (mouse_wheel => $ev) if $self->{hslider_visible};
1152
1153 1
1154}
1155
1156sub invoke_button_down {
1157 my ($self, $ev, $x, $y) = @_;
1158
1159 if ($ev->{button} == 2) {
1160 $self->start_dragging ($ev);
1161 return 1;
1162 }
1163
1164 0
1165}
1166
1167sub invoke_button_up {
1168 my ($self, $ev, $x, $y) = @_;
1169
1170 if (delete $self->{motion}) {
1171 return 1;
1172 }
1173
1174 0
1175}
1176
1177sub invoke_mouse_motion {
1178 my ($self, $ev, $x, $y) = @_;
1179
1180 if ($self->{motion}) {
1181 $self->{motion}->($ev, $x, $y);
1182 return 1;
1183 }
1184
1185 0
1186}
1187
1007sub size_allocate { 1188sub invoke_size_allocate {
1008 my ($self, $w, $h) = @_; 1189 my ($self, $w, $h) = @_;
1009 1190
1191 $self->update_slider;
1010 $self->SUPER::size_allocate ($w, $h); 1192 $self->SUPER::invoke_size_allocate ($w, $h)
1011
1012 my $child = $self->{vp}->child;
1013 $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]);
1014} 1193}
1015
1016#TODO# update range on size_allocate depending on child
1017# update viewport offset on scroll
1018 1194
1019############################################################################# 1195#############################################################################
1020 1196
1021package CFClient::UI::Frame; 1197package CFPlus::UI::Frame;
1022 1198
1023our @ISA = CFClient::UI::Bin::; 1199our @ISA = CFPlus::UI::Bin::;
1024 1200
1025use CFClient::OpenGL; 1201use CFPlus::OpenGL;
1026 1202
1027sub new { 1203sub new {
1028 my $class = shift; 1204 my $class = shift;
1029 1205
1030 $class->SUPER::new ( 1206 $class->SUPER::new (
1040 my ($w, $h) = @$self{qw(w h)}; 1216 my ($w, $h) = @$self{qw(w h)};
1041 1217
1042 glEnable GL_BLEND; 1218 glEnable GL_BLEND;
1043 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; 1219 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
1044 glColor_premultiply @{ $self->{bg} }; 1220 glColor_premultiply @{ $self->{bg} };
1045
1046 glBegin GL_QUADS;
1047 glVertex 0 , 0;
1048 glVertex 0 , $h;
1049 glVertex $w, $h; 1221 glRect 0, 0, $w, $h;
1050 glVertex $w, 0;
1051 glEnd;
1052
1053 glDisable GL_BLEND; 1222 glDisable GL_BLEND;
1054 } 1223 }
1055 1224
1056 $self->SUPER::_draw; 1225 $self->SUPER::_draw;
1057} 1226}
1058 1227
1059############################################################################# 1228#############################################################################
1060 1229
1061package CFClient::UI::FancyFrame; 1230package CFPlus::UI::FancyFrame;
1062 1231
1063our @ISA = CFClient::UI::Bin::; 1232our @ISA = CFPlus::UI::Bin::;
1064 1233
1065use CFClient::OpenGL; 1234use CFPlus::OpenGL;
1235
1236sub new {
1237 my ($class, %arg) = @_;
1238
1239 if ((exists $arg{label}) && !ref $arg{label}) {
1240 $arg{label} = new CFPlus::UI::Label
1241 align => 1,
1242 valign => 0,
1243 text => $arg{label},
1244 fontsize => ($arg{border} || 0.8) * 0.75;
1245 }
1246
1247 my $self = $class->SUPER::new (
1248 # label => "",
1249 fg => [0.6, 0.3, 0.1],
1250 border => 0.8,
1251 style => 'single',
1252 %arg,
1253 );
1254
1255 $self
1256}
1257
1258sub add {
1259 my ($self, @widgets) = @_;
1260
1261 $self->SUPER::add (@widgets);
1262 $self->CFPlus::UI::Container::add ($self->{label}) if $self->{label};
1263}
1264
1265sub border {
1266 int $_[0]{border} * $::FONTSIZE
1267}
1268
1269sub size_request {
1270 my ($self) = @_;
1271
1272 ($self->{label_w}, undef) = $self->{label}->size_request
1273 if $self->{label};
1274
1275 my ($w, $h) = $self->SUPER::size_request;
1276
1277 (
1278 $w + $self->border * 2,
1279 $h + $self->border * 2,
1280 )
1281}
1282
1283sub invoke_size_allocate {
1284 my ($self, $w, $h) = @_;
1285
1286 my $border = $self->border;
1287
1288 $w -= List::Util::max 0, $border * 2;
1289 $h -= List::Util::max 0, $border * 2;
1290
1291 if (my $label = $self->{label}) {
1292 $label->{w} = List::Util::max 0, List::Util::min $self->{label_w}, $w - $border * 2;
1293 $label->{h} = List::Util::min $h, $border;
1294 $label->invoke_size_allocate ($label->{w}, $label->{h});
1295 }
1296
1297 $self->child->configure ($border, $border, $w, $h);
1298
1299 1
1300}
1301
1302sub _draw {
1303 my ($self) = @_;
1304
1305 my $child = $self->{children}[0];
1306
1307 my $border = $self->border;
1308 my ($w, $h) = ($self->{w}, $self->{h});
1309
1310 $child->draw;
1311
1312 glColor @{$self->{fg}};
1313 glBegin GL_LINE_STRIP;
1314 glVertex $border * 1.5 , $border * 0.5 + 0.5;
1315 glVertex $border * 0.5 + 0.5, $border * 0.5 + 0.5;
1316 glVertex $border * 0.5 + 0.5, $h - $border * 0.5 + 0.5;
1317 glVertex $w - $border * 0.5 + 0.5, $h - $border * 0.5 + 0.5;
1318 glVertex $w - $border * 0.5 + 0.5, $border * 0.5 + 0.5;
1319 glVertex $self->{label} ? $border * 2 + $self->{label}{w} : $border * 1.5, $border * 0.5 + 0.5;
1320 glEnd;
1321
1322 if ($self->{label}) {
1323 glTranslate $border * 2, 0;
1324 $self->{label}->_draw;
1325 }
1326}
1327
1328#############################################################################
1329
1330package CFPlus::UI::Toplevel;
1331
1332our @ISA = CFPlus::UI::Bin::;
1333
1334use CFPlus::OpenGL;
1066 1335
1067my $bg = 1336my $bg =
1068 new_from_file CFClient::Texture CFClient::find_rcfile "d1_bg.png", 1337 new_from_file CFPlus::Texture CFPlus::find_rcfile "d1_bg.png",
1069 mipmap => 1, wrap => 1; 1338 mipmap => 1, wrap => 1;
1070 1339
1071my @border = 1340my @border =
1072 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 1341 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
1073 qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); 1342 qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png);
1343
1344my @icon =
1345 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
1346 qw(x1_move.png x1_resize.png);
1074 1347
1075sub new { 1348sub new {
1076 my ($class, %arg) = @_; 1349 my ($class, %arg) = @_;
1077 1350
1078 my $self = $class->SUPER::new ( 1351 my $self = $class->SUPER::new (
1079 bg => [1, 1, 1, 1], 1352 bg => [1, 1, 1, 1],
1080 border_bg => [1, 1, 1, 1], 1353 border_bg => [1, 1, 1, 1],
1081 border => 0.6, 1354 border => 0.6,
1082 can_events => 1, 1355 can_events => 1,
1083 min_w => 16, 1356 min_w => 64,
1084 min_h => 16, 1357 min_h => 32,
1085 %arg, 1358 %arg,
1086 ); 1359 );
1087 1360
1088 $self->{title_widget} = new CFClient::UI::Label 1361 $self->{title_widget} = new CFPlus::UI::Label
1089 align => 0, 1362 align => 0,
1090 valign => 1, 1363 valign => 1,
1091 text => $self->{title}, 1364 text => $self->{title},
1092 fontsize => $self->{border}, 1365 fontsize => $self->{border},
1093 if exists $self->{title}; 1366 if exists $self->{title};
1094 1367
1368 if ($self->{has_close_button}) {
1369 $self->{close_button} =
1370 new CFPlus::UI::ImageButton
1371 path => 'x1_close.png',
1372 on_activate => sub { $self->emit ("delete") };
1373
1374 $self->CFPlus::UI::Container::add ($self->{close_button});
1375 }
1376
1095 $self 1377 $self
1096} 1378}
1097 1379
1098sub add { 1380sub add {
1099 my ($self, @widgets) = @_; 1381 my ($self, @widgets) = @_;
1100 1382
1101 $self->SUPER::add (@widgets); 1383 $self->SUPER::add (@widgets);
1384 $self->CFPlus::UI::Container::add ($self->{close_button}) if $self->{close_button};
1102 $self->CFClient::UI::Container::add ($self->{title_widget}) if $self->{title_widget}; 1385 $self->CFPlus::UI::Container::add ($self->{title_widget}) if $self->{title_widget};
1103} 1386}
1104 1387
1105sub border { 1388sub border {
1106 int $_[0]{border} * $::FONTSIZE 1389 int $_[0]{border} * $::FONTSIZE
1107} 1390}
1108 1391
1392sub get_max_wh {
1393 my ($self) = @_;
1394
1395 return ($self->{w}, $self->{h})
1396 if $self->{visible} && $self->{w};
1397
1398 $self->SUPER::get_max_wh
1399}
1400
1109sub size_request { 1401sub size_request {
1110 my ($self) = @_; 1402 my ($self) = @_;
1111 1403
1112 $self->{title_widget}->size_request 1404 $self->{title_widget}->size_request
1113 if $self->{title_widget}; 1405 if $self->{title_widget};
1406
1407 $self->{close_button}->size_request
1408 if $self->{close_button};
1114 1409
1115 my ($w, $h) = $self->SUPER::size_request; 1410 my ($w, $h) = $self->SUPER::size_request;
1116 1411
1117 ( 1412 (
1118 $w + $self->border * 2, 1413 $w + $self->border * 2,
1119 $h + $self->border * 2, 1414 $h + $self->border * 2,
1120 ) 1415 )
1121} 1416}
1122 1417
1123sub size_allocate { 1418sub invoke_size_allocate {
1124 my ($self, $w, $h) = @_; 1419 my ($self, $w, $h) = @_;
1125 1420
1126 if ($self->{title_widget}) { 1421 if ($self->{title_widget}) {
1127 $self->{title_widget}{w} = $w; 1422 $self->{title_widget}{w} = $w;
1128 $self->{title_widget}{h} = $h; 1423 $self->{title_widget}{h} = $h;
1129 $self->{title_widget}->size_allocate ($w, $h); 1424 $self->{title_widget}->invoke_size_allocate ($w, $h);
1130 } 1425 }
1131 1426
1132 my $border = $self->border; 1427 my $border = $self->border;
1133 1428
1134 $h -= List::Util::max 0, $border * 2; 1429 $h -= List::Util::max 0, $border * 2;
1135 $w -= List::Util::max 0, $border * 2; 1430 $w -= List::Util::max 0, $border * 2;
1431
1432 $self->child->configure ($border, $border, $w, $h);
1433
1434 $self->{close_button}->configure ($self->{w} - $border, 0, $border, $border)
1435 if $self->{close_button};
1436
1437 1
1438}
1439
1440sub invoke_delete {
1441 my ($self) = @_;
1442
1443 $self->hide;
1136 1444
1137 $self->child->configure ($border, $border, $w, $h); 1445 1
1138} 1446}
1139 1447
1140sub button_down { 1448sub invoke_button_down {
1141 my ($self, $ev, $x, $y) = @_; 1449 my ($self, $ev, $x, $y) = @_;
1142 1450
1143 my ($w, $h) = @$self{qw(w h)}; 1451 my ($w, $h) = @$self{qw(w h)};
1144 my $border = $self->border; 1452 my $border = $self->border;
1145 1453
1176 1484
1177 ($x, $y) = ($ev->{x}, $ev->{y}); 1485 ($x, $y) = ($ev->{x}, $ev->{y});
1178 1486
1179 $self->move_abs ($bx + $x - $ox, $by + $y - $oy); 1487 $self->move_abs ($bx + $x - $ox, $by + $y - $oy);
1180 # HACK: the next line is required to enforce placement 1488 # HACK: the next line is required to enforce placement
1181 $self->{parent}->size_allocate ($self->{parent}{w}, $self->{parent}{h}); 1489 $self->{parent}->invoke_size_allocate ($self->{parent}{w}, $self->{parent}{h});
1182 }; 1490 };
1183 } else { 1491 } else {
1184 return 0; 1492 return 0;
1185 } 1493 }
1186 1494
1187 1 1495 1
1188} 1496}
1189 1497
1190sub button_up { 1498sub invoke_button_up {
1191 my ($self, $ev, $x, $y) = @_; 1499 my ($self, $ev, $x, $y) = @_;
1192 1500
1193 !!delete $self->{motion} 1501 ! ! delete $self->{motion}
1194} 1502}
1195 1503
1196sub mouse_motion { 1504sub invoke_mouse_motion {
1197 my ($self, $ev, $x, $y) = @_; 1505 my ($self, $ev, $x, $y) = @_;
1198 1506
1199 $self->{motion}->($ev, $x, $y) if $self->{motion}; 1507 $self->{motion}->($ev, $x, $y) if $self->{motion};
1200 1508
1201 !!$self->{motion} 1509 ! ! $self->{motion}
1510}
1511
1512sub invoke_visibility_change {
1513 my ($self, $visible) = @_;
1514
1515 delete $self->{motion} unless $visible;
1516
1517 0
1202} 1518}
1203 1519
1204sub _draw { 1520sub _draw {
1205 my ($self) = @_; 1521 my ($self) = @_;
1206 1522
1213 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE; 1529 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1214 1530
1215 my $border = $self->border; 1531 my $border = $self->border;
1216 1532
1217 glColor @{ $self->{border_bg} }; 1533 glColor @{ $self->{border_bg} };
1218 $border[0]->draw_quad_alpha (0, 0, $w, $border); 1534 $border[0]->draw_quad_alpha ( 0, 0, $w, $border);
1219 $border[1]->draw_quad_alpha (0, $border, $border, $ch); 1535 $border[1]->draw_quad_alpha ( 0, $border, $border, $ch);
1220 $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); 1536 $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch);
1221 $border[3]->draw_quad_alpha (0, $h - $border, $w, $border); 1537 $border[3]->draw_quad_alpha ( 0, $h - $border, $w, $border);
1538
1539 # move
1540 my $w2 = ($w - $border) * .5;
1541 my $h2 = ($h - $border) * .5;
1542 $icon[0]->draw_quad_alpha ( 0, $h2, $border, $border);
1543 $icon[0]->draw_quad_alpha ($w - $border, $h2, $border, $border);
1544 $icon[0]->draw_quad_alpha ($w2 , $h - $border, $border, $border);
1545
1546 # resize
1547 $icon[1]->draw_quad_alpha ( 0, 0, $border, $border);
1548 $icon[1]->draw_quad_alpha ($w - $border, 0, $border, $border)
1549 unless $self->{has_close_button};
1550 $icon[1]->draw_quad_alpha ( 0, $h - $border, $border, $border);
1551 $icon[1]->draw_quad_alpha ($w - $border, $h - $border, $border, $border);
1222 1552
1223 if (@{$self->{bg}} < 4 || $self->{bg}[3]) { 1553 if (@{$self->{bg}} < 4 || $self->{bg}[3]) {
1224 glColor @{ $self->{bg} }; 1554 glColor @{ $self->{bg} };
1225 1555
1226 # TODO: repeat texture not scale 1556 # TODO: repeat texture not scale
1235 $child->draw; 1565 $child->draw;
1236 1566
1237 if ($self->{title_widget}) { 1567 if ($self->{title_widget}) {
1238 glTranslate 0, $border - $self->{h}; 1568 glTranslate 0, $border - $self->{h};
1239 $self->{title_widget}->_draw; 1569 $self->{title_widget}->_draw;
1570
1571 glTranslate 0, - ($border - $self->{h});
1240 } 1572 }
1573
1574 $self->{close_button}->draw
1575 if $self->{close_button};
1241} 1576}
1242 1577
1243############################################################################# 1578#############################################################################
1244 1579
1245package CFClient::UI::Table; 1580package CFPlus::UI::Table;
1246 1581
1247our @ISA = CFClient::UI::Base::; 1582our @ISA = CFPlus::UI::Container::;
1248 1583
1249use List::Util qw(max sum); 1584use List::Util qw(max sum);
1250 1585
1251use CFClient::OpenGL; 1586use CFPlus::OpenGL;
1252 1587
1253sub new { 1588sub new {
1254 my $class = shift; 1589 my $class = shift;
1255 1590
1256 $class->SUPER::new ( 1591 $class->SUPER::new (
1257 col_expand => [], 1592 col_expand => [],
1593 row_expand => [],
1258 @_, 1594 @_,
1259 ) 1595 )
1260} 1596}
1261 1597
1262sub children {
1263 grep $_, map @$_, grep $_, @{ $_[0]{children} }
1264}
1265
1266sub add { 1598sub add {
1267 my ($self, $x, $y, $child) = @_; 1599 my ($self, @widgets) = @_;
1268 1600
1269 $child->set_parent ($self); 1601 for my $child (@widgets) {
1270 $self->{children}[$y][$x] = $child; 1602 $child->{c_rowspan} ||= 1;
1603 $child->{c_colspan} ||= 1;
1604 }
1271 1605
1272 $self->realloc; 1606 $self->SUPER::add (@widgets);
1273} 1607}
1274 1608
1275sub remove { 1609sub add_at {
1610 my $self = shift;
1611
1612 my @widgets;
1613
1614 while (@_) {
1615 my ($col, $row, $child) = splice @_, 0, 3, ();
1616
1617 $child->{c_row} = $row;
1618 $child->{c_col} = $col;
1619
1620 push @widgets, $child;
1621 }
1622
1623 $self->add (@widgets);
1624}
1625
1626sub get_wh {
1276 my ($self, $child) = @_; 1627 my ($self) = @_;
1277 1628
1278 # TODO: not yet implemented 1629 my (@w, @h);
1279}
1280
1281# TODO: move to container class maybe? send children a signal on removal?
1282sub clear {
1283 my ($self) = @_;
1284 1630
1285 my @children = $self->children; 1631 my @children = $self->children;
1286 delete $self->{children}; 1632
1633 # first pass, columns
1634 for my $widget (sort { $a->{c_colspan} <=> $b->{c_colspan} } @children) {
1635 my ($c, $w, $cs) = @$widget{qw(c_col req_w c_colspan)};
1636
1637 my $sw = sum @w[$c .. $c + $cs - 1];
1638
1639 if ($w > $sw) {
1640 $_ += ($w - $sw) / ($sw ? $sw / $_ : $cs) for @w[$c .. $c + $cs - 1];
1641 }
1287 1642 }
1288 for (@children) {
1289 delete $_->{parent};
1290 $_->hide;
1291 }
1292 1643
1293 $self->realloc; 1644 # second pass, rows
1294} 1645 for my $widget (sort { $a->{c_rowspan} <=> $b->{c_rowspan} } @children) {
1295
1296sub get_wh {
1297 my ($self) = @_;
1298
1299 my (@w, @h);
1300
1301 for my $y (0 .. $#{$self->{children}}) {
1302 my $row = $self->{children}[$y]
1303 or next;
1304
1305 for my $x (0 .. $#$row) {
1306 my $widget = $row->[$x]
1307 or next;
1308 my ($w, $h) = @$widget{qw(req_w req_h)}; 1646 my ($r, $h, $rs) = @$widget{qw(c_row req_h c_rowspan)};
1309 1647
1310 $w[$x] = max $w[$x], $w; 1648 my $sh = sum @h[$r .. $r + $rs - 1];
1311 $h[$y] = max $h[$y], $h; 1649
1650 if ($h > $sh) {
1651 $_ += ($h - $sh) / ($sh ? $sh / $_ : $rs) for @h[$r .. $r + $rs - 1];
1312 } 1652 }
1313 } 1653 }
1314 1654
1315 (\@w, \@h) 1655 (\@w, \@h)
1316} 1656}
1324 (sum @$ws), 1664 (sum @$ws),
1325 (sum @$hs), 1665 (sum @$hs),
1326 ) 1666 )
1327} 1667}
1328 1668
1329sub size_allocate { 1669sub invoke_size_allocate {
1330 my ($self, $w, $h) = @_; 1670 my ($self, $w, $h) = @_;
1331 1671
1332 my ($ws, $hs) = $self->get_wh; 1672 my ($ws, $hs) = $self->get_wh;
1333 1673
1334 my $req_w = (sum @$ws) || 1; 1674 my $req_w = (sum @$ws) || 1;
1335 my $req_h = (sum @$hs) || 1; 1675 my $req_h = (sum @$hs) || 1;
1336 1676
1337 # TODO: nicer code && do row_expand 1677 # now linearly scale the rows/columns to the allocated size
1338 my @col_expand = @{$self->{col_expand}}; 1678 my @col_expand = @{$self->{col_expand}};
1339 @col_expand = (1) x @$ws unless @col_expand; 1679 @col_expand = (1) x @$ws unless @col_expand;
1340 my $col_expand = (sum @col_expand) || 1; 1680 my $col_expand = (sum @col_expand) || 1;
1341 1681
1342 # linearly scale sizes
1343 $ws->[$_] += $col_expand[$_] / $col_expand * ($w - $req_w) for 0 .. $#$ws; 1682 $ws->[$_] += $col_expand[$_] / $col_expand * ($w - $req_w) for 0 .. $#$ws;
1344 $hs->[$_] *= 1 * $h / $req_h for 0 .. $#$hs;
1345 1683
1346 CFClient::UI::harmonize $ws; 1684 CFPlus::UI::harmonize $ws;
1685
1686 my @row_expand = @{$self->{row_expand}};
1687 @row_expand = (1) x @$ws unless @row_expand;
1688 my $row_expand = (sum @row_expand) || 1;
1689
1690 $hs->[$_] += $row_expand[$_] / $row_expand * ($h - $req_h) for 0 .. $#$hs;
1691
1347 CFClient::UI::harmonize $hs; 1692 CFPlus::UI::harmonize $hs;
1348 1693
1349 my $y; 1694 my @x; for (0 .. $#$ws) { $x[$_ + 1] = $x[$_] + $ws->[$_] }
1695 my @y; for (0 .. $#$hs) { $y[$_ + 1] = $y[$_] + $hs->[$_] }
1350 1696
1351 for my $r (0 .. $#{$self->{children}}) { 1697 for my $widget ($self->children) {
1352 my $row = $self->{children}[$r] 1698 my ($r, $c, $w, $h, $rs, $cs) = @$widget{qw(c_row c_col req_w req_h c_rowspan c_colspan)};
1353 or next;
1354 1699
1355 my $x = 0; 1700 $widget->configure (
1356 my $row_h = $hs->[$r]; 1701 $x[$c], $y[$r],
1702 $x[$c + $cs] - $x[$c], $y[$r + $rs] - $y[$r],
1357 1703 );
1358 for my $c (0 .. $#$row) { 1704 }
1359 my $col_w = $ws->[$c];
1360 1705
1361 if (my $widget = $row->[$c]) { 1706 1
1362 $widget->configure ($x, $y, $col_w, $row_h); 1707}
1363 }
1364 1708
1365 $x += $col_w; 1709#############################################################################
1710
1711package CFPlus::UI::Fixed;
1712
1713use List::Util qw(min max);
1714
1715our @ISA = CFPlus::UI::Container::;
1716
1717sub _scale($$$) {
1718 my ($rel, $val, $max) = @_;
1719
1720 $rel ? $val * $max : $val
1721}
1722
1723sub size_request {
1724 my ($self) = @_;
1725
1726 my ($x1, $y1, $x2, $y2) = (0, 0, 0, 0);
1727
1728 # determine overall size by querying abs widgets
1729 for my $child ($self->visible_children) {
1730 unless ($child->{c_rel}) {
1731 my $x = $child->{c_x};
1732 my $y = $child->{c_y};
1733
1734 $x1 = min $x1, $x; $x2 = max $x2, $x + $child->{req_w};
1735 $y1 = min $y1, $y; $y2 = max $y2, $y + $child->{req_h};
1366 } 1736 }
1367
1368 $y += $row_h;
1369 } 1737 }
1370 1738
1371} 1739 my $W = $x2 - $x1;
1740 my $H = $y2 - $y1;
1372 1741
1373sub find_widget { 1742 # now layout remaining widgets
1743 for my $child ($self->visible_children) {
1744 if ($child->{c_rel}) {
1745 my $x = _scale $child->{c_rel}, $child->{c_x}, $W;
1746 my $y = _scale $child->{c_rel}, $child->{c_y}, $H;
1747
1748 $x1 = min $x1, $x; $x2 = max $x2, $x + $child->{req_w};
1749 $y1 = min $y1, $y; $y2 = max $y2, $y + $child->{req_h};
1750 }
1751 }
1752
1753 my $W = $x2 - $x1;
1754 my $H = $y2 - $y1;
1755
1756 ($W, $H)
1757}
1758
1759sub invoke_size_allocate {
1374 my ($self, $x, $y) = @_; 1760 my ($self, $W, $H) = @_;
1375 1761
1376 $x -= $self->{x}; 1762 for my $child ($self->visible_children) {
1377 $y -= $self->{y}; 1763 my $x = _scale $child->{c_rel}, $child->{c_x}, $W;
1764 my $y = _scale $child->{c_rel}, $child->{c_y}, $H;
1378 1765
1379 my $res; 1766 $x += $child->{c_halign} * $child->{req_w};
1767 $y += $child->{c_valign} * $child->{req_h};
1380 1768
1381 for (grep $_, map @$_, grep $_, @{ $self->{children} }) { 1769 $child->configure (int $x, int $y, $child->{req_w}, $child->{req_h});
1382 $res = $_->find_widget ($x, $y) 1770 }
1383 and return $res; 1771
1384 } 1772 1
1385
1386 $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y})
1387}
1388
1389sub _draw {
1390 my ($self) = @_;
1391
1392 for (grep $_, @{$self->{children}}) {
1393 $_->draw for grep $_, @$_;
1394 }
1395} 1773}
1396 1774
1397############################################################################# 1775#############################################################################
1398 1776
1399package CFClient::UI::Box; 1777package CFPlus::UI::Box;
1400 1778
1401our @ISA = CFClient::UI::Container::; 1779our @ISA = CFPlus::UI::Container::;
1402 1780
1403sub size_request { 1781sub size_request {
1404 my ($self) = @_; 1782 my ($self) = @_;
1783
1784 my @children = $self->visible_children;
1405 1785
1406 $self->{vertical} 1786 $self->{vertical}
1407 ? ( 1787 ? (
1408 (List::Util::max map $_->{req_w}, @{$self->{children}}), 1788 (List::Util::max map $_->{req_w}, @children),
1409 (List::Util::sum map $_->{req_h}, @{$self->{children}}), 1789 (List::Util::sum map $_->{req_h}, @children),
1410 ) 1790 )
1411 : ( 1791 : (
1412 (List::Util::sum map $_->{req_w}, @{$self->{children}}), 1792 (List::Util::sum map $_->{req_w}, @children),
1413 (List::Util::max map $_->{req_h}, @{$self->{children}}), 1793 (List::Util::max map $_->{req_h}, @children),
1414 ) 1794 )
1415} 1795}
1416 1796
1417sub size_allocate { 1797sub invoke_size_allocate {
1418 my ($self, $w, $h) = @_; 1798 my ($self, $w, $h) = @_;
1419 1799
1420 my $space = $self->{vertical} ? $h : $w; 1800 my $space = $self->{vertical} ? $h : $w;
1421 my $children = $self->{children}; 1801 my @children = $self->visible_children;
1422 1802
1423 my @req; 1803 my @req;
1424 1804
1425 if ($self->{homogeneous}) { 1805 if ($self->{homogeneous}) {
1426 @req = ($space / (@$children || 1)) x @$children; 1806 @req = ($space / (@children || 1)) x @children;
1427 } else { 1807 } else {
1428 @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @$children; 1808 @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @children;
1429 my $req = List::Util::sum @req; 1809 my $req = List::Util::sum @req;
1430 1810
1431 if ($req > $space) { 1811 if ($req > $space) {
1432 # ah well, not enough space 1812 # ah well, not enough space
1433 $_ *= $space / $req for @req; 1813 $_ *= $space / $req for @req;
1434 } else { 1814 } else {
1435 my $expand = (List::Util::sum map $_->{expand}, @$children) || 1; 1815 my $expand = (List::Util::sum map $_->{expand}, @children) || 1;
1436 1816
1437 $space = ($space - $req) / $expand; # remaining space to give away 1817 $space = ($space - $req) / $expand; # remaining space to give away
1438 1818
1439 $req[$_] += $space * $children->[$_]{expand} 1819 $req[$_] += $space * $children[$_]{expand}
1440 for 0 .. $#$children; 1820 for 0 .. $#children;
1441 } 1821 }
1442 } 1822 }
1443 1823
1444 CFClient::UI::harmonize \@req; 1824 CFPlus::UI::harmonize \@req;
1445 1825
1446 my $pos = 0; 1826 my $pos = 0;
1447 for (0 .. $#$children) { 1827 for (0 .. $#children) {
1448 my $alloc = $req[$_]; 1828 my $alloc = $req[$_];
1449 $children->[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h)); 1829 $children[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h));
1450 1830
1451 $pos += $alloc; 1831 $pos += $alloc;
1452 } 1832 }
1453 1833
1454 1 1834 1
1455} 1835}
1456 1836
1457############################################################################# 1837#############################################################################
1458 1838
1459package CFClient::UI::HBox; 1839package CFPlus::UI::HBox;
1460 1840
1461our @ISA = CFClient::UI::Box::; 1841our @ISA = CFPlus::UI::Box::;
1462 1842
1463sub new { 1843sub new {
1464 my $class = shift; 1844 my $class = shift;
1465 1845
1466 $class->SUPER::new ( 1846 $class->SUPER::new (
1469 ) 1849 )
1470} 1850}
1471 1851
1472############################################################################# 1852#############################################################################
1473 1853
1474package CFClient::UI::VBox; 1854package CFPlus::UI::VBox;
1475 1855
1476our @ISA = CFClient::UI::Box::; 1856our @ISA = CFPlus::UI::Box::;
1477 1857
1478sub new { 1858sub new {
1479 my $class = shift; 1859 my $class = shift;
1480 1860
1481 $class->SUPER::new ( 1861 $class->SUPER::new (
1484 ) 1864 )
1485} 1865}
1486 1866
1487############################################################################# 1867#############################################################################
1488 1868
1489package CFClient::UI::Label; 1869package CFPlus::UI::Label;
1490 1870
1491our @ISA = CFClient::UI::DrawBG::; 1871our @ISA = CFPlus::UI::DrawBG::;
1492 1872
1493use CFClient::OpenGL; 1873use CFPlus::OpenGL;
1494 1874
1495sub new { 1875sub new {
1496 my ($class, %arg) = @_; 1876 my ($class, %arg) = @_;
1497 1877
1498 my $self = $class->SUPER::new ( 1878 my $self = $class->SUPER::new (
1501 #active_bg => none 1881 #active_bg => none
1502 #font => default_font 1882 #font => default_font
1503 #text => initial text 1883 #text => initial text
1504 #markup => initial narkup 1884 #markup => initial narkup
1505 #max_w => maximum pixel width 1885 #max_w => maximum pixel width
1886 #style => 0, # render flags
1506 ellipsise => 3, # end 1887 ellipsise => 3, # end
1507 layout => (new CFClient::Layout), 1888 layout => (new CFPlus::Layout),
1508 fontsize => 1, 1889 fontsize => 1,
1509 align => -1, 1890 align => -1,
1510 valign => -1, 1891 valign => -1,
1511 padding_x => 2, 1892 padding_x => 2,
1512 padding_y => 2, 1893 padding_y => 2,
1513 can_events => 0, 1894 can_events => 0,
1514 %arg 1895 %arg
1515 ); 1896 );
1516 1897
1517 if (exists $self->{template}) { 1898 if (exists $self->{template}) {
1518 my $layout = new CFClient::Layout; 1899 my $layout = new CFPlus::Layout;
1519 $layout->set_text (delete $self->{template}); 1900 $layout->set_text (delete $self->{template});
1520 $self->{template} = $layout; 1901 $self->{template} = $layout;
1521 } 1902 }
1522 1903
1523 if (exists $self->{markup}) { 1904 if (exists $self->{markup}) {
1527 } 1908 }
1528 1909
1529 $self 1910 $self
1530} 1911}
1531 1912
1532sub escape($) {
1533 local $_ = $_[0];
1534
1535 s/&/&amp;/g;
1536 s/>/&gt;/g;
1537 s/</&lt;/g;
1538
1539 $_
1540}
1541
1542sub update { 1913sub update {
1543 my ($self) = @_; 1914 my ($self) = @_;
1544 1915
1545 delete $self->{texture}; 1916 delete $self->{texture};
1546 $self->SUPER::update; 1917 $self->SUPER::update;
1551 1922
1552 delete $self->{ox}; 1923 delete $self->{ox};
1553 $self->SUPER::realloc; 1924 $self->SUPER::realloc;
1554} 1925}
1555 1926
1927sub clear {
1928 my ($self) = @_;
1929
1930 $self->set_text ("");
1931}
1932
1556sub set_text { 1933sub set_text {
1557 my ($self, $text) = @_; 1934 my ($self, $text) = @_;
1558 1935
1559 return if $self->{text} eq "T$text"; 1936 return if $self->{text} eq "T$text";
1560 $self->{text} = "T$text"; 1937 $self->{text} = "T$text";
1561 1938
1562 $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba;
1563 $self->{layout}->set_text ($text); 1939 $self->{layout}->set_text ($text);
1564 1940
1565 delete $self->{size_req}; 1941 delete $self->{size_req};
1566 $self->realloc; 1942 $self->realloc;
1567 $self->update; 1943 $self->update;
1573 return if $self->{text} eq "M$markup"; 1949 return if $self->{text} eq "M$markup";
1574 $self->{text} = "M$markup"; 1950 $self->{text} = "M$markup";
1575 1951
1576 my $rgba = $markup =~ /span.*(?:foreground|background)/; 1952 my $rgba = $markup =~ /span.*(?:foreground|background)/;
1577 1953
1578 $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba;
1579 $self->{layout}->set_markup ($markup); 1954 $self->{layout}->set_markup ($markup);
1580 1955
1581 delete $self->{size_req}; 1956 delete $self->{size_req};
1582 $self->realloc; 1957 $self->realloc;
1583 $self->update; 1958 $self->update;
1585 1960
1586sub size_request { 1961sub size_request {
1587 my ($self) = @_; 1962 my ($self) = @_;
1588 1963
1589 $self->{size_req} ||= do { 1964 $self->{size_req} ||= do {
1965 my ($max_w, $max_h) = $self->get_max_wh;
1966
1590 $self->{layout}->set_font ($self->{font}) if $self->{font}; 1967 $self->{layout}->set_font ($self->{font}) if $self->{font};
1591 $self->{layout}->set_width ($self->{max_w} || -1); 1968 $self->{layout}->set_width ($self->{max_w} || $max_w || -1);
1592 $self->{layout}->set_ellipsise ($self->{ellipsise}); 1969 $self->{layout}->set_ellipsise ($self->{ellipsise});
1593 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise}); 1970 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise});
1594 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); 1971 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
1595 1972
1596 my ($w, $h) = $self->{layout}->size; 1973 my ($w, $h) = $self->{layout}->size;
1597 1974
1598 if (exists $self->{template}) { 1975 if (exists $self->{template}) {
1599 $self->{template}->set_font ($self->{font}) if $self->{font}; 1976 $self->{template}->set_font ($self->{font}) if $self->{font};
1977 $self->{template}->set_width ($self->{max_w} || -1);
1600 $self->{template}->set_height ($self->{fontsize} * $::FONTSIZE); 1978 $self->{template}->set_height ($self->{fontsize} * $::FONTSIZE);
1601 1979
1602 my ($w2, $h2) = $self->{template}->size; 1980 my ($w2, $h2) = $self->{template}->size;
1603 1981
1604 $w = List::Util::max $w, $w2; 1982 $w = List::Util::max $w, $w2;
1609 }; 1987 };
1610 1988
1611 @{ $self->{size_req} } 1989 @{ $self->{size_req} }
1612} 1990}
1613 1991
1992sub baseline_shift {
1993 $_[0]{layout}->descent
1994}
1995
1614sub size_allocate { 1996sub invoke_size_allocate {
1615 my ($self, $w, $h) = @_; 1997 my ($self, $w, $h) = @_;
1616 1998
1617 delete $self->{ox}; 1999 delete $self->{ox};
1618 2000
1619 delete $self->{texture} 2001 delete $self->{texture}
1620 unless $w >= $self->{req_w} && $self->{old_w} >= $self->{req_w}; 2002 unless $w >= $self->{req_w} && $self->{old_w} >= $self->{req_w};
2003
2004 1
1621} 2005}
1622 2006
1623sub set_fontsize { 2007sub set_fontsize {
1624 my ($self, $fontsize) = @_; 2008 my ($self, $fontsize) = @_;
1625 2009
1626 $self->{fontsize} = $fontsize; 2010 $self->{fontsize} = $fontsize;
2011 delete $self->{size_req};
1627 delete $self->{texture}; 2012 delete $self->{texture};
1628 2013
1629 $self->realloc; 2014 $self->realloc;
1630} 2015}
1631 2016
1632sub reconfigure { 2017sub reconfigure {
1633 my ($self) = @_; 2018 my ($self) = @_;
1634 2019
1635 delete $self->{size_req}; 2020 delete $self->{size_req};
2021 delete $self->{texture};
1636 2022
1637 $self->SUPER::reconfigure; 2023 $self->SUPER::reconfigure;
1638} 2024}
1639 2025
1640sub _draw { 2026sub _draw {
1641 my ($self) = @_; 2027 my ($self) = @_;
1642 2028
1643 $self->SUPER::_draw; # draw background, if applicable 2029 $self->SUPER::_draw; # draw background, if applicable
1644 2030
1645 my $tex = $self->{texture} ||= do { 2031 my $size = $self->{texture} ||= do {
1646 $self->{layout}->set_foreground (@{$self->{fg}}); 2032 $self->{layout}->set_foreground (@{$self->{fg}});
1647 $self->{layout}->set_font ($self->{font}) if $self->{font}; 2033 $self->{layout}->set_font ($self->{font}) if $self->{font};
1648 $self->{layout}->set_width ($self->{w}); 2034 $self->{layout}->set_width ($self->{w});
1649 $self->{layout}->set_ellipsise ($self->{ellipsise}); 2035 $self->{layout}->set_ellipsise ($self->{ellipsise});
1650 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise}); 2036 $self->{layout}->set_single_paragraph_mode ($self->{ellipsise});
1651 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); 2037 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
1652 2038
1653 new_from_layout CFClient::Texture $self->{layout} 2039 [$self->{layout}->size]
1654 }; 2040 };
1655 2041
1656 unless (exists $self->{ox}) { 2042 unless (exists $self->{ox}) {
1657 $self->{ox} = int ($self->{align} < 0 ? $self->{padding_x} 2043 $self->{ox} = int ($self->{align} < 0 ? $self->{padding_x}
1658 : $self->{align} > 0 ? $self->{w} - $tex->{w} - $self->{padding_x} 2044 : $self->{align} > 0 ? $self->{w} - $size->[0] - $self->{padding_x}
1659 : ($self->{w} - $tex->{w}) * 0.5); 2045 : ($self->{w} - $size->[0]) * 0.5);
1660 2046
1661 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding_y} 2047 $self->{oy} = int ($self->{valign} < 0 ? $self->{padding_y}
1662 : $self->{valign} > 0 ? $self->{h} - $tex->{h} - $self->{padding_y} 2048 : $self->{valign} > 0 ? $self->{h} - $size->[1] - $self->{padding_y}
1663 : ($self->{h} - $tex->{h}) * 0.5); 2049 : ($self->{h} - $size->[1]) * 0.5);
2050
2051 $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style});
1664 }; 2052 };
1665 2053
1666 glEnable GL_TEXTURE_2D; 2054# unless ($self->{list}) {
1667 2055# $self->{list} = CFPlus::OpenGL::glGenList;
1668 my $w = List::Util::min $self->{w} + 4, $tex->{w}; 2056# CFPlus::OpenGL::glNewList $self->{list};
1669 my $h = List::Util::min $self->{h} + 2, $tex->{h}; 2057# $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style});
1670 2058# CFPlus::OpenGL::glEndList;
1671 if ($tex->{format} == GL_ALPHA) {
1672 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
1673 glColor @{$self->{fg}};
1674 $tex->draw_quad_alpha ($self->{ox}, $self->{oy}, $w, $h);
1675 } else {
1676 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1677 $tex->draw_quad_alpha_premultiplied ($self->{ox}, $self->{oy}, $w, $h);
1678 } 2059# }
2060#
2061# CFPlus::OpenGL::glCallList $self->{list};
1679 2062
1680 glDisable GL_TEXTURE_2D; 2063 $self->{layout}->draw;
1681} 2064}
2065
2066#sub destroy {
2067# my ($self) = @_;
2068#
2069# CFPlus::OpenGL::glDeleteList delete $self->{list} if $self->{list};
2070#
2071# $self->SUPER::destroy;
2072#}
1682 2073
1683############################################################################# 2074#############################################################################
1684 2075
1685package CFClient::UI::EntryBase; 2076package CFPlus::UI::EntryBase;
1686 2077
1687our @ISA = CFClient::UI::Label::; 2078our @ISA = CFPlus::UI::Label::;
1688 2079
1689use CFClient::OpenGL; 2080use CFPlus::OpenGL;
1690 2081
1691sub new { 2082sub new {
1692 my $class = shift; 2083 my $class = shift;
1693 2084
1694 $class->SUPER::new ( 2085 $class->SUPER::new (
1695 fg => [1, 1, 1], 2086 fg => [1, 1, 1],
1696 bg => [0, 0, 0, 0.2], 2087 bg => [0, 0, 0, 0.2],
2088 outline => [0.6, 0.3, 0.1],
1697 active_bg => [1, 1, 1, 0.5], 2089 active_bg => [0, 0, 1, .2],
1698 active_fg => [0, 0, 0], 2090 active_fg => [1, 1, 1],
2091 active_outline => [1, 1, 0],
1699 can_hover => 1, 2092 can_hover => 1,
1700 can_focus => 1, 2093 can_focus => 1,
1701 valign => 0, 2094 valign => 0,
1702 can_events => 1, 2095 can_events => 1,
2096 ellipsise => 0,
1703 #text => ... 2097 #text => ...
1704 #hidden => "*", 2098 #hidden => "*",
1705 @_ 2099 @_
1706 ) 2100 )
1707} 2101}
1718 2112
1719 $text =~ s/./*/g if $self->{hidden}; 2113 $text =~ s/./*/g if $self->{hidden};
1720 $self->{layout}->set_text ("$text "); 2114 $self->{layout}->set_text ("$text ");
1721 delete $self->{size_req}; 2115 delete $self->{size_req};
1722 2116
1723 $self->_emit (changed => $self->{text}); 2117 $self->emit (changed => $self->{text});
1724 2118
1725 $self->realloc; 2119 $self->realloc;
1726 $self->update; 2120 $self->update;
1727} 2121}
1728 2122
1743 my ($w, $h) = $self->SUPER::size_request; 2137 my ($w, $h) = $self->SUPER::size_request;
1744 2138
1745 ($w + 1, $h) # add 1 for cursor 2139 ($w + 1, $h) # add 1 for cursor
1746} 2140}
1747 2141
1748sub key_down { 2142sub invoke_key_down {
1749 my ($self, $ev) = @_; 2143 my ($self, $ev) = @_;
1750 2144
1751 my $mod = $ev->{mod}; 2145 my $mod = $ev->{mod};
1752 my $sym = $ev->{sym}; 2146 my $sym = $ev->{sym};
1753 my $uni = $ev->{unicode}; 2147 my $uni = $ev->{unicode};
1754 2148
1755 my $text = $self->get_text; 2149 my $text = $self->get_text;
2150
2151 $self->{cursor} = List::Util::max 0, List::Util::min $self->{cursor}, length $text;
1756 2152
1757 if ($uni == 8) { 2153 if ($uni == 8) {
1758 substr $text, --$self->{cursor}, 1, "" if $self->{cursor}; 2154 substr $text, --$self->{cursor}, 1, "" if $self->{cursor};
1759 } elsif ($uni == 127) { 2155 } elsif ($uni == 127) {
1760 substr $text, $self->{cursor}, 1, ""; 2156 substr $text, $self->{cursor}, 1, "";
1761 } elsif ($sym == CFClient::SDLK_LEFT) { 2157 } elsif ($sym == CFPlus::SDLK_LEFT) {
1762 --$self->{cursor} if $self->{cursor}; 2158 --$self->{cursor} if $self->{cursor};
1763 } elsif ($sym == CFClient::SDLK_RIGHT) { 2159 } elsif ($sym == CFPlus::SDLK_RIGHT) {
1764 ++$self->{cursor} if $self->{cursor} < length $self->{text}; 2160 ++$self->{cursor} if $self->{cursor} < length $self->{text};
1765 } elsif ($sym == CFClient::SDLK_HOME) { 2161 } elsif ($sym == CFPlus::SDLK_HOME) {
2162 # what a hack
2163 $self->{cursor} =
2164 (substr $self->{text}, 0, $self->{cursor}) =~ /^(.*\012)/
2165 ? length $1
2166 : 0;
2167 } elsif ($sym == CFPlus::SDLK_END) {
2168 # uh, again
2169 $self->{cursor} =
2170 (substr $self->{text}, $self->{cursor}) =~ /^([^\012]*)\012/
2171 ? $self->{cursor} + length $1
2172 : length $self->{text};
2173 } elsif ($uni == 21) { # ctrl-u
2174 $text = "";
1766 $self->{cursor} = 0; 2175 $self->{cursor} = 0;
1767 } elsif ($sym == CFClient::SDLK_END) {
1768 $self->{cursor} = length $text;
1769 } elsif ($uni == 27) { 2176 } elsif ($uni == 27) {
1770 $self->_emit ('escape'); 2177 $self->emit ('escape');
1771 } elsif ($uni) { 2178 } elsif ($uni == 0x0d) {
2179 substr $text, $self->{cursor}++, 0, "\012";
2180 } elsif ($uni >= 0x20) {
1772 substr $text, $self->{cursor}++, 0, chr $uni; 2181 substr $text, $self->{cursor}++, 0, chr $uni;
1773 } else { 2182 } else {
1774 return 0; 2183 return 0;
1775 } 2184 }
1776 2185
1777 $self->_set_text ($text); 2186 $self->_set_text ($text);
1778 2187
1779 $self->realloc; 2188 $self->realloc;
2189 $self->update;
1780 2190
1781 1 2191 1
1782} 2192}
1783 2193
1784sub focus_in { 2194sub invoke_focus_in {
1785 my ($self) = @_; 2195 my ($self) = @_;
1786 2196
1787 $self->{last_activity} = $::NOW; 2197 $self->{last_activity} = $::NOW;
1788 2198
1789 $self->SUPER::focus_in; 2199 $self->SUPER::invoke_focus_in
1790} 2200}
1791 2201
1792sub button_down { 2202sub invoke_button_down {
1793 my ($self, $ev, $x, $y) = @_; 2203 my ($self, $ev, $x, $y) = @_;
1794 2204
1795 $self->SUPER::button_down ($ev, $x, $y); 2205 $self->SUPER::invoke_button_down ($ev, $x, $y);
1796 2206
1797 my $idx = $self->{layout}->xy_to_index ($x, $y); 2207 my $idx = $self->{layout}->xy_to_index ($x, $y);
1798 2208
1799 # byte-index to char-index 2209 # byte-index to char-index
1800 my $text = $self->{text}; 2210 my $text = $self->{text};
1801 utf8::encode $text; 2211 utf8::encode $text; $text = substr $text, 0, $idx; utf8::decode $text;
1802 $self->{cursor} = length substr $text, 0, $idx; 2212 $self->{cursor} = length $text;
1803 2213
1804 $self->_set_text ($self->{text}); 2214 $self->_set_text ($self->{text});
1805 $self->update; 2215 $self->update;
1806 2216
1807 1 2217 1
1808} 2218}
1809 2219
1810sub mouse_motion { 2220sub invoke_mouse_motion {
1811 my ($self, $ev, $x, $y) = @_; 2221 my ($self, $ev, $x, $y) = @_;
1812# printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d# 2222# printf "M %d,%d %d,%d\n", $ev->motion_x, $ev->motion_y, $x, $y;#d#
1813 2223
1814 0 2224 1
1815} 2225}
1816 2226
1817sub _draw { 2227sub _draw {
1818 my ($self) = @_; 2228 my ($self) = @_;
1819 2229
1826 glColor_premultiply @{$self->{bg}}; 2236 glColor_premultiply @{$self->{bg}};
1827 } 2237 }
1828 2238
1829 glEnable GL_BLEND; 2239 glEnable GL_BLEND;
1830 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; 2240 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
1831 glBegin GL_QUADS;
1832 glVertex 0 , 0;
1833 glVertex 0 , $self->{h};
1834 glVertex $self->{w}, $self->{h}; 2241 glRect 0, 0, $self->{w}, $self->{h};
1835 glVertex $self->{w}, 0;
1836 glEnd;
1837 glDisable GL_BLEND; 2242 glDisable GL_BLEND;
1838 2243
1839 $self->SUPER::_draw; 2244 $self->SUPER::_draw;
1840 2245
1841 #TODO: force update every cursor change :( 2246 #TODO: force update every cursor change :(
1843 2248
1844 unless (exists $self->{cur_h}) { 2249 unless (exists $self->{cur_h}) {
1845 my $text = substr $self->{text}, 0, $self->{cursor}; 2250 my $text = substr $self->{text}, 0, $self->{cursor};
1846 utf8::encode $text; 2251 utf8::encode $text;
1847 2252
1848 @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text) 2253 @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text);
1849 } 2254 }
1850 2255
1851 glColor @{$self->{fg}}; 2256 glColor_premultiply @{$self->{active_fg}};
1852 glBegin GL_LINES; 2257 glBegin GL_LINES;
1853 glVertex $self->{cur_x} + $self->{ox}, $self->{cur_y} + $self->{oy}; 2258 glVertex $self->{cur_x} + $self->{ox} + .5, $self->{cur_y} + $self->{oy};
1854 glVertex $self->{cur_x} + $self->{ox}, $self->{cur_y} + $self->{oy} + $self->{cur_h}; 2259 glVertex $self->{cur_x} + $self->{ox} + .5, $self->{cur_y} + $self->{oy} + $self->{cur_h};
1855 glEnd; 2260 glEnd;
1856 }
1857}
1858 2261
2262 glLineWidth 3;
2263 glColor @{$self->{active_outline}};
2264 glRect_lineloop 1.5, 1.5, $self->{w} - 1.5, $self->{h} - 1.5;
2265 glLineWidth 1;
2266
2267 } else {
2268 glColor @{$self->{outline}};
2269 glBegin GL_LINE_STRIP;
2270 glVertex .5, $self->{h} * .5;
2271 glVertex .5, $self->{h} - 2.5;
2272 glVertex $self->{w} - .5, $self->{h} - 2.5;
2273 glVertex $self->{w} - .5, $self->{h} * .5;
2274 glEnd;
2275 }
2276}
2277
2278#############################################################################
2279
1859package CFClient::UI::Entry; 2280package CFPlus::UI::Entry;
1860 2281
1861our @ISA = CFClient::UI::EntryBase::; 2282our @ISA = CFPlus::UI::EntryBase::;
1862 2283
1863use CFClient::OpenGL; 2284use CFPlus::OpenGL;
1864 2285
1865sub key_down { 2286sub invoke_key_down {
1866 my ($self, $ev) = @_; 2287 my ($self, $ev) = @_;
1867 2288
1868 my $sym = $ev->{sym}; 2289 my $sym = $ev->{sym};
1869 2290
1870 if ($sym == 13) { 2291 if ($ev->{uni} == 0x0d || $sym == 13) {
1871 unshift @{$self->{history}}, 2292 unshift @{$self->{history}},
1872 my $txt = $self->get_text; 2293 my $txt = $self->get_text;
2294
1873 $self->{history_pointer} = -1; 2295 $self->{history_pointer} = -1;
1874 $self->{history_saveback} = ''; 2296 $self->{history_saveback} = '';
1875 $self->_emit (activate => $txt); 2297 $self->emit (activate => $txt);
1876 $self->update; 2298 $self->update;
1877 2299
1878 } elsif ($sym == CFClient::SDLK_UP) { 2300 } elsif ($sym == CFPlus::SDLK_UP) {
1879 if ($self->{history_pointer} < 0) { 2301 if ($self->{history_pointer} < 0) {
1880 $self->{history_saveback} = $self->get_text; 2302 $self->{history_saveback} = $self->get_text;
1881 } 2303 }
1882 if (@{$self->{history} || []} > 0) { 2304 if (@{$self->{history} || []} > 0) {
1883 $self->{history_pointer}++; 2305 $self->{history_pointer}++;
1885 $self->{history_pointer} = @{$self->{history} || []} - 1; 2307 $self->{history_pointer} = @{$self->{history} || []} - 1;
1886 } 2308 }
1887 $self->set_text ($self->{history}->[$self->{history_pointer}]); 2309 $self->set_text ($self->{history}->[$self->{history_pointer}]);
1888 } 2310 }
1889 2311
1890 } elsif ($sym == CFClient::SDLK_DOWN) { 2312 } elsif ($sym == CFPlus::SDLK_DOWN) {
1891 $self->{history_pointer}--; 2313 $self->{history_pointer}--;
1892 $self->{history_pointer} = -1 if $self->{history_pointer} < 0; 2314 $self->{history_pointer} = -1 if $self->{history_pointer} < 0;
1893 2315
1894 if ($self->{history_pointer} >= 0) { 2316 if ($self->{history_pointer} >= 0) {
1895 $self->set_text ($self->{history}->[$self->{history_pointer}]); 2317 $self->set_text ($self->{history}->[$self->{history_pointer}]);
1896 } else { 2318 } else {
1897 $self->set_text ($self->{history_saveback}); 2319 $self->set_text ($self->{history_saveback});
1898 } 2320 }
1899 2321
1900 } else { 2322 } else {
1901 return $self->SUPER::key_down ($ev) 2323 return $self->SUPER::invoke_key_down ($ev)
1902 } 2324 }
1903 2325
1904 1 2326 1
1905} 2327}
1906 2328
1907############################################################################# 2329#############################################################################
1908 2330
2331package CFPlus::UI::TextEdit;
2332
2333our @ISA = CFPlus::UI::EntryBase::;
2334
2335use CFPlus::OpenGL;
2336
2337sub move_cursor_ver {
2338 my ($self, $dy) = @_;
2339
2340 my ($y, $x) = $self->{layout}->index_to_line_x ($self->{cursor});
2341
2342 $y += $dy;
2343
2344 if (defined (my $index = $self->{layout}->line_x_to_index ($y, $x))) {
2345 $self->{cursor} = $index;
2346 delete $self->{cur_h};
2347 $self->update;
2348 return;
2349 }
2350}
2351
2352sub invoke_key_down {
2353 my ($self, $ev) = @_;
2354
2355 my $sym = $ev->{sym};
2356
2357 if ($sym == CFPlus::SDLK_UP) {
2358 $self->move_cursor_ver (-1);
2359 } elsif ($sym == CFPlus::SDLK_DOWN) {
2360 $self->move_cursor_ver (+1);
2361 } else {
2362 return $self->SUPER::invoke_key_down ($ev)
2363 }
2364
2365 1
2366}
2367
2368#############################################################################
2369
1909package CFClient::UI::Button; 2370package CFPlus::UI::ButtonBin;
1910 2371
1911our @ISA = CFClient::UI::Label::; 2372our @ISA = CFPlus::UI::Bin::;
1912 2373
1913use CFClient::OpenGL; 2374use CFPlus::OpenGL;
1914 2375
1915my @tex = 2376my @tex =
1916 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2377 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
1917 qw(b1_button_active.png); 2378 qw(b1_button_inactive.png b1_button_active.png);
1918 2379
1919sub new { 2380sub new {
1920 my $class = shift; 2381 my $class = shift;
1921 2382
1922 $class->SUPER::new ( 2383 $class->SUPER::new (
1923 padding_x => 4,
1924 padding_y => 4,
1925 fg => [1, 1, 1],
1926 active_fg => [0, 0, 1],
1927 can_hover => 1, 2384 can_hover => 1,
1928 align => 0, 2385 align => 0,
1929 valign => 0, 2386 valign => 0,
1930 can_events => 1, 2387 can_events => 1,
1931 @_ 2388 @_
1932 ) 2389 )
1933} 2390}
1934 2391
1935sub activate { }
1936
1937sub button_up { 2392sub invoke_button_up {
1938 my ($self, $ev, $x, $y) = @_; 2393 my ($self, $ev, $x, $y) = @_;
1939 2394
1940 $self->emit ("activate") 2395 $self->emit ("activate")
1941 if $x >= 0 && $x < $self->{w} 2396 if $x >= 0 && $x < $self->{w}
1942 && $y >= 0 && $y < $self->{h}; 2397 && $y >= 0 && $y < $self->{h};
1945} 2400}
1946 2401
1947sub _draw { 2402sub _draw {
1948 my ($self) = @_; 2403 my ($self) = @_;
1949 2404
1950 local $self->{fg} = $GRAB == $self ? $self->{active_fg} : $self->{fg};
1951
1952 glEnable GL_TEXTURE_2D; 2405 glEnable GL_TEXTURE_2D;
1953 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; 2406 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
1954 glColor 0, 0, 0, 1; 2407 glColor 0, 0, 0, 1;
1955 2408
2409 my $tex = $tex[$GRAB == $self];
1956 $tex[0]->draw_quad_alpha (0, 0, $self->{w}, $self->{h}); 2410 $tex->draw_quad_alpha (0, 0, $self->{w}, $self->{h});
1957 2411
1958 glDisable GL_TEXTURE_2D; 2412 glDisable GL_TEXTURE_2D;
1959 2413
1960 $self->SUPER::_draw; 2414 $self->SUPER::_draw;
1961} 2415}
1962 2416
1963############################################################################# 2417#############################################################################
1964 2418
1965package CFClient::UI::CheckBox; 2419package CFPlus::UI::Button;
1966 2420
1967our @ISA = CFClient::UI::DrawBG::; 2421our @ISA = CFPlus::UI::Label::;
2422
2423use CFPlus::OpenGL;
1968 2424
1969my @tex = 2425my @tex =
1970 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2426 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
2427 qw(b1_button_inactive.png b1_button_active.png);
2428
2429sub new {
2430 my $class = shift;
2431
2432 $class->SUPER::new (
2433 padding_x => 4,
2434 padding_y => 4,
2435 fg => [1.0, 1.0, 1.0],
2436 active_fg => [0.8, 0.8, 0.8],
2437 can_hover => 1,
2438 align => 0,
2439 valign => 0,
2440 can_events => 1,
2441 @_
2442 )
2443}
2444
2445sub invoke_button_up {
2446 my ($self, $ev, $x, $y) = @_;
2447
2448 $self->emit ("activate")
2449 if $x >= 0 && $x < $self->{w}
2450 && $y >= 0 && $y < $self->{h};
2451
2452 1
2453}
2454
2455sub _draw {
2456 my ($self) = @_;
2457
2458 local $self->{fg} = $GRAB == $self ? $self->{active_fg} : $self->{fg};
2459
2460 glEnable GL_TEXTURE_2D;
2461 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
2462 glColor 0, 0, 0, 1;
2463
2464 my $tex = $tex[$GRAB == $self];
2465 $tex->draw_quad_alpha (0, 0, $self->{w}, $self->{h});
2466
2467 glDisable GL_TEXTURE_2D;
2468
2469 $self->SUPER::_draw;
2470}
2471
2472#############################################################################
2473
2474package CFPlus::UI::CheckBox;
2475
2476our @ISA = CFPlus::UI::DrawBG::;
2477
2478my @tex =
2479 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
1971 qw(c1_checkbox_bg.png c1_checkbox_active.png); 2480 qw(c1_checkbox_bg.png c1_checkbox_active.png);
1972 2481
1973use CFClient::OpenGL; 2482use CFPlus::OpenGL;
1974 2483
1975sub new { 2484sub new {
1976 my $class = shift; 2485 my $class = shift;
1977 2486
1978 $class->SUPER::new ( 2487 $class->SUPER::new (
1992 my ($self) = @_; 2501 my ($self) = @_;
1993 2502
1994 (6) x 2 2503 (6) x 2
1995} 2504}
1996 2505
2506sub toggle {
2507 my ($self) = @_;
2508
2509 $self->{state} = !$self->{state};
2510 $self->emit (changed => $self->{state});
2511 $self->update;
2512}
2513
1997sub button_down { 2514sub invoke_button_down {
1998 my ($self, $ev, $x, $y) = @_; 2515 my ($self, $ev, $x, $y) = @_;
1999 2516
2000 if ($x >= $self->{padding_x} && $x < $self->{w} - $self->{padding_x} 2517 if ($x >= $self->{padding_x} && $x < $self->{w} - $self->{padding_x}
2001 && $y >= $self->{padding_y} && $y < $self->{h} - $self->{padding_y}) { 2518 && $y >= $self->{padding_y} && $y < $self->{h} - $self->{padding_y}) {
2002 $self->{state} = !$self->{state}; 2519 $self->toggle;
2003 $self->_emit (changed => $self->{state});
2004 } else { 2520 } else {
2005 return 0 2521 return 0
2006 } 2522 }
2007 2523
2008 1 2524 1
2011sub _draw { 2527sub _draw {
2012 my ($self) = @_; 2528 my ($self) = @_;
2013 2529
2014 $self->SUPER::_draw; 2530 $self->SUPER::_draw;
2015 2531
2016 glTranslate $self->{padding_x} + 0.375, $self->{padding_y} + 0.375, 0; 2532 glTranslate $self->{padding_x}, $self->{padding_y}, 0;
2017 2533
2018 my ($w, $h) = @$self{qw(w h)}; 2534 my ($w, $h) = @$self{qw(w h)};
2019 2535
2020 my $s = List::Util::min $w - $self->{padding_x} * 2, $h - $self->{padding_y} * 2; 2536 my $s = List::Util::min $w - $self->{padding_x} * 2, $h - $self->{padding_y} * 2;
2021 2537
2028 glDisable GL_TEXTURE_2D; 2544 glDisable GL_TEXTURE_2D;
2029} 2545}
2030 2546
2031############################################################################# 2547#############################################################################
2032 2548
2033package CFClient::UI::Image; 2549package CFPlus::UI::Image;
2034 2550
2035our @ISA = CFClient::UI::Base::; 2551our @ISA = CFPlus::UI::Base::;
2036 2552
2037use CFClient::OpenGL; 2553use CFPlus::OpenGL;
2038use Carp qw/confess/;
2039 2554
2040our %loaded_images; 2555our %texture_cache;
2041 2556
2042sub new { 2557sub new {
2043 my $class = shift; 2558 my $class = shift;
2044 2559
2045 my $self = $class->SUPER::new (can_events => 0, @_); 2560 my $self = $class->SUPER::new (
2561 can_events => 0,
2562 scale => 1,
2563 @_,
2564 );
2046 2565
2047 $self->{image} or confess "Image has 'image' not set. This is a fatal error!"; 2566 $self->{path} || $self->{tex}
2567 or Carp::croak "'path' or 'tex' attributes required";
2048 2568
2049 $loaded_images{$self->{image}} ||= 2569 $self->{tex} ||= $texture_cache{$self->{path}} ||=
2050 new_from_file CFClient::Texture CFClient::find_rcfile $self->{image}, mipmap => 1; 2570 new_from_file CFPlus::Texture CFPlus::find_rcfile $self->{path}, mipmap => 1;
2051 2571
2052 my $tex = $self->{tex} = $loaded_images{$self->{image}}; 2572 CFPlus::weaken $texture_cache{$self->{path}};
2053 2573
2054 Scalar::Util::weaken $loaded_images{$self->{image}}; 2574 $self->{aspect} ||= $self->{tex}{w} / $self->{tex}{h};
2055
2056 $self->{aspect} = $tex->{w} / $tex->{h};
2057 2575
2058 $self 2576 $self
2059} 2577}
2060 2578
2579sub STORABLE_freeze {
2580 my ($self, $cloning) = @_;
2581
2582 $self->{path}
2583 or die "cannot serialise CFPlus::UI::Image on non-loadable images\n";
2584
2585 $self->{path}
2586}
2587
2588sub STORABLE_attach {
2589 my ($self, $cloning, $path) = @_;
2590
2591 $self->new (path => $path)
2592}
2593
2061sub size_request { 2594sub size_request {
2062 my ($self) = @_; 2595 my ($self) = @_;
2063 2596
2064 ($self->{tex}->{w}, $self->{tex}->{h}) 2597 (int $self->{tex}{w} * $self->{scale}, int $self->{tex}{h} * $self->{scale})
2065} 2598}
2066 2599
2067sub _draw { 2600sub _draw {
2068 my ($self) = @_; 2601 my ($self) = @_;
2069 2602
2086 glDisable GL_TEXTURE_2D; 2619 glDisable GL_TEXTURE_2D;
2087} 2620}
2088 2621
2089############################################################################# 2622#############################################################################
2090 2623
2624package CFPlus::UI::ImageButton;
2625
2626our @ISA = CFPlus::UI::Image::;
2627
2628use CFPlus::OpenGL;
2629
2630my %textures;
2631
2632sub new {
2633 my $class = shift;
2634
2635 my $self = $class->SUPER::new (
2636 padding_x => 4,
2637 padding_y => 4,
2638 fg => [1, 1, 1],
2639 active_fg => [0, 0, 1],
2640 can_hover => 1,
2641 align => 0,
2642 valign => 0,
2643 can_events => 1,
2644 @_
2645 );
2646}
2647
2648sub invoke_button_down {
2649 my ($self, $ev, $x, $y) = @_;
2650
2651 1
2652}
2653
2654sub invoke_button_up {
2655 my ($self, $ev, $x, $y) = @_;
2656
2657 $self->emit ("activate")
2658 if $x >= 0 && $x < $self->{w}
2659 && $y >= 0 && $y < $self->{h};
2660
2661 1
2662}
2663
2664#############################################################################
2665
2091package CFClient::UI::VGauge; 2666package CFPlus::UI::VGauge;
2092 2667
2093our @ISA = CFClient::UI::Base::; 2668our @ISA = CFPlus::UI::Base::;
2094 2669
2095use List::Util qw(min max); 2670use List::Util qw(min max);
2096 2671
2097use CFClient::OpenGL; 2672use CFPlus::OpenGL;
2098 2673
2099my %tex = ( 2674my %tex = (
2100 food => [ 2675 food => [
2101 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2676 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
2102 qw/g1_food_gauge_empty.png g1_food_gauge_full.png/ 2677 qw/g1_food_gauge_empty.png g1_food_gauge_full.png/
2103 ], 2678 ],
2104 grace => [ 2679 grace => [
2105 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2680 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
2106 qw/g1_grace_gauge_empty.png g1_grace_gauge_full.png g1_grace_gauge_overflow.png/ 2681 qw/g1_grace_gauge_empty.png g1_grace_gauge_full.png g1_grace_gauge_overflow.png/
2107 ], 2682 ],
2108 hp => [ 2683 hp => [
2109 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2684 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
2110 qw/g1_hp_gauge_empty.png g1_hp_gauge_full.png/ 2685 qw/g1_hp_gauge_empty.png g1_hp_gauge_full.png/
2111 ], 2686 ],
2112 mana => [ 2687 mana => [
2113 map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } 2688 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 }
2114 qw/g1_mana_gauge_empty.png g1_mana_gauge_full.png g1_mana_gauge_overflow.png/ 2689 qw/g1_mana_gauge_empty.png g1_mana_gauge_full.png g1_mana_gauge_overflow.png/
2115 ], 2690 ],
2116); 2691);
2117 2692
2118# eg. VGauge->new (gauge => 'food'), default gauge: food 2693# eg. VGauge->new (gauge => 'food'), default gauge: food
2178 my $ycut1 = max 0, min 1, $ycut; 2753 my $ycut1 = max 0, min 1, $ycut;
2179 my $ycut2 = max 0, min 1, $ycut - 1; 2754 my $ycut2 = max 0, min 1, $ycut - 1;
2180 2755
2181 my $h1 = $self->{h} * (1 - $ycut1); 2756 my $h1 = $self->{h} * (1 - $ycut1);
2182 my $h2 = $self->{h} * (1 - $ycut2); 2757 my $h2 = $self->{h} * (1 - $ycut2);
2758 my $h3 = $self->{h};
2759
2760 $_ = $_ * (284-4)/288 + 4/288 for ($h1, $h2, $h3);
2183 2761
2184 glEnable GL_BLEND; 2762 glEnable GL_BLEND;
2185 glBlendFuncSeparate GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, 2763 glBlendFuncSeparate GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
2186 GL_ONE, GL_ONE_MINUS_SRC_ALPHA; 2764 GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
2187 glEnable GL_TEXTURE_2D; 2765 glEnable GL_TEXTURE_2D;
2206 2784
2207 if ($t3) { 2785 if ($t3) {
2208 glBindTexture GL_TEXTURE_2D, $t3->{name}; 2786 glBindTexture GL_TEXTURE_2D, $t3->{name};
2209 glBegin GL_QUADS; 2787 glBegin GL_QUADS;
2210 glTexCoord 0 , $t3->{t} * (1 - $ycut2); glVertex 0 , $h2; 2788 glTexCoord 0 , $t3->{t} * (1 - $ycut2); glVertex 0 , $h2;
2211 glTexCoord 0 , $t3->{t}; glVertex 0 , $self->{h}; 2789 glTexCoord 0 , $t3->{t}; glVertex 0 , $h3;
2212 glTexCoord $t3->{s}, $t3->{t}; glVertex $w, $self->{h}; 2790 glTexCoord $t3->{s}, $t3->{t}; glVertex $w, $h3;
2213 glTexCoord $t3->{s}, $t3->{t} * (1 - $ycut2); glVertex $w, $h2; 2791 glTexCoord $t3->{s}, $t3->{t} * (1 - $ycut2); glVertex $w, $h2;
2214 glEnd; 2792 glEnd;
2215 } 2793 }
2216 2794
2217 glDisable GL_BLEND; 2795 glDisable GL_BLEND;
2218 glDisable GL_TEXTURE_2D; 2796 glDisable GL_TEXTURE_2D;
2219} 2797}
2220 2798
2221############################################################################# 2799#############################################################################
2222 2800
2801package CFPlus::UI::Progress;
2802
2803our @ISA = CFPlus::UI::Label::;
2804
2805use CFPlus::OpenGL;
2806
2807sub new {
2808 my ($class, %arg) = @_;
2809
2810 my $self = $class->SUPER::new (
2811 fg => [1, 1, 1],
2812 bg => [0, 0, 1, 0.2],
2813 bar => [0.7, 0.5, 0.1, 0.8],
2814 outline => [0.4, 0.3, 0],
2815 fontsize => 0.9,
2816 valign => 0,
2817 align => 0,
2818 can_events => 1,
2819 ellipsise => 1,
2820 label => "%d%%",
2821 %arg,
2822 );
2823
2824 $self->set_value ($arg{value} || -1);
2825
2826 $self
2827}
2828
2829sub set_label {
2830 my ($self, $label) = @_;
2831
2832 return if $self->{label} eq $label;
2833 $self->{label} = $label;
2834
2835 $self->CFPlus::UI::Progress::set_value (0 + delete $self->{value});
2836}
2837
2838sub set_value {
2839 my ($self, $value) = @_;
2840
2841 if ($self->{value} ne $value) {
2842 $self->{value} = $value;
2843
2844 if ($value < 0) {
2845 $self->set_text ("-");
2846 } else {
2847 $self->set_text (sprintf $self->{label}, $value * 100);
2848 }
2849
2850 $self->update;
2851 }
2852}
2853
2854sub _draw {
2855 my ($self) = @_;
2856
2857 glEnable GL_BLEND;
2858 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
2859
2860 if ($self->{value} >= 0) {
2861 my $s = int 2 + ($self->{w} - 4) * $self->{value};
2862
2863 glColor_premultiply @{$self->{bar}};
2864 glRect 2, 2, $s, $self->{h} - 2;
2865 glColor_premultiply @{$self->{bg}};
2866 glRect $s, 2, $self->{w} - 2, $self->{h} - 2;
2867 }
2868
2869 glColor_premultiply @{$self->{outline}};
2870 glRect_lineloop 1.5, 1.5, $self->{w} - 1.5, $self->{h} - 1.5;
2871
2872 glDisable GL_BLEND;
2873
2874 {
2875 local $self->{bg}; # do not draw background
2876 $self->SUPER::_draw;
2877 }
2878}
2879
2880#############################################################################
2881
2882package CFPlus::UI::ExperienceProgress;
2883
2884our @ISA = CFPlus::UI::Progress::;
2885
2886sub new {
2887 my ($class, %arg) = @_;
2888
2889 my $self = $class->SUPER::new (
2890 tooltip => sub {
2891 my ($self) = @_;
2892
2893 sprintf "level %d\n%s points\n%s next level\n%s to go",
2894 $self->{lvl},
2895 ::formsep ($self->{exp}),
2896 ::formsep ($self->{nxt}),
2897 ::formsep ($self->{nxt} - $self->{exp}),
2898 },
2899 %arg
2900 );
2901
2902 $::CONN->{on_exp_update}{$self+0} = sub { $self->set_value ($self->{value}) }
2903 if $::CONN;
2904
2905 $self
2906}
2907
2908sub DESTROY {
2909 my ($self) = @_;
2910
2911 delete $::CONN->{on_exp_update}{$self+0}
2912 if $::CONN;
2913
2914 $self->SUPER::DESTROY;
2915}
2916
2917sub set_value {
2918 my ($self, $lvl, $exp) = @_;
2919
2920 $self->{lvl} = $lvl;
2921 $self->{exp} = $exp;
2922
2923 my $v = -1;
2924
2925 if ($::CONN && (my $table = $::CONN->{exp_table})) {
2926 my $l0 = $table->[$lvl - 1];
2927 my $l1 = $table->[$lvl];
2928
2929 $self->{nxt} = $l1;
2930
2931 $v = ($exp - $l0) / ($l1 - $l0);
2932 }
2933
2934 $self->SUPER::set_value ($v);
2935}
2936
2937#############################################################################
2938
2223package CFClient::UI::Gauge; 2939package CFPlus::UI::Gauge;
2224 2940
2225our @ISA = CFClient::UI::VBox::; 2941our @ISA = CFPlus::UI::VBox::;
2226 2942
2227sub new { 2943sub new {
2228 my ($class, %arg) = @_; 2944 my ($class, %arg) = @_;
2229 2945
2230 my $self = $class->SUPER::new ( 2946 my $self = $class->SUPER::new (
2232 can_hover => 1, 2948 can_hover => 1,
2233 can_events => 1, 2949 can_events => 1,
2234 %arg, 2950 %arg,
2235 ); 2951 );
2236 2952
2237 $self->add ($self->{value} = new CFClient::UI::Label valign => +1, align => 0, template => "999"); 2953 $self->add ($self->{value} = new CFPlus::UI::Label valign => +1, align => 0, template => "999");
2238 $self->add ($self->{gauge} = new CFClient::UI::VGauge type => $self->{type}, expand => 1, can_hover => 1); 2954 $self->add ($self->{gauge} = new CFPlus::UI::VGauge type => $self->{type}, expand => 1, can_hover => 1);
2239 $self->add ($self->{max} = new CFClient::UI::Label valign => -1, align => 0, template => "999"); 2955 $self->add ($self->{max} = new CFPlus::UI::Label valign => -1, align => 0, template => "999");
2240 2956
2241 $self 2957 $self
2242} 2958}
2243 2959
2244sub set_fontsize { 2960sub set_fontsize {
2265 $self->{value}->set_text ($val); 2981 $self->{value}->set_text ($val);
2266} 2982}
2267 2983
2268############################################################################# 2984#############################################################################
2269 2985
2270package CFClient::UI::Slider; 2986package CFPlus::UI::Slider;
2271 2987
2272use strict; 2988use strict;
2273 2989
2274use CFClient::OpenGL; 2990use CFPlus::OpenGL;
2275 2991
2276our @ISA = CFClient::UI::DrawBG::; 2992our @ISA = CFPlus::UI::DrawBG::;
2277 2993
2278my @tex = 2994my @tex =
2279 map { new_from_file CFClient::Texture CFClient::find_rcfile $_ } 2995 map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_ }
2280 qw(s1_slider.png s1_slider_bg.png); 2996 qw(s1_slider.png s1_slider_bg.png);
2281 2997
2282sub new { 2998sub new {
2283 my $class = shift; 2999 my $class = shift;
2284 3000
2305 $self->update; 3021 $self->update;
2306 3022
2307 $self 3023 $self
2308} 3024}
2309 3025
2310sub changed { }
2311
2312sub set_range { 3026sub set_range {
2313 my ($self, $range) = @_; 3027 my ($self, $range) = @_;
2314 3028
2315 ($range, $self->{range}) = ($self->{range}, $range); 3029 ($range, $self->{range}) = ($self->{range}, $range);
2316 3030
2336 if $unit; 3050 if $unit;
2337 3051
2338 @{$self->{range}} = ($value, $lo, $hi, $page, $unit); 3052 @{$self->{range}} = ($value, $lo, $hi, $page, $unit);
2339 3053
2340 if ($value != $old_value) { 3054 if ($value != $old_value) {
2341 $self->_emit (changed => $value); 3055 $self->emit (changed => $value);
2342 $self->update; 3056 $self->update;
2343 } 3057 }
2344} 3058}
2345 3059
2346sub size_request { 3060sub size_request {
2347 my ($self) = @_; 3061 my ($self) = @_;
2348 3062
2349 ($self->{req_w}, $self->{req_h}) 3063 ($self->{req_w}, $self->{req_h})
2350} 3064}
2351 3065
2352sub button_down { 3066sub invoke_button_down {
2353 my ($self, $ev, $x, $y) = @_; 3067 my ($self, $ev, $x, $y) = @_;
2354 3068
2355 $self->SUPER::button_down ($ev, $x, $y); 3069 $self->SUPER::invoke_button_down ($ev, $x, $y);
2356 3070
2357 $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x]; 3071 $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x];
2358 3072
2359 $self->mouse_motion ($ev, $x, $y) 3073 $self->invoke_mouse_motion ($ev, $x, $y);
2360}
2361 3074
3075 1
3076}
3077
2362sub mouse_motion { 3078sub invoke_mouse_motion {
2363 my ($self, $ev, $x, $y) = @_; 3079 my ($self, $ev, $x, $y) = @_;
2364 3080
2365 if ($GRAB == $self) { 3081 if ($GRAB == $self) {
2366 my ($x, $w) = $self->{vertical} ? ($y, $self->{h}) : ($x, $self->{w}); 3082 my ($x, $w) = $self->{vertical} ? ($y, $self->{h}) : ($x, $self->{w});
2367 3083
2371 3087
2372 $self->set_value ($self->{click}[0] + $x * ($hi - $page - $lo)); 3088 $self->set_value ($self->{click}[0] + $x * ($hi - $page - $lo));
2373 } else { 3089 } else {
2374 return 0; 3090 return 0;
2375 } 3091 }
3092
3093 1
3094}
3095
3096sub invoke_mouse_wheel {
3097 my ($self, $ev) = @_;
3098
3099 my $delta = $self->{vertical} ? $ev->{dy} : $ev->{dx};
3100
3101 my $pagepart = $ev->{mod} & CFPlus::KMOD_SHIFT ? 1 : 0.2;
3102
3103 $self->set_value ($self->{range}[0] + $delta * $self->{range}[3] * $pagepart);
2376 3104
2377 1 3105 1
2378} 3106}
2379 3107
2380sub update { 3108sub update {
2431 glDisable GL_TEXTURE_2D; 3159 glDisable GL_TEXTURE_2D;
2432} 3160}
2433 3161
2434############################################################################# 3162#############################################################################
2435 3163
2436package CFClient::UI::ValSlider; 3164package CFPlus::UI::ValSlider;
2437 3165
2438our @ISA = CFClient::UI::HBox::; 3166our @ISA = CFPlus::UI::HBox::;
2439 3167
2440sub new { 3168sub new {
2441 my ($class, %arg) = @_; 3169 my ($class, %arg) = @_;
2442 3170
2443 my $range = delete $arg{range}; 3171 my $range = delete $arg{range};
2444 3172
2445 my $self = $class->SUPER::new ( 3173 my $self = $class->SUPER::new (
2446 slider => (new CFClient::UI::Slider expand => 1, range => $range), 3174 slider => (new CFPlus::UI::Slider expand => 1, range => $range),
2447 entry => (new CFClient::UI::Label text => "", template => delete $arg{template}), 3175 entry => (new CFPlus::UI::Label text => "", template => delete $arg{template}),
2448 to_value => sub { shift }, 3176 to_value => sub { shift },
2449 from_value => sub { shift }, 3177 from_value => sub { shift },
2450 %arg, 3178 %arg,
2451 ); 3179 );
2452 3180
2472sub set_range { shift->{slider}->set_range (@_) } 3200sub set_range { shift->{slider}->set_range (@_) }
2473sub set_value { shift->{slider}->set_value (@_) } 3201sub set_value { shift->{slider}->set_value (@_) }
2474 3202
2475############################################################################# 3203#############################################################################
2476 3204
2477package CFClient::UI::TextScroller; 3205package CFPlus::UI::TextScroller;
2478 3206
2479our @ISA = CFClient::UI::HBox::; 3207our @ISA = CFPlus::UI::HBox::;
2480 3208
2481use CFClient::OpenGL; 3209use CFPlus::OpenGL;
2482 3210
2483sub new { 3211sub new {
2484 my $class = shift; 3212 my $class = shift;
2485 3213
2486 my $self = $class->SUPER::new ( 3214 my $self = $class->SUPER::new (
2487 fontsize => 1, 3215 fontsize => 1,
2488 can_events => 0, 3216 can_events => 1,
2489 indent => 0, 3217 indent => 0,
2490 #font => default_font 3218 #font => default_font
2491 @_, 3219 @_,
2492 3220
2493 layout => (new CFClient::Layout 1), 3221 layout => (new CFPlus::Layout),
2494 par => [], 3222 par => [],
3223 max_par => 0,
2495 height => 0, 3224 height => 0,
2496 children => [ 3225 children => [
2497 (new CFClient::UI::Empty expand => 1), 3226 (new CFPlus::UI::Empty expand => 1),
2498 (new CFClient::UI::Slider vertical => 1), 3227 (new CFPlus::UI::Slider vertical => 1),
2499 ], 3228 ],
2500 ); 3229 );
2501 3230
2502 $self->{children}[1]->connect (changed => sub { $self->update }); 3231 $self->{children}[1]->connect (changed => sub { $self->update });
2503 3232
2509 3238
2510 $self->{fontsize} = $fontsize; 3239 $self->{fontsize} = $fontsize;
2511 $self->reflow; 3240 $self->reflow;
2512} 3241}
2513 3242
3243sub size_request {
3244 my ($self) = @_;
3245
3246 my ($empty, $slider) = $self->visible_children;
3247
3248 local $self->{children} = [$empty, $slider];
3249 $self->SUPER::size_request
3250}
3251
2514sub size_allocate { 3252sub invoke_size_allocate {
2515 my ($self, $w, $h) = @_; 3253 my ($self, $w, $h) = @_;
2516 3254
2517 $self->SUPER::size_allocate ($w, $h); 3255 my ($empty, $slider, @other) = @{ $self->{children} };
3256 $_->configure (@$_{qw(x y req_w req_h)}) for @other;
2518 3257
2519 $self->{layout}->set_font ($self->{font}) if $self->{font}; 3258 $self->{layout}->set_font ($self->{font}) if $self->{font};
2520 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); 3259 $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
2521 $self->{layout}->set_width ($self->{children}[0]{w}); 3260 $self->{layout}->set_width ($empty->{w});
2522 $self->{layout}->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); 3261 $self->{layout}->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
2523 3262
2524 $self->reflow; 3263 $self->reflow;
2525}
2526 3264
2527sub text_size { 3265 local $self->{children} = [$empty, $slider];
3266 $self->SUPER::invoke_size_allocate ($w, $h)
3267}
3268
3269sub invoke_mouse_wheel {
2528 my ($self, $text, $indent) = @_; 3270 my ($self, $ev) = @_;
3271
3272 return 0 unless $ev->{dy}; # only vertical movements
3273
3274 $self->{children}[1]->emit (mouse_wheel => $ev);
3275
3276 1
3277}
3278
3279sub get_layout {
3280 my ($self, $para) = @_;
2529 3281
2530 my $layout = $self->{layout}; 3282 my $layout = $self->{layout};
2531 3283
3284 $layout->set_font ($self->{font}) if $self->{font};
3285 $layout->set_foreground (@{$para->{fg}});
2532 $layout->set_height ($self->{fontsize} * $::FONTSIZE); 3286 $layout->set_height ($self->{fontsize} * $::FONTSIZE);
2533 $layout->set_width ($self->{children}[0]{w} - $indent); 3287 $layout->set_width ($self->{children}[0]{w} - $para->{indent});
2534 $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); 3288 $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
2535 $layout->set_markup ($text); 3289 $layout->set_markup ($para->{markup});
3290
3291 $layout->set_shapes (
3292 map
3293 +(0, $_->baseline_shift +$_->{padding_y} - $_->{h}, $_->{w}, $_->{h}),
3294 @{$para->{widget}}
2536 3295 );
3296
2537 $layout->size 3297 $layout
2538} 3298}
2539 3299
2540sub reflow { 3300sub reflow {
2541 my ($self) = @_; 3301 my ($self) = @_;
2542 3302
2549 3309
2550 # todo: base offset on lines or so, not on pixels 3310 # todo: base offset on lines or so, not on pixels
2551 $self->{children}[1]->set_value ($offset); 3311 $self->{children}[1]->set_value ($offset);
2552} 3312}
2553 3313
3314sub current_paragraph {
3315 my ($self) = @_;
3316
3317 $self->{top_paragraph} - 1
3318}
3319
3320sub scroll_to {
3321 my ($self, $para) = @_;
3322
3323 $para = List::Util::max 0, List::Util::min $#{$self->{par}}, $para;
3324
3325 $self->{scroll_to} = $para;
3326 $self->update;
3327}
3328
2554sub clear { 3329sub clear {
2555 my ($self) = @_; 3330 my ($self) = @_;
3331
3332 my (undef, undef, @other) = @{ $self->{children} };
3333 $self->remove ($_) for @other;
2556 3334
2557 $self->{par} = []; 3335 $self->{par} = [];
2558 $self->{height} = 0; 3336 $self->{height} = 0;
2559 $self->{children}[1]->set_range ([0, 0, 0, 1, 1]); 3337 $self->{children}[1]->set_range ([0, 0, 0, 1, 1]);
2560} 3338}
2561 3339
2562sub add_paragraph { 3340sub add_paragraph {
2563 my ($self, $color, $text, $indent) = @_; 3341 my $self = shift;
2564 3342
2565 for my $line (split /\n/, $text) { 3343 for my $para (@_) {
2566 my ($w, $h) = $self->text_size ($line); 3344 $para = {
3345 fg => [1, 1, 1, 1],
3346 indent => 0,
3347 markup => "",
3348 widget => [],
3349 ref $para ? %$para : (markup => $para),
3350 w => 1e10,
3351 wrapped => 1,
3352 };
3353
3354 $self->add (@{ $para->{widget} }) if @{ $para->{widget} };
3355 push @{$self->{par}}, $para;
3356 }
3357
3358 if (my $max = $self->{max_par}) {
3359 shift @{$self->{par}} while @{$self->{par}} > $max;
3360 }
3361
3362 $self->{need_reflow}++;
3363 $self->update;
3364}
3365
3366sub scroll_to_bottom {
3367 my ($self) = @_;
3368
3369 $self->{scroll_to} = $#{$self->{par}};
3370 $self->update;
3371}
3372
3373sub force_uptodate {
3374 my ($self) = @_;
3375
3376 if (delete $self->{need_reflow}) {
3377 my ($W, $H) = @{$self->{children}[0]}{qw(w h)};
3378
3379 my $height = 0;
3380
3381 for my $para (@{$self->{par}}) {
3382 if ($para->{w} != $W && ($para->{wrapped} || $para->{w} > $W)) {
3383 my $layout = $self->get_layout ($para);
3384 my ($w, $h) = $layout->size;
3385
3386 $para->{w} = $w + $para->{indent};
3387 $para->{h} = $h;
3388 $para->{wrapped} = $layout->has_wrapped;
3389 }
3390
3391 $para->{y} = $height;
3392 $height += $para->{h};
3393 }
3394
2567 $self->{height} += $h; 3395 $self->{height} = $height;
2568 push @{$self->{par}}, [$w + $indent, $h, $color, $indent, $line]; 3396 $self->{children}[1]->set_range ([$self->{children}[1]{range}[0], 0, $height, $H, 1]);
2569 }
2570 3397
2571 $self->{children}[1]->set_range ([$self->{height}, 0, $self->{height}, $self->{h}, 1]); 3398 delete $self->{texture};
3399 }
3400
3401 if (my $paridx = delete $self->{scroll_to}) {
3402 $self->{children}[1]->set_value ($self->{par}[$paridx]{y});
3403 }
2572} 3404}
2573 3405
2574sub update { 3406sub update {
2575 my ($self) = @_; 3407 my ($self) = @_;
2576 3408
2579 return unless $self->{h} > 0; 3411 return unless $self->{h} > 0;
2580 3412
2581 delete $self->{texture}; 3413 delete $self->{texture};
2582 3414
2583 $ROOT->on_post_alloc ($self => sub { 3415 $ROOT->on_post_alloc ($self => sub {
3416 $self->force_uptodate;
3417
2584 my ($W, $H) = @{$self->{children}[0]}{qw(w h)}; 3418 my ($W, $H) = @{$self->{children}[0]}{qw(w h)};
2585 3419
2586 if (delete $self->{need_reflow}) {
2587 my $height = 0;
2588
2589 my $layout = $self->{layout};
2590
2591 $layout->set_height ($self->{fontsize} * $::FONTSIZE);
2592
2593 for (@{$self->{par}}) {
2594 if (1 || $_->[0] >= $W) { # TODO: works,but needs reconfigure etc. support
2595 $layout->set_width ($W - $_->[3]);
2596 $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
2597 $layout->set_markup ($_->[4]);
2598 my ($w, $h) = $layout->size;
2599 $_->[0] = $w + $_->[3];
2600 $_->[1] = $h;
2601 }
2602
2603 $height += $_->[1];
2604 }
2605
2606 $self->{height} = $height;
2607
2608 $self->{children}[1]->set_range ([$height, 0, $height, $H, 1]);
2609
2610 delete $self->{texture};
2611 }
2612
2613 $self->{texture} ||= new_from_opengl CFClient::Texture $W, $H, sub { 3420 $self->{texture} ||= new_from_opengl CFPlus::Texture $W, $H, sub {
2614 glClearColor 0, 0, 0, 0; 3421 glClearColor 0, 0, 0, 0;
2615 glClear GL_COLOR_BUFFER_BIT; 3422 glClear GL_COLOR_BUFFER_BIT;
2616 3423
3424 package CFPlus::UI::Base;
3425 local ($draw_x, $draw_y, $draw_w, $draw_h) =
3426 (0, 0, $self->{w}, $self->{h});
3427
3428 my $top = int $self->{children}[1]{range}[0];
3429
3430 my $paridx = 0;
3431 my $top_paragraph;
2617 my $top = int $self->{children}[1]{range}[0]; 3432 my $top = int $self->{children}[1]{range}[0];
2618 3433
2619 my $y0 = $top; 3434 my $y0 = $top;
2620 my $y1 = $top + $H; 3435 my $y1 = $top + $H;
2621 3436
2622 my $y = 0;
2623
2624 my $layout = $self->{layout};
2625
2626 $layout->set_font ($self->{font}) if $self->{font};
2627
2628 glEnable GL_BLEND;
2629 #TODO# not correct in windows where rgba is forced off
2630 glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
2631
2632 for my $par (@{$self->{par}}) { 3437 for my $para (@{$self->{par}}) {
2633 my $h = $par->[1]; 3438 my $h = $para->{h};
3439 my $y = $para->{y};
2634 3440
2635 if ($y0 < $y + $h && $y < $y1) { 3441 if ($y0 < $y + $h && $y < $y1) {
2636 $layout->set_foreground (@{ $par->[2] }); 3442 my $layout = $self->get_layout ($para);
2637 $layout->set_width ($W - $par->[3]);
2638 $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
2639 $layout->set_markup ($par->[4]);
2640 3443
2641 my ($w, $h, $data, $format, $internalformat) = $layout->render; 3444 $layout->render ($para->{indent}, $y - $y0);
3445 $layout->draw;
2642 3446
2643 glRasterPos $par->[3], $y - $y0; 3447 if (my @w = @{ $para->{widget} }) {
2644 glDrawPixels $w, $h, $format, GL_UNSIGNED_BYTE, $data; 3448 my @s = $layout->get_shapes;
3449
3450 for (@w) {
3451 my ($dx, $dy) = splice @s, 0, 2, ();
3452
3453 $_->{x} = $dx + $para->{indent};
3454 $_->{y} = $dy + $y - $y0;
3455
3456 $_->draw;
3457 }
3458 }
2645 } 3459 }
2646 3460
2647 $y += $h; 3461 $paridx++;
3462 $top_paragraph ||= $paridx if $y >= $top;
2648 } 3463 }
2649 3464
2650 glDisable GL_BLEND; 3465 $self->{top_paragraph} = $top_paragraph;
2651 }; 3466 };
2652 }); 3467 });
3468}
3469
3470sub reconfigure {
3471 my ($self) = @_;
3472
3473 $self->SUPER::reconfigure;
3474
3475 $_->{w} = 1e10 for @{ $self->{par} };
3476 $self->reflow;
2653} 3477}
2654 3478
2655sub _draw { 3479sub _draw {
2656 my ($self) = @_; 3480 my ($self) = @_;
2657 3481
2660 glColor 0, 0, 0, 1; 3484 glColor 0, 0, 0, 1;
2661 $self->{texture}->draw_quad_alpha_premultiplied (0, 0, $self->{children}[0]{w}, $self->{children}[0]{h}); 3485 $self->{texture}->draw_quad_alpha_premultiplied (0, 0, $self->{children}[0]{w}, $self->{children}[0]{h});
2662 glDisable GL_TEXTURE_2D; 3486 glDisable GL_TEXTURE_2D;
2663 3487
2664 $self->{children}[1]->draw; 3488 $self->{children}[1]->draw;
2665
2666} 3489}
2667 3490
2668############################################################################# 3491#############################################################################
2669 3492
2670package CFClient::UI::Animator; 3493package CFPlus::UI::Animator;
2671 3494
2672use CFClient::OpenGL; 3495use CFPlus::OpenGL;
2673 3496
2674our @ISA = CFClient::UI::Bin::; 3497our @ISA = CFPlus::UI::Bin::;
2675 3498
2676sub moveto { 3499sub moveto {
2677 my ($self, $x, $y) = @_; 3500 my ($self, $x, $y) = @_;
2678 3501
2679 $self->{moveto} = [$self->{x}, $self->{y}, $x, $y]; 3502 $self->{moveto} = [$self->{x}, $self->{y}, $x, $y];
2707 glPopMatrix; 3530 glPopMatrix;
2708} 3531}
2709 3532
2710############################################################################# 3533#############################################################################
2711 3534
2712package CFClient::UI::Flopper; 3535package CFPlus::UI::Flopper;
2713 3536
2714our @ISA = CFClient::UI::Button::; 3537our @ISA = CFPlus::UI::Button::;
2715 3538
2716sub new { 3539sub new {
2717 my $class = shift; 3540 my $class = shift;
2718 3541
2719 my $self = $class->SUPER::new ( 3542 my $self = $class->SUPER::new (
2731 $self->{other}->toggle_visibility; 3554 $self->{other}->toggle_visibility;
2732} 3555}
2733 3556
2734############################################################################# 3557#############################################################################
2735 3558
2736package CFClient::UI::Tooltip; 3559package CFPlus::UI::Tooltip;
2737 3560
2738our @ISA = CFClient::UI::Bin::; 3561our @ISA = CFPlus::UI::Bin::;
2739 3562
2740use CFClient::OpenGL; 3563use CFPlus::OpenGL;
2741 3564
2742sub new { 3565sub new {
2743 my $class = shift; 3566 my $class = shift;
2744 3567
2745 $class->SUPER::new ( 3568 $class->SUPER::new (
2749} 3572}
2750 3573
2751sub set_tooltip_from { 3574sub set_tooltip_from {
2752 my ($self, $widget) = @_; 3575 my ($self, $widget) = @_;
2753 3576
2754 my $tooltip = $widget->{tooltip}; 3577 my $tip = $widget->{tooltip};
3578 $tip = $tip->($widget) if "CODE" eq ref $tip;
3579
3580 $tip = CFPlus::Pod::section_label tooltip => $1
3581 if $tip =~ /^#(.*)$/;
2755 3582
2756 if ($ENV{CFPLUS_DEBUG} & 2) { 3583 if ($ENV{CFPLUS_DEBUG} & 2) {
2757 $tooltip .= "\n\n" . (ref $widget) . "\n" 3584 $tip .= "\n\n" . (ref $widget) . "\n"
2758 . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n" 3585 . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n"
2759 . "req $widget->{req_w} $widget->{req_h}\n" 3586 . "req $widget->{req_w} $widget->{req_h}\n"
2760 . "visible $widget->{visible}"; 3587 . "visible $widget->{visible}";
2761 } 3588 }
2762 3589
2763 $tooltip =~ s/^\n+//; 3590 $tip =~ s/^\n+//;
2764 $tooltip =~ s/\n+$//; 3591 $tip =~ s/\n+$//;
2765 3592
2766 $self->add (new CFClient::UI::Label 3593 $self->add (new CFPlus::UI::Label
2767 markup => $tooltip, 3594 markup => $tip,
2768 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH, 3595 max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH,
2769 fontsize => 0.8, 3596 fontsize => 0.8,
2770 fg => [0, 0, 0, 1], 3597 style => 1, # FLAG_INVERSE
2771 ellipsise => 0, 3598 ellipsise => 0,
2772 font => ($widget->{tooltip_font} || $::FONT_PROP), 3599 font => ($widget->{tooltip_font} || $::FONT_PROP),
2773 ); 3600 );
2774} 3601}
2775 3602
2779 my ($w, $h) = @{$self->child}{qw(req_w req_h)}; 3606 my ($w, $h) = @{$self->child}{qw(req_w req_h)};
2780 3607
2781 ($w + 4, $h + 4) 3608 ($w + 4, $h + 4)
2782} 3609}
2783 3610
2784sub size_allocate { 3611sub invoke_size_allocate {
2785 my ($self, $w, $h) = @_; 3612 my ($self, $w, $h) = @_;
2786 3613
2787 $self->SUPER::size_allocate ($w - 4, $h - 4); 3614 $self->SUPER::invoke_size_allocate ($w - 4, $h - 4)
2788} 3615}
2789 3616
2790sub visibility_change { 3617sub invoke_visibility_change {
2791 my ($self, $visible) = @_; 3618 my ($self, $visible) = @_;
2792 3619
2793 return unless $visible; 3620 return unless $visible;
2794 3621
2795 $self->{root}->on_post_alloc ("move_$self" => sub { 3622 $self->{root}->on_post_alloc ("move_$self" => sub {
2796 my $widget = $self->{owner} 3623 my $widget = $self->{owner}
2797 or return; 3624 or return;
2798 3625
3626 if ($widget->{visible}) {
2799 my ($x, $y) = $widget->coord2global ($widget->{w}, 0); 3627 my ($x, $y) = $widget->coord2global ($widget->{w}, 0);
2800 3628
2801 ($x, $y) = $widget->coord2global (-$self->{w}, 0) 3629 ($x, $y) = $widget->coord2global (-$self->{w}, 0)
2802 if $x + $self->{w} > $self->{root}{w}; 3630 if $x + $self->{w} > $self->{root}{w};
2803 3631
2804 $self->move_abs ($x, $y); 3632 $self->move_abs ($x, $y);
3633 } else {
3634 $self->hide;
3635 }
2805 }); 3636 });
2806} 3637}
2807 3638
2808sub _draw { 3639sub _draw {
2809 my ($self) = @_; 3640 my ($self) = @_;
2810 3641
2811 glTranslate 0.375, 0.375;
2812
2813 my ($w, $h) = @$self{qw(w h)}; 3642 my ($w, $h) = @$self{qw(w h)};
2814 3643
2815 glColor 1, 0.8, 0.4; 3644 glColor 1, 0.8, 0.4;
2816 glBegin GL_QUADS; 3645 glRect 0, 0, $w, $h;
2817 glVertex 0 , 0;
2818 glVertex 0 , $h;
2819 glVertex $w, $h;
2820 glVertex $w, 0;
2821 glEnd;
2822 3646
2823 glColor 0, 0, 0; 3647 glColor 0, 0, 0;
2824 glBegin GL_LINE_LOOP; 3648 glRect_lineloop .5, .5, $w + .5, $h + .5;
2825 glVertex 0 , 0;
2826 glVertex 0 , $h;
2827 glVertex $w, $h;
2828 glVertex $w, 0;
2829 glEnd;
2830 3649
2831 glTranslate 2 - 0.375, 2 - 0.375; 3650 glTranslate 2, 2;
2832 3651
2833 $self->SUPER::_draw; 3652 $self->SUPER::_draw;
2834} 3653}
2835 3654
2836############################################################################# 3655#############################################################################
2837 3656
2838package CFClient::UI::Face; 3657package CFPlus::UI::Face;
2839 3658
2840our @ISA = CFClient::UI::Base::; 3659our @ISA = CFPlus::UI::DrawBG::;
2841 3660
2842use CFClient::OpenGL; 3661use CFPlus::OpenGL;
2843 3662
2844sub new { 3663sub new {
2845 my $class = shift; 3664 my $class = shift;
2846 3665
2847 my $self = $class->SUPER::new ( 3666 my $self = $class->SUPER::new (
3667 size_w => 32,
3668 size_h => 8,
2848 aspect => 1, 3669 aspect => 1,
2849 can_events => 0, 3670 can_events => 0,
2850 @_, 3671 @_,
2851 ); 3672 );
2852 3673
2853 if ($self->{anim} && $self->{animspeed}) { 3674 if ($self->{anim} && $self->{animspeed}) {
2854 Scalar::Util::weaken (my $widget = $self); 3675 CFPlus::weaken (my $widget = $self);
2855 3676
3677 $widget->{animspeed} = List::Util::max 0.05, $widget->{animspeed};
3678 $widget->{anim_start} = $self->{animspeed} * int Event::time / $self->{animspeed};
2856 $self->{timer} = Event->timer ( 3679 $self->{timer} = Event->timer (
2857 at => $self->{animspeed} * int $::NOW / $self->{animspeed},
2858 hard => 1, 3680 parked => 1,
2859 interval => $self->{animspeed},
2860 cb => sub { 3681 cb => sub {
3682 return unless $::CONN;
3683
3684 my $w = $widget
3685 or return;
3686
2861 ++$widget->{frame}; 3687 ++$w->{frame};
3688 $w->update_face;
3689
3690 # somehow, $widget can go away
2862 $widget->update; 3691 $w->update;
3692 $w->update_timer;
2863 }, 3693 },
2864 ); 3694 );
3695
3696 $self->update_face;
3697 $self->update_timer;
2865 } 3698 }
2866 3699
2867 $self 3700 $self
2868} 3701}
2869 3702
3703sub update_timer {
3704 my ($self) = @_;
3705
3706 return unless $self->{timer};
3707
3708 if ($self->{visible}) {
3709 $self->{timer}->at (
3710 $self->{anim_start}
3711 + $self->{animspeed}
3712 * int 1.5 + (Event::time - $self->{anim_start}) / $self->{animspeed}
3713 );
3714 $self->{timer}->start;
3715 } else {
3716 $self->{timer}->stop;
3717 }
3718}
3719
3720sub update_face {
3721 my ($self) = @_;
3722
3723 if ($::CONN) {
3724 if (my $anim = $::CONN->{anim}[$self->{anim}]) {
3725 if ($anim && @$anim) {
3726 $self->{face} = $anim->[ $self->{frame} % @$anim ];
3727 delete $self->{face_change_cb};
3728
3729 if (my $tex = $self->{tex} = $::CONN->{texture}[ $::CONN->{face}[$self->{face}]{id} ]) {
3730 unless ($tex->{name} || $tex->{loading}) {
3731 $tex->upload (sub { $self->reconfigure });
3732 }
3733 }
3734 }
3735 }
3736 }
3737}
3738
2870sub size_request { 3739sub size_request {
2871 (32, 8) 3740 my ($self) = @_;
3741
3742 if ($::CONN) {
3743 if (my $faceid = $::CONN->{face}[$self->{face}]{id}) {
3744 if (my $tex = $self->{tex} = $::CONN->{texture}[$faceid]) {
3745 if ($tex->{name}) {
3746 return ($self->{size_w} || $tex->{w}, $self->{size_h} || $tex->{h});
3747 } elsif (!$tex->{loading}) {
3748 $tex->upload (sub { $self->reconfigure });
3749 }
3750 }
3751
3752 $self->{face_change_cb} ||= $::CONN->on_face_change ($self->{face}, sub { $self->reconfigure });
3753 }
3754 }
3755
3756 ($self->{size_w} || 8, $self->{size_h} || 8)
2872} 3757}
2873 3758
2874sub update { 3759sub update {
2875 my ($self) = @_; 3760 my ($self) = @_;
2876 3761
2877 return unless $self->{visible}; 3762 return unless $self->{visible};
2878 3763
2879 $self->SUPER::update; 3764 $self->SUPER::update;
2880} 3765}
2881 3766
3767sub invoke_visibility_change {
3768 my ($self) = @_;
3769
3770 $self->update_timer;
3771
3772 0
3773}
3774
2882sub _draw { 3775sub _draw {
2883 my ($self) = @_; 3776 my ($self) = @_;
2884 3777
2885 return unless $::CONN; 3778 $self->SUPER::_draw;
2886 3779
2887 my $face; 3780 if (my $tex = $self->{tex}) {
2888
2889 if ($self->{frame}) {
2890 my $anim = $::CONN->{anim}[$self->{anim}];
2891
2892 $face = $anim->[ $self->{frame} % @$anim ]
2893 if $anim && @$anim;
2894 }
2895
2896 my $tex = $::CONN->{texture}[$::CONN->{faceid}[$face || $self->{face}]];
2897
2898 if ($tex) {
2899 glEnable GL_TEXTURE_2D; 3781 glEnable GL_TEXTURE_2D;
2900 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; 3782 glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
2901 glColor 0, 0, 0, 1; 3783 glColor 0, 0, 0, 1;
2902 $tex->draw_quad_alpha (0, 0, $self->{w}, $self->{h}); 3784 $tex->draw_quad_alpha (0, 0, $self->{w}, $self->{h});
2903 glDisable GL_TEXTURE_2D; 3785 glDisable GL_TEXTURE_2D;
2905} 3787}
2906 3788
2907sub destroy { 3789sub destroy {
2908 my ($self) = @_; 3790 my ($self) = @_;
2909 3791
2910 $self->{timer}->cancel 3792 (delete $self->{timer})->cancel
2911 if $self->{timer}; 3793 if $self->{timer};
2912 3794
2913 $self->SUPER::destroy; 3795 $self->SUPER::destroy;
2914} 3796}
2915 3797
2916############################################################################# 3798#############################################################################
2917 3799
2918package CFClient::UI::Buttonbar; 3800package CFPlus::UI::Buttonbar;
2919 3801
2920our @ISA = CFClient::UI::HBox::; 3802our @ISA = CFPlus::UI::HBox::;
2921 3803
2922# TODO: should actualyl wrap buttons and other goodies. 3804# TODO: should actually wrap buttons and other goodies.
2923 3805
2924############################################################################# 3806#############################################################################
2925 3807
2926package CFClient::UI::Menu; 3808package CFPlus::UI::Menu;
2927 3809
2928our @ISA = CFClient::UI::FancyFrame::; 3810our @ISA = CFPlus::UI::Toplevel::;
2929 3811
2930use CFClient::OpenGL; 3812use CFPlus::OpenGL;
2931 3813
2932sub new { 3814sub new {
2933 my $class = shift; 3815 my $class = shift;
2934 3816
2935 my $self = $class->SUPER::new ( 3817 my $self = $class->SUPER::new (
2936 items => [], 3818 items => [],
2937 z => 100, 3819 z => 100,
2938 @_, 3820 @_,
2939 ); 3821 );
2940 3822
2941 $self->add ($self->{vbox} = new CFClient::UI::VBox); 3823 $self->add ($self->{vbox} = new CFPlus::UI::VBox);
2942 3824
2943 for my $item (@{ $self->{items} }) { 3825 for my $item (@{ $self->{items} }) {
2944 my ($widget, $cb, $tooltip) = @$item; 3826 my ($widget, $cb, $tooltip) = @$item;
2945 3827
2946 # handle various types of items, only text for now 3828 # handle various types of items, only text for now
2947 if (!ref $widget) { 3829 if (!ref $widget) {
3830 if ($widget =~ /\t/) {
3831 my ($left, $right) = split /\t/, $widget, 2;
3832
2948 $widget = new CFClient::UI::Label 3833 $widget = new CFPlus::UI::HBox
2949 can_hover => 1, 3834 can_hover => 1,
2950 can_events => 1, 3835 can_events => 1,
3836 tooltip => $tooltip,
3837 children => [
3838 (new CFPlus::UI::Label markup => $left, expand => 1),
3839 (new CFPlus::UI::Label markup => $right, align => +1),
3840 ],
3841 ;
3842
3843 } else {
3844 $widget = new CFPlus::UI::Label
3845 can_hover => 1,
3846 can_events => 1,
2951 markup => $widget, 3847 markup => $widget,
2952 tooltip => $tooltip 3848 tooltip => $tooltip;
3849 }
2953 } 3850 }
2954 3851
2955 $self->{item}{$widget} = $item; 3852 $self->{item}{$widget} = $item;
2956 3853
2957 $self->{vbox}->add ($widget); 3854 $self->{vbox}->add ($widget);
2962 3859
2963# popup given the event (must be a mouse button down event currently) 3860# popup given the event (must be a mouse button down event currently)
2964sub popup { 3861sub popup {
2965 my ($self, $ev) = @_; 3862 my ($self, $ev) = @_;
2966 3863
2967 $self->_emit ("popdown"); 3864 $self->emit ("popdown");
2968 3865
2969 # maybe save $GRAB? must be careful about events... 3866 # maybe save $GRAB? must be careful about events...
2970 $GRAB = $self; 3867 $GRAB = $self;
2971 $self->{button} = $ev->{button}; 3868 $self->{button} = $ev->{button};
2972 3869
2973 $self->show; 3870 $self->show;
2974 $self->move_abs ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5); 3871 $self->move_abs ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5);
2975} 3872}
2976 3873
2977sub mouse_motion { 3874sub invoke_mouse_motion {
2978 my ($self, $ev, $x, $y) = @_; 3875 my ($self, $ev, $x, $y) = @_;
2979 3876
2980 # TODO: should use vbox->find_widget or so 3877 # TODO: should use vbox->find_widget or so
2981 $HOVER = $ROOT->find_widget ($ev->{x}, $ev->{y}); 3878 $HOVER = $ROOT->find_widget ($ev->{x}, $ev->{y});
2982 $self->{hover} = $self->{item}{$HOVER}; 3879 $self->{hover} = $self->{item}{$HOVER};
2983 3880
2984 0 3881 0
2985} 3882}
2986 3883
2987sub button_up { 3884sub invoke_button_up {
2988 my ($self, $ev, $x, $y) = @_; 3885 my ($self, $ev, $x, $y) = @_;
2989 3886
2990 if ($ev->{button} == $self->{button}) { 3887 if ($ev->{button} == $self->{button}) {
2991 undef $GRAB; 3888 undef $GRAB;
2992 $self->hide; 3889 $self->hide;
2993 3890
2994 $self->_emit ("popdown"); 3891 $self->emit ("popdown");
2995 $self->{hover}[1]->() if $self->{hover}; 3892 $self->{hover}[1]->() if $self->{hover};
2996 } else { 3893 } else {
2997 return 0 3894 return 0
2998 } 3895 }
2999 3896
3000 1 3897 1
3001} 3898}
3002 3899
3003############################################################################# 3900#############################################################################
3004 3901
3005package CFClient::UI::Multiplexer; 3902package CFPlus::UI::Multiplexer;
3006 3903
3007our @ISA = CFClient::UI::Container::; 3904our @ISA = CFPlus::UI::Container::;
3008 3905
3009sub new { 3906sub new {
3010 my $class = shift; 3907 my $class = shift;
3011 3908
3012 my $self = $class->SUPER::new ( 3909 my $self = $class->SUPER::new (
3026 3923
3027 $self->{current} = $self->{children}[0] 3924 $self->{current} = $self->{children}[0]
3028 if @{ $self->{children} }; 3925 if @{ $self->{children} };
3029} 3926}
3030 3927
3928sub get_current_page {
3929 my ($self) = @_;
3930
3931 $self->{current}
3932}
3933
3031sub set_current_page { 3934sub set_current_page {
3032 my ($self, $page_or_widget) = @_; 3935 my ($self, $page_or_widget) = @_;
3033 3936
3034 my $widget = ref $page_or_widget 3937 my $widget = ref $page_or_widget
3035 ? $page_or_widget 3938 ? $page_or_widget
3036 : $self->{children}[$page_or_widget]; 3939 : $self->{children}[$page_or_widget];
3037 3940
3038 $self->{current} = $widget; 3941 $self->{current} = $widget;
3039 $self->{current}->configure (0, 0, $self->{w}, $self->{h}); 3942 $self->{current}->configure (0, 0, $self->{w}, $self->{h});
3040 3943
3041 $self->_emit (page_changed => $self->{current}); 3944 $self->emit (page_changed => $self->{current});
3042 3945
3043 $self->realloc; 3946 $self->realloc;
3044} 3947}
3045 3948
3046sub visible_children { 3949sub visible_children {
3051 my ($self) = @_; 3954 my ($self) = @_;
3052 3955
3053 $self->{current}->size_request 3956 $self->{current}->size_request
3054} 3957}
3055 3958
3056sub size_allocate { 3959sub invoke_size_allocate {
3057 my ($self, $w, $h) = @_; 3960 my ($self, $w, $h) = @_;
3058 3961
3059 $self->{current}->configure (0, 0, $w, $h); 3962 $self->{current}->configure (0, 0, $w, $h);
3963
3964 1
3060} 3965}
3061 3966
3062sub _draw { 3967sub _draw {
3063 my ($self) = @_; 3968 my ($self) = @_;
3064 3969
3065 $self->{current}->draw; 3970 $self->{current}->draw;
3066} 3971}
3067 3972
3068############################################################################# 3973#############################################################################
3069 3974
3070package CFClient::UI::Notebook; 3975package CFPlus::UI::Notebook;
3071 3976
3977use CFPlus::OpenGL;
3978
3072our @ISA = CFClient::UI::VBox::; 3979our @ISA = CFPlus::UI::VBox::;
3073 3980
3074sub new { 3981sub new {
3075 my $class = shift; 3982 my $class = shift;
3076 3983
3077 my $self = $class->SUPER::new ( 3984 my $self = $class->SUPER::new (
3078 buttonbar => (new CFClient::UI::Buttonbar), 3985 buttonbar => (new CFPlus::UI::Buttonbar),
3079 multiplexer => (new CFClient::UI::Multiplexer expand => 1), 3986 multiplexer => (new CFPlus::UI::Multiplexer expand => 1),
3987 active_outline => [.7, .7, 0.2],
3080 # filter => # will be put between multiplexer and $self 3988 # filter => # will be put between multiplexer and $self
3081 @_, 3989 @_,
3082 ); 3990 );
3083 3991
3084 $self->{filter}->add ($self->{multiplexer}) if $self->{filter}; 3992 $self->{filter}->add ($self->{multiplexer}) if $self->{filter};
3085 $self->SUPER::add ($self->{buttonbar}, $self->{filter} || $self->{multiplexer}); 3993 $self->SUPER::add ($self->{buttonbar}, $self->{filter} || $self->{multiplexer});
3086 3994
3995 {
3996 Scalar::Util::weaken (my $wself = $self);
3997
3998 $self->{multiplexer}->connect (c_add => sub {
3999 my ($mplex, $widgets) = @_;
4000
4001 for my $child (@$widgets) {
4002 Scalar::Util::weaken $child;
4003 $child->{c_tab_} ||= do {
4004 my $tab =
4005 (UNIVERSAL::isa $child->{c_tab}, "CFPlus::UI::Base")
4006 ? $child->{c_tab}
4007 : new CFPlus::UI::Button markup => $child->{c_tab}[0], tooltip => $child->{c_tab}[1];
4008
4009 $tab->connect (activate => sub {
4010 $wself->set_current_page ($child);
4011 });
4012
4013 $tab
4014 };
4015
4016 $self->{buttonbar}->add ($child->{c_tab_});
4017 }
4018 });
4019
4020 $self->{multiplexer}->connect (c_remove => sub {
4021 my ($mplex, $widgets) = @_;
4022
4023 for my $child (@$widgets) {
4024 $wself->{buttonbar}->remove ($child->{c_tab_});
4025 }
4026 });
4027 }
4028
3087 $self 4029 $self
3088} 4030}
3089 4031
3090sub add { 4032sub add {
4033 my ($self, @widgets) = @_;
4034
4035 $self->{multiplexer}->add (@widgets)
4036}
4037
4038sub remove {
4039 my ($self, @widgets) = @_;
4040
4041 $self->{multiplexer}->remove (@widgets)
4042}
4043
4044sub pages {
4045 my ($self) = @_;
4046 $self->{multiplexer}->children
4047}
4048
4049sub add_tab {
3091 my ($self, $title, $widget, $tooltip) = @_; 4050 my ($self, $title, $widget, $tooltip) = @_;
3092 4051
3093 Scalar::Util::weaken $self; 4052 $title = [$title, $tooltip] unless ref $title;
4053 $widget->{c_tab} = $title;
3094 4054
3095 $self->{buttonbar}->add (new CFClient::UI::Button
3096 markup => $title,
3097 tooltip => $tooltip,
3098 on_activate => sub { $self->set_current_page ($widget) },
3099 );
3100
3101 $self->{multiplexer}->add ($widget); 4055 $self->add ($widget);
4056}
4057
4058sub get_current_page {
4059 my ($self) = @_;
4060
4061 $self->{multiplexer}->get_current_page
3102} 4062}
3103 4063
3104sub set_current_page { 4064sub set_current_page {
3105 my ($self, $page) = @_; 4065 my ($self, $page) = @_;
3106 4066
3107 $self->{multiplexer}->set_current_page ($page); 4067 $self->{multiplexer}->set_current_page ($page);
3108 $self->_emit (page_changed => $self->{multiplexer}{current}); 4068 $self->emit (page_changed => $self->{multiplexer}{current});
4069}
4070
4071sub _draw {
4072 my ($self) = @_;
4073
4074 $self->SUPER::_draw ();
4075
4076 if (my $cur = $self->{multiplexer}{current}) {
4077 if ($cur = $cur->{c_tab_}) {
4078 glTranslate $self->{buttonbar}{x} + $cur->{x},
4079 $self->{buttonbar}{y} + $cur->{y};
4080 glLineWidth 3;
4081 #glEnable GL_BLEND;
4082 #glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
4083 glColor @{$self->{active_outline}};
4084 glRect_lineloop 1.5, 1.5, $cur->{w} - 1.5, $cur->{h} - 1.5;
4085 glLineWidth 1;
4086 #glDisable GL_BLEND;
4087 }
4088 }
3109} 4089}
3110 4090
3111############################################################################# 4091#############################################################################
3112 4092
3113package CFClient::UI::Combobox; 4093package CFPlus::UI::Selector;
3114 4094
3115use utf8; 4095use utf8;
3116 4096
3117our @ISA = CFClient::UI::Button::; 4097our @ISA = CFPlus::UI::Button::;
3118 4098
3119sub new { 4099sub new {
3120 my $class = shift; 4100 my $class = shift;
3121 4101
3122 my $self = $class->SUPER::new ( 4102 my $self = $class->SUPER::new (
3128 $self->_set_value ($self->{value}); 4108 $self->_set_value ($self->{value});
3129 4109
3130 $self 4110 $self
3131} 4111}
3132 4112
3133sub button_down { 4113sub invoke_button_down {
3134 my ($self, $ev) = @_; 4114 my ($self, $ev) = @_;
3135 4115
3136 my @menu_items; 4116 my @menu_items;
3137 4117
3138 for (@{ $self->{options} }) { 4118 for (@{ $self->{options} }) {
3139 my ($value, $title, $tooltip) = @$_; 4119 my ($value, $title, $tooltip) = @$_;
3140 4120
3141 push @menu_items, [$tooltip || $title, sub { $self->set_value ($value) }]; 4121 push @menu_items, [$tooltip || $title, sub { $self->set_value ($value) }];
3142 } 4122 }
3143 4123
3144 CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev); 4124 CFPlus::UI::Menu->new (items => \@menu_items)->popup ($ev);
3145} 4125}
3146 4126
3147sub _set_value { 4127sub _set_value {
3148 my ($self, $value) = @_; 4128 my ($self, $value) = @_;
3149 4129
3150 my ($item) = grep $_->[0] eq $value, @{ $self->{options} } 4130 my ($item) = grep $_->[0] eq $value, @{ $self->{options} };
4131 $item ||= $self->{options}[0]
3151 or return; 4132 or return;
3152 4133
3153 $self->{value} = $item->[0]; 4134 $self->{value} = $item->[0];
3154 $self->set_markup ("$item->[1] ⇓"); 4135 $self->set_markup ("$item->[1] ⇓");
3155 $self->set_tooltip ($item->[2]); 4136# $self->set_tooltip ($item->[2]);
3156} 4137}
3157 4138
3158sub set_value { 4139sub set_value {
3159 my ($self, $value) = @_; 4140 my ($self, $value) = @_;
3160 4141
3161 return unless $self->{value} ne $value; 4142 return unless $self->{value} ne $value;
3162 4143
3163 $self->_set_value ($value); 4144 $self->_set_value ($value);
3164 $self->_emit (changed => $value); 4145 $self->emit (changed => $value);
4146}
4147
4148sub set_options {
4149 my ($self, $options) = @_;
4150
4151 $self->{options} = $options;
4152 $self->_set_value ($self->{value});
3165} 4153}
3166 4154
3167############################################################################# 4155#############################################################################
3168 4156
3169package CFClient::UI::Statusbox; 4157package CFPlus::UI::Statusbox;
3170 4158
3171our @ISA = CFClient::UI::VBox::; 4159our @ISA = CFPlus::UI::VBox::;
3172 4160
3173sub new { 4161sub new {
3174 my $class = shift; 4162 my $class = shift;
3175 4163
3176 my $self = $class->SUPER::new ( 4164 my $self = $class->SUPER::new (
3177 fontsize => 0.8, 4165 fontsize => 0.8,
3178 @_, 4166 @_,
3179 ); 4167 );
3180 4168
3181 Scalar::Util::weaken (my $this = $self); 4169 CFPlus::weaken (my $this = $self);
3182 4170
3183 $self->{timer} = Event->timer (after => 1, interval => 1, cb => sub { $this->reorder }); 4171 $self->{timer} = Event->timer (after => 1, interval => 1, cb => sub { $this->reorder });
3184 4172
3185 $self 4173 $self
3186} 4174}
3188sub reorder { 4176sub reorder {
3189 my ($self) = @_; 4177 my ($self) = @_;
3190 my $NOW = Time::HiRes::time; 4178 my $NOW = Time::HiRes::time;
3191 4179
3192 # freeze display when hovering over any label 4180 # freeze display when hovering over any label
3193 return if $CFClient::UI::TOOLTIP->{owner} 4181 return if $CFPlus::UI::TOOLTIP->{owner}
3194 && grep $CFClient::UI::TOOLTIP->{owner} == $_->{label}, 4182 && grep $CFPlus::UI::TOOLTIP->{owner} == $_->{label},
3195 values %{ $self->{item} }; 4183 values %{ $self->{item} };
3196 4184
3197 while (my ($k, $v) = each %{ $self->{item} }) { 4185 while (my ($k, $v) = each %{ $self->{item} }) {
3198 delete $self->{item}{$k} if $v->{timeout} < $NOW; 4186 delete $self->{item}{$k} if $v->{timeout} < $NOW;
3199 } 4187 }
3220 for ($short) { 4208 for ($short) {
3221 s/^\s+//; 4209 s/^\s+//;
3222 s/\s+/ /g; 4210 s/\s+/ /g;
3223 } 4211 }
3224 4212
3225 new CFClient::UI::Label 4213 new CFPlus::UI::Label
3226 markup => $short, 4214 markup => $short,
3227 tooltip => $item->{tooltip}, 4215 tooltip => $item->{tooltip},
3228 tooltip_font => $::FONT_PROP, 4216 tooltip_font => $::FONT_PROP,
3229 tooltip_width => 0.67, 4217 tooltip_width => 0.67,
3230 fontsize => $item->{fontsize} || $self->{fontsize}, 4218 fontsize => $item->{fontsize} || $self->{fontsize},
3284 count => 1, 4272 count => 1,
3285 %arg, 4273 %arg,
3286 }; 4274 };
3287 } 4275 }
3288 4276
4277 $ROOT->on_refresh (reorder => sub {
3289 $self->reorder; 4278 $self->reorder;
4279 });
3290} 4280}
3291 4281
3292sub reconfigure { 4282sub reconfigure {
3293 my ($self) = @_; 4283 my ($self) = @_;
3294 4284
3307 $self->SUPER::destroy; 4297 $self->SUPER::destroy;
3308} 4298}
3309 4299
3310############################################################################# 4300#############################################################################
3311 4301
3312package CFClient::UI::Inventory;
3313
3314our @ISA = CFClient::UI::ScrolledWindow::;
3315
3316sub new {
3317 my $class = shift;
3318
3319 my $self = $class->SUPER::new (
3320 child => (new CFClient::UI::Table col_expand => [0, 1, 0]),
3321 @_,
3322 );
3323
3324 $self
3325}
3326
3327sub set_items {
3328 my ($self, $items) = @_;
3329
3330 $self->{child}->clear;
3331 return unless $items;
3332
3333 my @items = sort {
3334 ($a->{type} <=> $b->{type})
3335 or ($a->{name} cmp $b->{name})
3336 } @$items;
3337
3338 $self->{real_items} = \@items;
3339
3340 my $row = 0;
3341 for my $item (@items) {
3342 CFClient::Item::update_widgets $item;
3343
3344 $self->{child}->add (0, $row, $item->{face_widget});
3345 $self->{child}->add (1, $row, $item->{desc_widget});
3346 $self->{child}->add (2, $row, $item->{weight_widget});
3347
3348 $row++;
3349 }
3350}
3351
3352#############################################################################
3353
3354package CFClient::UI::BindEditor;
3355
3356our @ISA = CFClient::UI::FancyFrame::;
3357
3358sub new {
3359 my $class = shift;
3360
3361 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3362
3363 $self->add (my $vb = new CFClient::UI::VBox);
3364
3365
3366 $vb->add ($self->{rec_btn} = new CFClient::UI::Button
3367 text => "start recording",
3368 tooltip => "Start/Stops recording of actions."
3369 ."All subsequent actions after the recording started will be captured."
3370 ."The actions are displayed after the record was stopped."
3371 ."To bind the action you have to click on the 'Bind' button",
3372 on_activate => sub {
3373 unless ($self->{recording}) {
3374 $self->start;
3375 } else {
3376 $self->stop;
3377 }
3378 });
3379
3380 $vb->add (new CFClient::UI::Label text => "Actions:");
3381 $vb->add ($self->{cmdbox} = new CFClient::UI::VBox);
3382
3383 $vb->add (new CFClient::UI::Label text => "Bound to: ");
3384 $vb->add (my $hb = new CFClient::UI::HBox);
3385 $hb->add ($self->{keylbl} = new CFClient::UI::Label expand => 1);
3386 $hb->add (new CFClient::UI::Button
3387 text => "bind",
3388 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
3389 on_activate => sub {
3390 $self->ask_for_bind;
3391 });
3392
3393 $vb->add (my $hb = new CFClient::UI::HBox);
3394 $hb->add (new CFClient::UI::Button
3395 text => "ok",
3396 expand => 1,
3397 tooltip => "This closes the binding editor and saves the binding",
3398 on_activate => sub {
3399 $self->hide;
3400 $self->commit;
3401 });
3402
3403 $hb->add (new CFClient::UI::Button
3404 text => "cancel",
3405 expand => 1,
3406 tooltip => "This closes the binding editor without saving",
3407 on_activate => sub {
3408 $self->hide;
3409 $self->{binding_cancel}->()
3410 if $self->{binding_cancel};
3411 });
3412
3413 $self->update_binding_widgets;
3414
3415 $self
3416}
3417
3418sub commit {
3419 my ($self) = @_;
3420 my ($mod, $sym, $cmds) = $self->get_binding;
3421 if ($sym != 0 && @$cmds > 0) {
3422 $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym)
3423 ."'. Don't forget 'Save Config'!");
3424 $self->{binding_change}->($mod, $sym, $cmds)
3425 if $self->{binding_change};
3426 } else {
3427 $::STATUSBOX->add ("No action bound, no key or action specified!");
3428 $self->{binding_cancel}->()
3429 if $self->{binding_cancel};
3430 }
3431}
3432
3433sub start {
3434 my ($self) = @_;
3435
3436 $self->{rec_btn}->set_text ("stop recording");
3437 $self->{recording} = 1;
3438 $self->clear_command_list;
3439 $::CONN->start_record if $::CONN;
3440}
3441
3442sub stop {
3443 my ($self) = @_;
3444
3445 $self->{rec_btn}->set_text ("start recording");
3446 $self->{recording} = 0;
3447
3448 my $rec;
3449 $rec = $::CONN->stop_record if $::CONN;
3450 return unless ref $rec eq 'ARRAY';
3451 $self->set_command_list ($rec);
3452}
3453
3454
3455sub ask_for_bind_and_commit {
3456 my ($self) = @_;
3457 $self->ask_for_bind (1);
3458}
3459
3460sub ask_for_bind {
3461 my ($self, $commit) = @_;
3462
3463 CFClient::Binder::open_binding_dialog (sub {
3464 my ($mod, $sym) = @_;
3465 $self->{binding} = [$mod, $sym]; # XXX: how to stop that memleak?
3466 $self->update_binding_widgets;
3467 $self->commit if $commit;
3468 });
3469}
3470
3471# $mod and $sym are the modifiers and key symbol
3472# $cmds is a array ref of strings (the commands)
3473# $cb is the callback that is executed on OK
3474# $ccb is the callback that is executed on CANCEL and
3475# when the binding was unsuccessful on OK
3476sub set_binding {
3477 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
3478
3479 $self->clear_command_list;
3480 $self->{recording} = 0;
3481 $self->{rec_btn}->set_text ("start recording");
3482
3483 $self->{binding} = [$mod, $sym];
3484 $self->{commands} = $cmds;
3485
3486 $self->{binding_change} = $cb;
3487 $self->{binding_cancel} = $ccb;
3488
3489 $self->update_binding_widgets;
3490}
3491
3492# this is a shortcut method that asks for a binding
3493# and then just binds it.
3494sub do_quick_binding {
3495 my ($self, $cmds) = @_;
3496 $self->set_binding (undef, undef, $cmds, sub {
3497 $::CFG->{bindings}->{$_[0]}->{$_[1]} = $_[2];
3498 });
3499 $self->ask_for_bind (1);
3500}
3501
3502sub update_binding_widgets {
3503 my ($self) = @_;
3504 my ($mod, $sym, $cmds) = $self->get_binding;
3505 $self->{keylbl}->set_text (CFClient::Binder::keycombo_to_name ($mod, $sym));
3506 $self->set_command_list ($cmds);
3507}
3508
3509sub get_binding {
3510 my ($self) = @_;
3511 return (
3512 $self->{binding}->[0],
3513 $self->{binding}->[1],
3514 [ grep { defined $_ } @{$self->{commands}} ]
3515 );
3516}
3517
3518sub clear_command_list {
3519 my ($self) = @_;
3520 $self->{cmdbox}->clear ();
3521}
3522
3523sub set_command_list {
3524 my ($self, $cmds) = @_;
3525
3526 $self->{cmdbox}->clear ();
3527 $self->{commands} = $cmds;
3528
3529 my $idx = 0;
3530
3531 for (@$cmds) {
3532 $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox);
3533
3534 my $i = $idx;
3535 $hb->add (new CFClient::UI::Label text => $_);
3536 $hb->add (new CFClient::UI::Button
3537 text => "delete",
3538 tooltip => "Deletes the action from the record",
3539 on_activate => sub {
3540 $self->{cmdbox}->remove ($hb);
3541 $cmds->[$i] = undef;
3542 });
3543
3544
3545 $idx++
3546 }
3547}
3548
3549#############################################################################
3550
3551package CFClient::UI::SpellList;
3552
3553our @ISA = CFClient::UI::Table::;
3554
3555sub new {
3556 my $class = shift;
3557
3558 my $self = $class->SUPER::new (
3559 binding => [],
3560 commands => [],
3561 @_,
3562 )
3563}
3564
3565my $TOOLTIP_ALL = "\n\n<small>Left click - ready spell\nMiddle click - invoke spell\nRight click - further options</small>";
3566
3567my @TOOLTIP_NAME = (align => -1, can_events => 1, can_hover => 1, tooltip =>
3568 "<b>Name</b>. The name of the spell.$TOOLTIP_ALL");
3569my @TOOLTIP_SKILL = (align => -1, can_events => 1, can_hover => 1, tooltip =>
3570 "<b>Skill</b>. The skill (or magic school) required to be able to attempt casting this spell.$TOOLTIP_ALL");
3571my @TOOLTIP_LVL = (align => 1, can_events => 1, can_hover => 1, tooltip =>
3572 "<b>Level</b>. Minimum level the caster needs in the associated skill to be able to attempt casting this spell.$TOOLTIP_ALL");
3573my @TOOLTIP_SP = (align => 1, can_events => 1, can_hover => 1, tooltip =>
3574 "<b>Spell points / Grace points</b>. Amount of spell or grace points used by each invocation.$TOOLTIP_ALL");
3575my @TOOLTIP_DMG = (align => 1, can_events => 1, can_hover => 1, tooltip =>
3576 "<b>Damage</b>. The amount of damage the spell deals when it hits.$TOOLTIP_ALL");
3577
3578sub rebuild_spell_list {
3579 my ($self) = @_;
3580
3581 $CFClient::UI::ROOT->on_refresh ($self => sub {
3582 $self->clear;
3583
3584 return unless $::CONN;
3585
3586 $self->add (1, 0, new CFClient::UI::Label text => "Spell Name", @TOOLTIP_NAME);
3587 $self->add (2, 0, new CFClient::UI::Label text => "Skill", @TOOLTIP_SKILL);
3588 $self->add (3, 0, new CFClient::UI::Label text => "Lvl" , @TOOLTIP_LVL);
3589 $self->add (4, 0, new CFClient::UI::Label text => "Sp/Gp", @TOOLTIP_SP);
3590 $self->add (5, 0, new CFClient::UI::Label text => "Dmg" , @TOOLTIP_DMG);
3591
3592 my $row = 0;
3593
3594 for (sort { $a cmp $b } keys %{ $self->{spell} }) {
3595 my $spell = $self->{spell}{$_};
3596
3597 $row++;
3598
3599 my $spell_cb = sub {
3600 my ($widget, $ev) = @_;
3601
3602 if ($ev->{button} == 1) {
3603 $::CONN->user_send ("cast $spell->{name}");
3604 } elsif ($ev->{button} == 2) {
3605 $::CONN->user_send ("invoke $spell->{name}");
3606 } elsif ($ev->{button} == 3) {
3607 (new CFClient::UI::Menu
3608 items => [
3609 ["bind <i>cast $spell->{name}</i> to a key" => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) }],
3610 ["bind <i>invoke $spell->{name}</i> to a key" => sub { $::BIND_EDITOR->do_quick_binding (["invoke $spell->{name}"]) }],
3611 ],
3612 )->popup ($ev);
3613 } else {
3614 return 0;
3615 }
3616
3617 1
3618 };
3619
3620 my $tooltip = "$spell->{message}$TOOLTIP_ALL";
3621
3622 #TODO: add path info to tooltip
3623 #$self->add (6, $row, new CFClient::UI::Label text => $spell->{path});
3624
3625 $self->add (0, $row, new CFClient::UI::Face
3626 face => $spell->{face},
3627 can_hover => 1,
3628 can_events => 1,
3629 tooltip => $tooltip,
3630 on_button_down => $spell_cb,
3631 );
3632
3633 $self->add (1, $row, new CFClient::UI::Label
3634 expand => 1,
3635 text => $spell->{name},
3636 can_hover => 1,
3637 can_events => 1,
3638 tooltip => $tooltip,
3639 on_button_down => $spell_cb,
3640 );
3641
3642 $self->add (2, $row, new CFClient::UI::Label text => $::CONN->{skill_info}{$spell->{skill}}, @TOOLTIP_SKILL);
3643 $self->add (3, $row, new CFClient::UI::Label text => $spell->{level}, @TOOLTIP_LVL);
3644 $self->add (4, $row, new CFClient::UI::Label text => $spell->{mana} || $spell->{grace}, @TOOLTIP_SP);
3645 $self->add (5, $row, new CFClient::UI::Label text => $spell->{damage}, @TOOLTIP_DMG);
3646 }
3647 });
3648}
3649
3650sub add_spell {
3651 my ($self, $spell) = @_;
3652
3653 $self->{spell}->{$spell->{name}} = $spell;
3654 $self->rebuild_spell_list;
3655}
3656
3657sub remove_spell {
3658 my ($self, $spell) = @_;
3659
3660 delete $self->{spell}->{$spell->{name}};
3661 $self->rebuild_spell_list;
3662}
3663
3664sub clear_spells {
3665 my ($self) = @_;
3666
3667 $self->{spell} = {};
3668 $self->rebuild_spell_list;
3669}
3670
3671#############################################################################
3672
3673package CFClient::UI::Root; 4302package CFPlus::UI::Root;
3674 4303
3675our @ISA = CFClient::UI::Container::; 4304our @ISA = CFPlus::UI::Container::;
3676 4305
3677use List::Util qw(min max); 4306use List::Util qw(min max);
3678 4307
3679use CFClient::OpenGL; 4308use CFPlus::OpenGL;
3680 4309
3681sub new { 4310sub new {
3682 my $class = shift; 4311 my $class = shift;
3683 4312
3684 my $self = $class->SUPER::new ( 4313 my $self = $class->SUPER::new (
3685 visible => 1, 4314 visible => 1,
3686 @_, 4315 @_,
3687 ); 4316 );
3688 4317
3689 Scalar::Util::weaken ($self->{root} = $self); 4318 CFPlus::weaken ($self->{root} = $self);
3690 4319
3691 $self 4320 $self
3692} 4321}
3693 4322
3694sub size_request { 4323sub size_request {
3709 $coord = $max - $size if $coord > $max - $size; 4338 $coord = $max - $size if $coord > $max - $size;
3710 4339
3711 int $coord + 0.5 4340 int $coord + 0.5
3712} 4341}
3713 4342
3714sub size_allocate { 4343sub invoke_size_allocate {
3715 my ($self, $w, $h) = @_; 4344 my ($self, $w, $h) = @_;
3716 4345
3717 for my $child ($self->children) { 4346 for my $child ($self->children) {
3718 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)}; 4347 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)};
3719 4348
3723 $X = _to_pixel $X, $W, $self->{w}; 4352 $X = _to_pixel $X, $W, $self->{w};
3724 $Y = _to_pixel $Y, $H, $self->{h}; 4353 $Y = _to_pixel $Y, $H, $self->{h};
3725 4354
3726 $child->configure ($X, $Y, $W, $H); 4355 $child->configure ($X, $Y, $W, $H);
3727 } 4356 }
4357
4358 1
3728} 4359}
3729 4360
3730sub coord2local { 4361sub coord2local {
3731 my ($self, $x, $y) = @_; 4362 my ($self, $x, $y) = @_;
3732 4363
3740} 4371}
3741 4372
3742sub update { 4373sub update {
3743 my ($self) = @_; 4374 my ($self) = @_;
3744 4375
3745 $::WANT_REFRESH++; 4376 $::WANT_REFRESH->start;
3746} 4377}
3747 4378
3748sub add { 4379sub add {
3749 my ($self, @children) = @_; 4380 my ($self, @children) = @_;
3750 4381
3787 while ($self->{refresh_hook}) { 4418 while ($self->{refresh_hook}) {
3788 $_->() 4419 $_->()
3789 for values %{delete $self->{refresh_hook}}; 4420 for values %{delete $self->{refresh_hook}};
3790 } 4421 }
3791 4422
3792 if ($self->{realloc}) { 4423 while ($self->{realloc}) {
3793 my %queue; 4424 my %queue;
3794 my @queue; 4425 my @queue;
3795 my $widget; 4426 my $widget;
3796 4427
3797 outer: 4428 outer:
3844 } 4475 }
3845 } 4476 }
3846 4477
3847 delete $self->{realloc}{$widget+0}; 4478 delete $self->{realloc}{$widget+0};
3848 } 4479 }
3849 }
3850 4480
3851 while (my $size_alloc = delete $self->{size_alloc}) { 4481 while (my $size_alloc = delete $self->{size_alloc}) {
3852 my @queue = sort { $b->{visible} <=> $a->{visible} } 4482 my @queue = sort { $a->{visible} <=> $b->{visible} }
3853 values %$size_alloc; 4483 values %$size_alloc;
3854 4484
3855 while () { 4485 while () {
3856 my $widget = pop @queue || last; 4486 my $widget = pop @queue || last;
3857 4487
3858 my ($w, $h) = @$widget{qw(alloc_w alloc_h)}; 4488 my ($w, $h) = @$widget{qw(alloc_w alloc_h)};
3859 4489
3860 $w = 0 if $w < 0; 4490 $w = max $widget->{min_w}, $w;
3861 $h = 0 if $h < 0; 4491 $h = max $widget->{min_h}, $h;
3862 4492
4493# $w = min $self->{w} - $widget->{x}, $w if $self->{w};
4494# $h = min $self->{h} - $widget->{y}, $h if $self->{h};
4495
4496 $w = min $widget->{max_w}, $w if exists $widget->{max_w};
4497 $h = min $widget->{max_h}, $h if exists $widget->{max_h};
4498
3863 $w = int $w + 0.5; 4499 $w = int $w + 0.5;
3864 $h = int $h + 0.5; 4500 $h = int $h + 0.5;
3865 4501
3866 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) { 4502 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) {
3867 $widget->{old_w} = $widget->{w}; 4503 $widget->{old_w} = $widget->{w};
3868 $widget->{old_h} = $widget->{h}; 4504 $widget->{old_h} = $widget->{h};
3869 4505
3870 $widget->{w} = $w; 4506 $widget->{w} = $w;
3871 $widget->{h} = $h; 4507 $widget->{h} = $h;
3872 4508
3873 $widget->emit (size_allocate => $w, $h); 4509 $widget->emit (size_allocate => $w, $h);
4510 }
3874 } 4511 }
3875 } 4512 }
3876 } 4513 }
3877 4514
3878 while ($self->{post_alloc_hook}) { 4515 while ($self->{post_alloc_hook}) {
3879 $_->() 4516 $_->()
3880 for values %{delete $self->{post_alloc_hook}}; 4517 for values %{delete $self->{post_alloc_hook}};
3881 } 4518 }
3882
3883 4519
3884 glViewport 0, 0, $::WIDTH, $::HEIGHT; 4520 glViewport 0, 0, $::WIDTH, $::HEIGHT;
3885 glClearColor +($::CFG->{fow_intensity}) x 3, 1; 4521 glClearColor +($::CFG->{fow_intensity}) x 3, 1;
3886 glClear GL_COLOR_BUFFER_BIT; 4522 glClear GL_COLOR_BUFFER_BIT;
3887 4523
3890 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000; 4526 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000;
3891 glMatrixMode GL_MODELVIEW; 4527 glMatrixMode GL_MODELVIEW;
3892 glLoadIdentity; 4528 glLoadIdentity;
3893 4529
3894 { 4530 {
3895 package CFClient::UI::Base; 4531 package CFPlus::UI::Base;
3896 4532
3897 ($draw_x, $draw_y, $draw_w, $draw_h) = 4533 local ($draw_x, $draw_y, $draw_w, $draw_h) =
3898 (0, 0, $self->{w}, $self->{h}); 4534 (0, 0, $self->{w}, $self->{h});
3899 }
3900 4535
3901 $self->_draw; 4536 $self->_draw;
4537 }
3902} 4538}
3903 4539
3904############################################################################# 4540#############################################################################
3905 4541
3906package CFClient::UI; 4542package CFPlus::UI;
3907 4543
3908$ROOT = new CFClient::UI::Root; 4544$ROOT = new CFPlus::UI::Root;
3909$TOOLTIP = new CFClient::UI::Tooltip z => 900; 4545$TOOLTIP = new CFPlus::UI::Tooltip z => 900;
3910 4546
39111 45471
3912 4548

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines