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.307 by root, Sun Jun 18 19:13:20 2006 UTC vs.
Revision 1.453 by root, Wed Dec 26 20:46:39 2007 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines