… | |
… | |
335 | sub size_request { |
335 | sub size_request { |
336 | require Carp; |
336 | require Carp; |
337 | Carp::confess "size_request is abstract"; |
337 | Carp::confess "size_request is abstract"; |
338 | } |
338 | } |
339 | |
339 | |
|
|
340 | sub baseline_shift { |
|
|
341 | 0 |
|
|
342 | } |
|
|
343 | |
340 | sub configure { |
344 | sub configure { |
341 | my ($self, $x, $y, $w, $h) = @_; |
345 | my ($self, $x, $y, $w, $h) = @_; |
342 | |
346 | |
343 | if ($self->{aspect}) { |
347 | if ($self->{aspect}) { |
344 | my ($ow, $oh) = ($w, $h); |
348 | my ($ow, $oh) = ($w, $h); |
… | |
… | |
535 | return unless $self->{h} && $self->{w}; |
539 | return unless $self->{h} && $self->{w}; |
536 | |
540 | |
537 | # update screen rectangle |
541 | # update screen rectangle |
538 | local $draw_x = $draw_x + $self->{x}; |
542 | local $draw_x = $draw_x + $self->{x}; |
539 | local $draw_y = $draw_y + $self->{y}; |
543 | local $draw_y = $draw_y + $self->{y}; |
540 | local $draw_w = $draw_x + $self->{w}; |
|
|
541 | local $draw_h = $draw_y + $self->{h}; |
|
|
542 | |
544 | |
543 | # skip widgets that are entirely outside the drawing area |
545 | # skip widgets that are entirely outside the drawing area |
544 | return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w) |
546 | return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w) |
545 | || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h); |
547 | || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h); |
546 | |
548 | |
… | |
… | |
1091 | if exists $self->{title}; |
1093 | if exists $self->{title}; |
1092 | |
1094 | |
1093 | if ($self->{has_close_button}) { |
1095 | if ($self->{has_close_button}) { |
1094 | $self->{close_button} = |
1096 | $self->{close_button} = |
1095 | new CFClient::UI::ImageButton |
1097 | new CFClient::UI::ImageButton |
1096 | image => 'x1_close.png', |
1098 | path => 'x1_close.png', |
1097 | on_activate => sub { $self->hide }; |
1099 | on_activate => sub { $self->hide }; |
1098 | |
1100 | |
1099 | $self->CFClient::UI::Container::add ($self->{close_button}); |
1101 | $self->CFClient::UI::Container::add ($self->{close_button}); |
1100 | } |
1102 | } |
1101 | |
1103 | |
… | |
… | |
1438 | |
1440 | |
1439 | sub invoke_size_allocate { |
1441 | sub invoke_size_allocate { |
1440 | my ($self, $w, $h) = @_; |
1442 | my ($self, $w, $h) = @_; |
1441 | |
1443 | |
1442 | my $space = $self->{vertical} ? $h : $w; |
1444 | my $space = $self->{vertical} ? $h : $w; |
1443 | my $children = $self->{children}; |
1445 | my @children = $self->visible_children; |
1444 | |
1446 | |
1445 | my @req; |
1447 | my @req; |
1446 | |
1448 | |
1447 | if ($self->{homogeneous}) { |
1449 | if ($self->{homogeneous}) { |
1448 | @req = ($space / (@$children || 1)) x @$children; |
1450 | @req = ($space / (@children || 1)) x @children; |
1449 | } else { |
1451 | } else { |
1450 | @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @$children; |
1452 | @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @children; |
1451 | my $req = List::Util::sum @req; |
1453 | my $req = List::Util::sum @req; |
1452 | |
1454 | |
1453 | if ($req > $space) { |
1455 | if ($req > $space) { |
1454 | # ah well, not enough space |
1456 | # ah well, not enough space |
1455 | $_ *= $space / $req for @req; |
1457 | $_ *= $space / $req for @req; |
1456 | } else { |
1458 | } else { |
1457 | my $expand = (List::Util::sum map $_->{expand}, @$children) || 1; |
1459 | my $expand = (List::Util::sum map $_->{expand}, @children) || 1; |
1458 | |
1460 | |
1459 | $space = ($space - $req) / $expand; # remaining space to give away |
1461 | $space = ($space - $req) / $expand; # remaining space to give away |
1460 | |
1462 | |
1461 | $req[$_] += $space * $children->[$_]{expand} |
1463 | $req[$_] += $space * $children[$_]{expand} |
1462 | for 0 .. $#$children; |
1464 | for 0 .. $#children; |
1463 | } |
1465 | } |
1464 | } |
1466 | } |
1465 | |
1467 | |
1466 | CFClient::UI::harmonize \@req; |
1468 | CFClient::UI::harmonize \@req; |
1467 | |
1469 | |
1468 | my $pos = 0; |
1470 | my $pos = 0; |
1469 | for (0 .. $#$children) { |
1471 | for (0 .. $#children) { |
1470 | my $alloc = $req[$_]; |
1472 | my $alloc = $req[$_]; |
1471 | $children->[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h)); |
1473 | $children[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h)); |
1472 | |
1474 | |
1473 | $pos += $alloc; |
1475 | $pos += $alloc; |
1474 | } |
1476 | } |
1475 | |
1477 | |
1476 | 1 |
1478 | 1 |
… | |
… | |
1631 | }; |
1633 | }; |
1632 | |
1634 | |
1633 | @{ $self->{size_req} } |
1635 | @{ $self->{size_req} } |
1634 | } |
1636 | } |
1635 | |
1637 | |
|
|
1638 | sub baseline_shift { |
|
|
1639 | $_[0]{layout}->descent |
|
|
1640 | } |
|
|
1641 | |
1636 | sub invoke_size_allocate { |
1642 | sub invoke_size_allocate { |
1637 | my ($self, $w, $h) = @_; |
1643 | my ($self, $w, $h) = @_; |
1638 | |
1644 | |
1639 | delete $self->{ox}; |
1645 | delete $self->{ox}; |
1640 | |
1646 | |
… | |
… | |
2092 | package CFClient::UI::Image; |
2098 | package CFClient::UI::Image; |
2093 | |
2099 | |
2094 | our @ISA = CFClient::UI::Base::; |
2100 | our @ISA = CFClient::UI::Base::; |
2095 | |
2101 | |
2096 | use CFClient::OpenGL; |
2102 | use CFClient::OpenGL; |
2097 | use Carp qw/confess/; |
|
|
2098 | |
2103 | |
2099 | our %loaded_images; |
2104 | our %texture_cache; |
2100 | |
2105 | |
2101 | sub new { |
2106 | sub new { |
2102 | my $class = shift; |
2107 | my $class = shift; |
2103 | |
2108 | |
2104 | my $self = $class->SUPER::new (can_events => 0, @_); |
2109 | my $self = $class->SUPER::new ( |
|
|
2110 | can_events => 0, |
|
|
2111 | @_, |
|
|
2112 | ); |
2105 | |
2113 | |
2106 | $self->{image} or confess "Image has 'image' not set. This is a fatal error!"; |
2114 | $self->{path} |
|
|
2115 | or Carp::croak "required attribute 'path' not set"; |
2107 | |
2116 | |
2108 | $loaded_images{$self->{image}} ||= |
2117 | $self->{tex} = $texture_cache{$self->{path}} ||= |
2109 | new_from_file CFClient::Texture CFClient::find_rcfile $self->{image}, mipmap => 1; |
2118 | new_from_file CFClient::Texture CFClient::find_rcfile $self->{path}, mipmap => 1; |
2110 | |
2119 | |
2111 | my $tex = $self->{tex} = $loaded_images{$self->{image}}; |
2120 | Scalar::Util::weaken $texture_cache{$self->{path}}; |
2112 | |
2121 | |
2113 | Scalar::Util::weaken $loaded_images{$self->{image}}; |
2122 | $self->{aspect} ||= $self->{tex}{w} / $self->{tex}{h}; |
2114 | |
|
|
2115 | $self->{aspect} = $tex->{w} / $tex->{h}; |
|
|
2116 | |
2123 | |
2117 | $self |
2124 | $self |
2118 | } |
2125 | } |
2119 | |
2126 | |
2120 | sub size_request { |
2127 | sub size_request { |
2121 | my ($self) = @_; |
2128 | my ($self) = @_; |
2122 | |
2129 | |
2123 | ($self->{tex}->{w}, $self->{tex}->{h}) |
2130 | ($self->{tex}{w}, $self->{tex}{h}) |
2124 | } |
2131 | } |
2125 | |
2132 | |
2126 | sub _draw { |
2133 | sub _draw { |
2127 | my ($self) = @_; |
2134 | my ($self) = @_; |
2128 | |
2135 | |
… | |
… | |
2566 | |
2573 | |
2567 | $self->{fontsize} = $fontsize; |
2574 | $self->{fontsize} = $fontsize; |
2568 | $self->reflow; |
2575 | $self->reflow; |
2569 | } |
2576 | } |
2570 | |
2577 | |
|
|
2578 | sub size_request { |
|
|
2579 | my ($self) = @_; |
|
|
2580 | |
|
|
2581 | my ($empty, $slider) = @{ $self->{children} }; |
|
|
2582 | |
|
|
2583 | local $self->{children} = [$empty, $slider]; |
|
|
2584 | $self->SUPER::size_request |
|
|
2585 | } |
|
|
2586 | |
2571 | sub invoke_size_allocate { |
2587 | sub invoke_size_allocate { |
2572 | my ($self, $w, $h) = @_; |
2588 | my ($self, $w, $h) = @_; |
2573 | |
2589 | |
|
|
2590 | my ($empty, $slider, @other) = @{ $self->{children} }; |
|
|
2591 | $_->configure (@$_{qw(x y req_w req_h)}) for @other; |
|
|
2592 | |
2574 | $self->{layout}->set_font ($self->{font}) if $self->{font}; |
2593 | $self->{layout}->set_font ($self->{font}) if $self->{font}; |
2575 | $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); |
2594 | $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); |
2576 | $self->{layout}->set_width ($self->{children}[0]{w}); |
2595 | $self->{layout}->set_width ($empty->{w}); |
2577 | $self->{layout}->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
2596 | $self->{layout}->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
2578 | |
2597 | |
2579 | $self->reflow; |
2598 | $self->reflow; |
2580 | |
2599 | |
|
|
2600 | local $self->{children} = [$empty, $slider]; |
2581 | $self->SUPER::invoke_size_allocate ($w, $h) |
2601 | $self->SUPER::invoke_size_allocate ($w, $h) |
2582 | } |
2602 | } |
2583 | |
2603 | |
2584 | sub text_size { |
2604 | sub get_layout { |
2585 | my ($self, $text, $indent) = @_; |
2605 | my ($self, $para) = @_; |
2586 | |
2606 | |
2587 | my $layout = $self->{layout}; |
2607 | my $layout = $self->{layout}; |
2588 | |
2608 | |
|
|
2609 | $layout->set_font ($self->{font}) if $self->{font}; |
|
|
2610 | $layout->set_foreground (@{$para->{fg}}); |
2589 | $layout->set_height ($self->{fontsize} * $::FONTSIZE); |
2611 | $layout->set_height ($self->{fontsize} * $::FONTSIZE); |
2590 | $layout->set_width ($self->{children}[0]{w} - $indent); |
2612 | $layout->set_width ($self->{children}[0]{w} - $para->{indent}); |
2591 | $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
2613 | $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
2592 | $layout->set_markup ($text); |
2614 | $layout->set_markup ($para->{markup}); |
|
|
2615 | |
|
|
2616 | $layout->set_shapes ( |
|
|
2617 | map |
|
|
2618 | +(0, $_->baseline_shift +$_->{padding_y} - $_->{h}, $_->{w}, $_->{h}), |
|
|
2619 | @{$para->{widget}} |
2593 | |
2620 | ); |
|
|
2621 | |
2594 | $layout->size |
2622 | $layout |
2595 | } |
2623 | } |
2596 | |
2624 | |
2597 | sub reflow { |
2625 | sub reflow { |
2598 | my ($self) = @_; |
2626 | my ($self) = @_; |
2599 | |
2627 | |
… | |
… | |
2608 | $self->{children}[1]->set_value ($offset); |
2636 | $self->{children}[1]->set_value ($offset); |
2609 | } |
2637 | } |
2610 | |
2638 | |
2611 | sub clear { |
2639 | sub clear { |
2612 | my ($self) = @_; |
2640 | my ($self) = @_; |
|
|
2641 | |
|
|
2642 | my (undef, undef, @other) = @{ $self->{children} }; |
|
|
2643 | $self->remove ($_) for @other; |
2613 | |
2644 | |
2614 | $self->{par} = []; |
2645 | $self->{par} = []; |
2615 | $self->{height} = 0; |
2646 | $self->{height} = 0; |
2616 | $self->{children}[1]->set_range ([0, 0, 0, 1, 1]); |
2647 | $self->{children}[1]->set_range ([0, 0, 0, 1, 1]); |
2617 | } |
2648 | } |
2618 | |
2649 | |
2619 | sub add_paragraph { |
2650 | sub add_paragraph { |
2620 | my ($self, $color, $text, $indent) = @_; |
2651 | my ($self, $color, $para, $indent) = @_; |
2621 | |
2652 | |
2622 | for my $line (split /\n/, $text) { |
2653 | my ($text, @w) = ref $para ? @$para : $para; |
2623 | my ($w, $h) = $self->text_size ($line); |
2654 | |
2624 | $self->{height} += $h; |
2655 | $para = { |
2625 | push @{$self->{par}}, [$w + $indent, $h, $color, $indent, $line]; |
2656 | w => 1e10, |
|
|
2657 | wrapped => 1, |
|
|
2658 | fg => $color, |
|
|
2659 | indent => $indent, |
|
|
2660 | markup => $text, |
|
|
2661 | widget => \@w, |
2626 | } |
2662 | }; |
2627 | |
2663 | |
2628 | $self->{children}[1]->set_range ([$self->{height}, 0, $self->{height}, $self->{h}, 1]); |
2664 | $self->add (@w) if @w; |
|
|
2665 | push @{$self->{par}}, $para; |
|
|
2666 | |
|
|
2667 | $self->{need_reflow}++; |
|
|
2668 | $self->update; |
|
|
2669 | } |
|
|
2670 | |
|
|
2671 | sub scroll_to_bottom { |
|
|
2672 | my ($self) = @_; |
|
|
2673 | |
|
|
2674 | $self->{scroll_to_bottom} = 1; |
|
|
2675 | $self->update; |
2629 | } |
2676 | } |
2630 | |
2677 | |
2631 | sub update { |
2678 | sub update { |
2632 | my ($self) = @_; |
2679 | my ($self) = @_; |
2633 | |
2680 | |
… | |
… | |
2641 | my ($W, $H) = @{$self->{children}[0]}{qw(w h)}; |
2688 | my ($W, $H) = @{$self->{children}[0]}{qw(w h)}; |
2642 | |
2689 | |
2643 | if (delete $self->{need_reflow}) { |
2690 | if (delete $self->{need_reflow}) { |
2644 | my $height = 0; |
2691 | my $height = 0; |
2645 | |
2692 | |
2646 | my $layout = $self->{layout}; |
|
|
2647 | |
|
|
2648 | $layout->set_height ($self->{fontsize} * $::FONTSIZE); |
|
|
2649 | |
|
|
2650 | for (@{$self->{par}}) { |
2693 | for my $para (@{$self->{par}}) { |
2651 | if (1 || $_->[0] >= $W) { # TODO: works,but needs reconfigure etc. support |
2694 | if ($para->{w} != $W && ($para->{wrapped} || $para->{w} > $W)) { |
2652 | $layout->set_width ($W - $_->[3]); |
2695 | my $layout = $self->get_layout ($para); |
2653 | $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
|
|
2654 | $layout->set_markup ($_->[4]); |
|
|
2655 | my ($w, $h) = $layout->size; |
2696 | my ($w, $h) = $layout->size; |
2656 | $_->[0] = $w + $_->[3]; |
2697 | |
2657 | $_->[1] = $h; |
2698 | $para->{w} = $w + $para->{indent}; |
|
|
2699 | $para->{h} = $h; |
|
|
2700 | $para->{wrapped} = $layout->has_wrapped; |
2658 | } |
2701 | } |
2659 | |
2702 | |
2660 | $height += $_->[1]; |
2703 | $height += $para->{h}; |
2661 | } |
2704 | } |
2662 | |
2705 | |
2663 | $self->{height} = $height; |
2706 | $self->{height} = $height; |
2664 | |
2707 | |
2665 | $self->{children}[1]->set_range ([$height, 0, $height, $H, 1]); |
2708 | $self->{children}[1]->set_range ([$self->{children}[1]{range}[0], 0, $height, $H, 1]); |
2666 | |
2709 | |
2667 | delete $self->{texture}; |
2710 | delete $self->{texture}; |
|
|
2711 | } |
|
|
2712 | |
|
|
2713 | if (delete $self->{scroll_to_bottom}) { |
|
|
2714 | $self->{children}[1]->set_value (1e10); |
2668 | } |
2715 | } |
2669 | |
2716 | |
2670 | $self->{texture} ||= new_from_opengl CFClient::Texture $W, $H, sub { |
2717 | $self->{texture} ||= new_from_opengl CFClient::Texture $W, $H, sub { |
2671 | glClearColor 0, 0, 0, 0; |
2718 | glClearColor 0, 0, 0, 0; |
2672 | glClear GL_COLOR_BUFFER_BIT; |
2719 | glClear GL_COLOR_BUFFER_BIT; |
… | |
… | |
2676 | my $y0 = $top; |
2723 | my $y0 = $top; |
2677 | my $y1 = $top + $H; |
2724 | my $y1 = $top + $H; |
2678 | |
2725 | |
2679 | my $y = 0; |
2726 | my $y = 0; |
2680 | |
2727 | |
2681 | my $layout = $self->{layout}; |
|
|
2682 | |
|
|
2683 | $layout->set_font ($self->{font}) if $self->{font}; |
|
|
2684 | |
|
|
2685 | glEnable GL_BLEND; |
2728 | glEnable GL_BLEND; |
2686 | #TODO# not correct in windows where rgba is forced off |
2729 | #TODO# not correct in windows where rgba is forced off |
2687 | glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; |
2730 | glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; |
2688 | |
2731 | |
2689 | for my $par (@{$self->{par}}) { |
2732 | for my $para (@{$self->{par}}) { |
2690 | my $h = $par->[1]; |
2733 | my $h = $para->{h}; |
2691 | |
2734 | |
2692 | if ($y0 < $y + $h && $y < $y1) { |
2735 | if ($y0 < $y + $h && $y < $y1) { |
2693 | $layout->set_foreground (@{ $par->[2] }); |
2736 | |
2694 | $layout->set_width ($W - $par->[3]); |
2737 | my $layout = $self->get_layout ($para); |
2695 | $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent}); |
|
|
2696 | $layout->set_markup ($par->[4]); |
|
|
2697 | |
2738 | |
2698 | my ($w, $h, $data, $format, $internalformat) = $layout->render; |
2739 | my ($w, $h, $data, $format, $internalformat) = $layout->render; |
2699 | |
2740 | |
2700 | glRasterPos $par->[3], $y - $y0; |
2741 | glRasterPos $para->{indent}, $y - $y0; |
2701 | glDrawPixels $w, $h, $format, GL_UNSIGNED_BYTE, $data; |
2742 | glDrawPixels $w, $h, $format, GL_UNSIGNED_BYTE, $data; |
|
|
2743 | |
|
|
2744 | if (my @w = @{ $para->{widget} }) { |
|
|
2745 | my @s = $layout->get_shapes; |
|
|
2746 | |
|
|
2747 | for (@w) { |
|
|
2748 | my ($dx, $dy) = splice @s, 0, 2, (); |
|
|
2749 | |
|
|
2750 | $_->{x} = $dx + $para->{indent}; |
|
|
2751 | $_->{y} = $dy + $y - $y0; |
|
|
2752 | |
|
|
2753 | $_->draw; |
|
|
2754 | } |
|
|
2755 | } |
2702 | } |
2756 | } |
2703 | |
2757 | |
2704 | $y += $h; |
2758 | $y += $h; |
2705 | } |
2759 | } |
2706 | |
2760 | |
2707 | glDisable GL_BLEND; |
2761 | glDisable GL_BLEND; |
2708 | }; |
2762 | }; |
2709 | }); |
2763 | }); |
|
|
2764 | } |
|
|
2765 | |
|
|
2766 | sub reconfigure { |
|
|
2767 | my ($self) = @_; |
|
|
2768 | |
|
|
2769 | $self->SUPER::reconfigure; |
|
|
2770 | |
|
|
2771 | $_->{w} = 1e10 for @{ $self->{par} }; |
|
|
2772 | $self->reflow; |
2710 | } |
2773 | } |
2711 | |
2774 | |
2712 | sub _draw { |
2775 | sub _draw { |
2713 | my ($self) = @_; |
2776 | my ($self) = @_; |
2714 | |
2777 | |
… | |
… | |
2717 | glColor 0, 0, 0, 1; |
2780 | glColor 0, 0, 0, 1; |
2718 | $self->{texture}->draw_quad_alpha_premultiplied (0, 0, $self->{children}[0]{w}, $self->{children}[0]{h}); |
2781 | $self->{texture}->draw_quad_alpha_premultiplied (0, 0, $self->{children}[0]{w}, $self->{children}[0]{h}); |
2719 | glDisable GL_TEXTURE_2D; |
2782 | glDisable GL_TEXTURE_2D; |
2720 | |
2783 | |
2721 | $self->{children}[1]->draw; |
2784 | $self->{children}[1]->draw; |
2722 | |
|
|
2723 | } |
2785 | } |
2724 | |
2786 | |
2725 | ############################################################################# |
2787 | ############################################################################# |
2726 | |
2788 | |
2727 | package CFClient::UI::Animator; |
2789 | package CFClient::UI::Animator; |