--- deliantra/Deliantra-Client/DC/UI.pm 2008/09/03 10:36:25 1.468 +++ deliantra/Deliantra-Client/DC/UI.pm 2012/11/23 12:39:05 1.488 @@ -1,10 +1,12 @@ package DC::UI; -use utf8; -use strict; +use common::sense; use List::Util (); +use AnyEvent (); +use Guard (); + use DC; use DC::Pod; use DC::Texture; @@ -52,7 +54,7 @@ for (grep { $_->{name} } values %WIDGET) { my $win = $layout->{$_->{name}} = { }; - + $win->{x} = ($_->{x} + $_->{w} * 0.5) / $::WIDTH if $_->{x} =~ /^[0-9.]+$/; $win->{y} = ($_->{y} + $_->{h} * 0.5) / $::HEIGHT if $_->{y} =~ /^[0-9.]+$/; $win->{w} = $_->{w} / $::WIDTH if defined $_->{w}; @@ -71,7 +73,7 @@ } # class methods for events -sub feed_sdl_key_down_event { +sub feed_sdl_key_down_event { $FOCUS->emit (key_down => $_[0]) if $FOCUS; } @@ -191,7 +193,7 @@ my ($sx, $sy) = @_; for my $widget (values %WIDGET) { - if ($widget->{is_toplevel}) { + if ($widget->{is_toplevel} || $widget->{c_rescale}) { $widget->{x} += int $widget->{w} * 0.5 if $widget->{x} =~ /^[0-9.]+$/; $widget->{y} += int $widget->{h} * 0.5 if $widget->{y} =~ /^[0-9.]+$/; @@ -223,7 +225,7 @@ package DC::UI::Base; -use strict; +use common::sense; use DC::OpenGL; @@ -288,6 +290,9 @@ return if $self->{visible}; + $self->{parent} && $self->{parent}{root}#d# + or return ::clienterror ("set_visible called without parent ($self->{parent}) or root\n" => 1); + $self->{root} = $self->{parent}{root}; $self->{visible} = $self->{parent}{visible} + 1; @@ -295,7 +300,7 @@ $self->realloc if !exists $self->{req_w}; - $_->set_visible for $self->children; + $_->set_visible for $self->visible_children; } sub set_invisible { @@ -360,29 +365,29 @@ sub set_size { my ($self, $w, $h) = @_; - $self->{force_w} = $w; - $self->{force_h} = $h; + $self->{force_w} = List::Util::min $w, ($self->{max_w} || $::WIDTH ); + $self->{force_h} = List::Util::min $h, ($self->{max_h} || $::HEIGHT); $self->realloc; } -# traverse the widget chain up to find the maximum "physical" size constraints -sub get_max_wh { - my ($self) = @_; - - my ($w, $h) = @$self{qw(max_w max_h)}; - - if ($w <= 0 || $h <= 0) { - my ($mw, $mh) = $self->{parent} - ? $self->{parent}->get_max_wh - : ($::WIDTH, $::HEIGHT); - - $w = $mw if $w <= 0; - $h = $mh if $h <= 0; - } - - ($w, $h) -} +## traverse the widget chain up to find the maximum "physical" size constraints +#sub get_max_wh { +# my ($self) = @_; +# +# my ($w, $h) = @$self{qw(max_w max_h)}; +# +# if ($w <= 0 || $h <= 0) { +# my ($mw, $mh) = $self->{parent} +# ? $self->{parent}->get_max_wh +# : ($::WIDTH, $::HEIGHT); +# +# $w = $mw if $w <= 0; +# $h = $mh if $h <= 0; +# } +# +# ($w, $h) +#} sub size_request { require Carp; @@ -529,7 +534,7 @@ push @{ $self->{signal_cb}{$signal} }, $cb; - defined wantarray and DC::guard { + defined wantarray and Guard::guard { @{ $self->{signal_cb}{$signal} } = grep $_ != $cb, @{ $self->{signal_cb}{$signal} }; } @@ -640,7 +645,8 @@ our @ISA = DC::UI::Base::; -use strict; +use common::sense; + use DC::OpenGL; sub new { @@ -653,11 +659,18 @@ ) } +sub set_bg { + my ($self, $bg) = @_; + + $self->{bg} = $bg; + $self->update; +} + sub _draw { my ($self) = @_; - my $color = $FOCUS == $self && $self->{active_bg} - ? $self->{active_bg} + my $color = $FOCUS == $self + ? $self->{active_bg} || $self->{bg} : $self->{bg}; if ($color && (@$color < 4 || $color->[3])) { @@ -1145,7 +1158,7 @@ my $ox = $self->{vp}{view_x}; my $oy = $self->{vp}{view_y}; - + $self->{motion} = sub { my ($ev, $x, $y) = @_; @@ -1345,11 +1358,11 @@ use DC::OpenGL; -my $bg = +my $bg = new_from_resource DC::Texture "d1_bg.png", mipmap => 1, wrap => 1; -my @border = +my @border = map { new_from_resource DC::Texture $_, mipmap => 1 } qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); @@ -1363,7 +1376,7 @@ my $self = $class->SUPER::new ( bg => [1, 1, 1, 1], border_bg => [1, 1, 1, 1], - border => 1, + border => 0.8, can_events => 1, min_w => 64, min_h => 32, @@ -1401,14 +1414,14 @@ int $_[0]{border} * $::FONTSIZE } -sub get_max_wh { - my ($self) = @_; - - return ($self->{w}, $self->{h}) - if $self->{visible} && $self->{w}; - - $self->SUPER::get_max_wh -} +#sub get_max_wh { +# my ($self) = @_; +# +# return ($self->{w}, $self->{h}) +# if $self->{visible} && $self->{w}; +# +# $self->SUPER::get_max_wh +#} sub size_request { my ($self) = @_; @@ -1453,7 +1466,7 @@ my ($self) = @_; $self->hide; - + 1 } @@ -1463,10 +1476,10 @@ my ($w, $h) = @$self{qw(w h)}; my $border = $self->border; - my $lr = ($x >= 0 && $x < $border) || ($x > $w - $border && $x < $w); - my $td = ($y >= 0 && $y < $border) || ($y > $h - $border && $y < $h); + my $lr = ($x >= 0 && $x < $border) || ($x > $w - $border && $x < $w); # left-right + my $td = ($y >= 0 && $y < $border) || ($y > $h - $border && $y < $h); # top-down - if ($lr & $td) { + if ($lr & $td) { # corners my ($wx, $wy) = ($self->{x}, $self->{y}); my ($ox, $oy) = ($ev->{x}, $ev->{y}); my ($bw, $bh) = ($self->{w}, $self->{h}); @@ -1480,14 +1493,16 @@ my $dx = $ev->{x} - $ox; my $dy = $ev->{y} - $oy; - $self->{force_w} = $bw + $dx * ($mx ? -1 : 1); - $self->{force_h} = $bh + $dy * ($my ? -1 : 1); + $self->set_size ( + $bw + $dx * ($mx ? -1 : 1), + $bh + $dy * ($my ? -1 : 1), + ); $self->move_abs ($wx + $dx * $mx, $wy + $dy * $my); $self->realloc; }; - } elsif ($lr ^ $td) { + } elsif ($lr ^ $td) { # edges my ($ox, $oy) = ($ev->{x}, $ev->{y}); my ($bx, $by) = ($self->{x}, $self->{y}); @@ -1542,25 +1557,27 @@ my $border = $self->border; - glColor @{ $self->{border_bg} }; - $border[0]->draw_quad_alpha ( 0, 0, $w, $border); - $border[1]->draw_quad_alpha ( 0, $border, $border, $ch); - $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); - $border[3]->draw_quad_alpha ( 0, $h - $border, $w, $border); - - # move - my $w2 = ($w - $border) * .5; - my $h2 = ($h - $border) * .5; - $icon[0]->draw_quad_alpha ( 0, $h2, $border, $border); - $icon[0]->draw_quad_alpha ($w - $border, $h2, $border, $border); - $icon[0]->draw_quad_alpha ($w2 , $h - $border, $border, $border); - - # resize - $icon[1]->draw_quad_alpha ( 0, 0, $border, $border); - $icon[1]->draw_quad_alpha ($w - $border, 0, $border, $border) - unless $self->{has_close_button}; - $icon[1]->draw_quad_alpha ( 0, $h - $border, $border, $border); - $icon[1]->draw_quad_alpha ($w - $border, $h - $border, $border, $border); + if ($border) { + glColor @{ $self->{border_bg} }; + $border[0]->draw_quad_alpha ( 0, 0, $w, $border); + $border[1]->draw_quad_alpha ( 0, $border, $border, $ch); + $border[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); + $border[3]->draw_quad_alpha ( 0, $h - $border, $w, $border); + + # move + my $w2 = ($w - $border) * .5; + my $h2 = ($h - $border) * .5; + $icon[0]->draw_quad_alpha ( 0, $h2, $border, $border); + $icon[0]->draw_quad_alpha ($w - $border, $h2, $border, $border); + $icon[0]->draw_quad_alpha ($w2 , $h - $border, $border, $border); + + # resize + $icon[1]->draw_quad_alpha ( 0, 0, $border, $border); + $icon[1]->draw_quad_alpha ($w - $border, 0, $border, $border) + unless $self->{has_close_button}; + $icon[1]->draw_quad_alpha ( 0, $h - $border, $border, $border); + $icon[1]->draw_quad_alpha ($w - $border, $h - $border, $border, $border); + } if (@{$self->{bg}} < 4 || $self->{bg}[3]) { glColor @{ $self->{bg} }; @@ -1825,7 +1842,7 @@ $_ *= $space / $req for @req; } else { my $expand = (List::Util::sum map $_->{expand}, @children) || 1; - + $space = ($space - $req) / $expand; # remaining space to give away $req[$_] += $space * $children[$_]{expand} @@ -1974,7 +1991,7 @@ my ($self) = @_; $self->{size_req} ||= do { - my ($max_w, $max_h) = $self->get_max_wh; + my $max_w = $self->{w} || $self->{max_w} || 0x0fffffff; # actually 2**31-1 but allow for overflow inside pango $self->{layout}->set_font ($self->{font}) if $self->{font}; $self->{layout}->set_width ($max_w); @@ -2064,7 +2081,7 @@ # $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style}); # DC::OpenGL::glEndList; # } -# +# # DC::OpenGL::glCallList $self->{list}; $self->{layout}->draw; @@ -2223,7 +2240,7 @@ $self->_set_text ($self->{text}); $self->update; - + 1 } @@ -2518,6 +2535,7 @@ my $class = shift; $class->SUPER::new ( + fontsize => 1, padding_x => 2, padding_y => 2, fg => [1, 1, 1], @@ -2533,7 +2551,7 @@ sub size_request { my ($self) = @_; - (6) x 2 + ($self->{fontsize} * $::FONTSIZE) x 2 } sub toggle { @@ -2581,7 +2599,7 @@ package DC::UI::Image; -our @ISA = DC::UI::Base::; +our @ISA = DC::UI::DrawBG::; use DC::OpenGL; @@ -2624,6 +2642,13 @@ $self->new (path => $path) } +sub set_texture { + my ($self, $tex) = @_; + + $self->{tex} = $tex; + $self->update; +} + sub size_request { my ($self) = @_; @@ -2633,6 +2658,8 @@ sub _draw { my ($self) = @_; + $self->SUPER::_draw; + my $tex = $self->{tex}; my ($w, $h) = ($self->{w}, $self->{h}); @@ -2660,8 +2687,6 @@ use DC::OpenGL; -my %textures; - sub new { my $class = shift; @@ -2841,6 +2866,8 @@ my ($class, %arg) = @_; my $self = $class->SUPER::new ( + padding_x => 2, + padding_y => 2, fg => [1, 1, 1], bg => [0, 0, 1, 0.2], bar => [0.7, 0.5, 0.1, 0.8], @@ -2890,17 +2917,24 @@ glEnable GL_BLEND; glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; + my $px = $self->{padding_x}; + my $py = $self->{padding_y}; + if ($self->{value} >= 0) { - my $s = int 2 + ($self->{w} - 4) * $self->{value}; + my $s = int $px + ($self->{w} - $px * 2) * $self->{value}; glColor_premultiply @{$self->{bar}}; - glRect 2, 2, $s, $self->{h} - 2; + glRect $px, $py, $s, $self->{h} - $py; glColor_premultiply @{$self->{bg}}; - glRect $s, 2, $self->{w} - 2, $self->{h} - 2; + glRect $s , $py, $self->{w} - $px, $self->{h} - $py; } glColor_premultiply @{$self->{outline}}; - glRect_lineloop 1.5, 1.5, $self->{w} - 1.5, $self->{h} - 1.5; + + $px -= .5; + $py -= .5; + + glRect_lineloop $px, $py, $self->{w} - $px, $self->{h} - $py; glDisable GL_BLEND; @@ -2919,17 +2953,21 @@ sub new { my ($class, %arg) = @_; + my $tt = exists $arg{tooltip} ? "$arg{tooltip}\n\n" : ""; + my $self = $class->SUPER::new ( + %arg, tooltip => sub { my ($self) = @_; - sprintf "level %d\n%s points\n%s next level\n%s to go", + sprintf "%slevel %d\n%s points\n%s next level\n%s to go, %d%% done", + $tt, $self->{lvl}, ::formsep ($self->{exp}), ::formsep ($self->{nxt}), ::formsep ($self->{nxt} - $self->{exp}), + $self->_percent * 100, }, - %arg ); $::CONN->{on_exp_update}{$self+0} = sub { $self->set_value ($self->{value}) } @@ -2947,24 +2985,27 @@ $self->SUPER::DESTROY; } -sub set_value { - my ($self, $lvl, $exp) = @_; +sub _percent { + my ($self) = @_; - $self->{lvl} = $lvl; - $self->{exp} = $exp; + my $table = $::CONN && $::CONN->{exp_table} + or return -1; - my $v = -1; + my $l0 = $table->[$self->{lvl} - 1]; + my $l1 = $table->[$self->{lvl}]; - if ($::CONN && (my $table = $::CONN->{exp_table})) { - my $l0 = $table->[$lvl - 1]; - my $l1 = $table->[$lvl]; + $self->{nxt} = $l1; - $self->{nxt} = $l1; + ($self->{exp} - $l0) / ($l1 - $l0) +} - $v = ($exp - $l0) / ($l1 - $l0); - } +sub set_value { + my ($self, $lvl, $exp) = @_; + + $self->{lvl} = $lvl; + $self->{exp} = $exp; - $self->SUPER::set_value ($v); + $self->SUPER::set_value ($self->_percent); } ############################################################################# @@ -3018,7 +3059,7 @@ package DC::UI::Slider; -use strict; +use common::sense; use DC::OpenGL; @@ -3100,7 +3141,7 @@ $self->SUPER::invoke_button_down ($ev, $x, $y); $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x]; - + $self->invoke_mouse_motion ($ev, $x, $y); 1 @@ -3114,7 +3155,7 @@ my (undef, $lo, $hi, $page) = @{$self->{range}}; - $x = ($x - $self->{click}[1]) / ($w * $self->{scale}); + $x = ($x - $self->{click}[1]) / ($w * $self->{scale} || 1e999); $self->set_value ($self->{click}[0] + $x * ($hi - $page - $lo)); } else { @@ -3170,7 +3211,7 @@ if ($self->{vertical}) { # draw a vertical slider like a rotated horizontal slider - + glTranslate 1, 0, 0; glRotate 90, 0, 0, 1; } @@ -3248,9 +3289,8 @@ indent => 0, #font => default_font @_, - + layout => (new DC::Layout), - par => [], max_par => 0, height => 0, children => [ @@ -3261,6 +3301,8 @@ $self->{children}[1]->connect (changed => sub { $self->update }); + $self->add_paragraph (@{ delete $self->{par} }) if @{ $self->{par} }; + $self } @@ -3533,7 +3575,7 @@ $self->{moveto} = [$self->{x}, $self->{y}, $x, $y]; $self->{speed} = 0.001; $self->{time} = 1; - + ::animation_start $self; } @@ -3547,7 +3589,7 @@ } my ($x0, $y0, $x1, $y1) = @{$self->{moveto}}; - + $self->{x} = $x0 * $self->{time} + $x1 * (1 - $self->{time}); $self->{y} = $y0 * $self->{time} + $y1 * (1 - $self->{time}); } @@ -3602,28 +3644,66 @@ ) } -sub set_tooltip_from { - my ($self, $widget) = @_; +# expand, as good as possible +sub _expand_doclets { + my ($tip) = @_; + + $tip =~ s{#\(([^)]+)\)}{ + if ($::CONN) { + exists $::CONN->{doclet}{$1} + ? $::CONN->{doclet}{$1} + : "(waiting for server to show full text)" + } else { + "(unable to show full text without server connection)" + } + }ge; - my $tip = $widget->{tooltip}; - $tip = $tip->($widget) if "CODE" eq ref $tip; - + $tip =~ s/^\n+//; + $tip =~ s/\n+$//; + + $tip +} + +# expands a tooltip, potentially multiple times remotely +# and returns a guard. clals the clalback each time the text changes. +sub expand_tooltip { + my ($tip, $cb) = @_; + + # first expand #name tooltips from local pod $tip = DC::Pod::section_label tooltip => $1 - if $tip =~ /^#(.*)$/; + if $tip =~ /^#([^(].*)$/; + + my $active; # true if any remote requests outstanding - if ($ENV{CFPLUS_DEBUG} & 2) { - $tip .= "\n\n" . (ref $widget) . "\n" - . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n" - . "req $widget->{req_w} $widget->{req_h}\n" - . "visible $widget->{visible}"; + if ($::CONN && $::CONN->{addme_success}) { + # now find all doclet references + for my $doclet ($tip =~ /#\(([^)]+)\)/g) { + unless (exists $::CONN->{doclet}{$doclet}) { + # need to ask the server + # we don't try to avoid duplicate requests + + $active = 1; + $::CONN->send_exti_req (doclet => (split /\//, $doclet, 2), sub { + $::CONN->{doclet}{$doclet} = DC::sanitise_cfxml $_[0]; + $cb->(_expand_doclets $tip) if $active; + }); + } + } } - $tip =~ s/^\n+//; - $tip =~ s/\n+$//; + $cb->(_expand_doclets $tip); + + $active and Guard::guard { undef $active } +} + +sub set_tooltip_from { + my ($self, $widget) = @_; + + my $tip = $widget->{tooltip}; + $tip = $tip->($widget) if "CODE" eq ref $tip; $self->add (new DC::UI::Label fg => $DC::THEME{tooltip_fg}, - markup => $tip, max_w => ($widget->{tooltip_width} || 0.25) * $::WIDTH, align => 0, fontsize => 0.8, @@ -3631,6 +3711,19 @@ ellipsise => 0, font => ($widget->{tooltip_font} || $::FONT_PROP), ); + + $self->{tooltip_expand} = expand_tooltip $tip, sub { + my ($tip) = @_; + + if ($ENV{CFPLUS_DEBUG} & 2) { + $tip .= "\n\n" . (ref $widget) . "\n" + . "$widget->{x} $widget->{y} $widget->{w} $widget->{h}\n" + . "req $widget->{req_w} $widget->{req_h}\n" + . "visible $widget->{visible}"; + } + + $self->{children}[0]->set_markup ($tip); + }; } sub size_request { @@ -3676,10 +3769,10 @@ glColor @{ $DC::THEME{tooltip_bg} }; glRect 0, 0, $w, $h; - + glColor @{ $DC::THEME{tooltip_border} }; glRect_lineloop .5, .5, $w + .5, $h + .5; - + glTranslate 2, 2; $self->SUPER::_draw; @@ -3704,28 +3797,8 @@ @_, ); - if ($self->{anim} && $self->{animspeed}) { - DC::weaken (my $widget = $self); - - $self->{animspeed} = List::Util::max 0.05, $self->{animspeed}; - $self->{timer} = EV::periodic_ns 0, $self->{animspeed}, undef, sub { - return unless $::CONN; - - my $w = $widget - or return; - - ++$w->{frame}; - $w->update_face; - - # somehow, $widget can go away - $w->update; - $w->update_timer; - }; + $self->update_anim; - $self->update_face; - $self->update_timer; - } - $self } @@ -3760,6 +3833,34 @@ } } +sub update_anim { + my ($self) = @_; + + if ($self->{anim} && $self->{animspeed}) { + DC::weaken (my $widget = $self); + + $self->{animspeed} = List::Util::max 0.05, $self->{animspeed}; + $self->{timer} = EV::periodic_ns 0, $self->{animspeed}, undef, sub { + return unless $::CONN; + + my $w = $widget + or return; + + ++$w->{frame}; + $w->update_face; + + # somehow, $widget can go away + $w->update; + $w->update_timer; + }; + + $self->update_face; + $self->update_timer; + } else { + delete $self->{timer}; + } +} + sub size_request { my ($self) = @_; @@ -3788,6 +3889,27 @@ $self->SUPER::update; } +sub set_face { + my ($self, $face) = @_; + + $self->{face} = $face; + $self->reconfigure; +} + +sub set_anim { + my ($self, $anim) = @_; + + $self->{anim} = $anim; + $self->update_anim; +} + +sub set_animspeed { + my ($self, $animspeed) = @_; + + $self->{animspeed} = $animspeed; + $self->update_anim; +} + sub invoke_visibility_change { my ($self) = @_; @@ -3893,7 +4015,15 @@ $self->{button} = $ev->{button}; $self->show; - $self->move_abs ($ev->{x} - $self->{w} * 0.5, $ev->{y} - $self->{h} * 0.5); + + my $x = $ev->{x}; + my $y = $ev->{y}; + + $self->{root}->on_post_alloc ($self => sub { + $self->move_abs ($x - $self->{w} * 0.25, $y - $self->{border} * $::FONTSIZE * .5); + }); + + 1 # so it can be used inside event handlers } sub invoke_mouse_motion { @@ -3935,8 +4065,7 @@ @_, ); - $self->{current} = $self->{children}[0] - if @{ $self->{children} }; + $self->set_current_page (0); $self } @@ -3946,8 +4075,8 @@ $self->SUPER::add (@widgets); - $self->{current} = $self->{children}[0] - if @{ $self->{children} }; + $self->set_current_page (0) + if @widgets == @{ $self->{children} }; } sub get_current_page { @@ -3963,28 +4092,35 @@ ? $page_or_widget : $self->{children}[$page_or_widget]; - $self->{current} = $widget; - $self->{current}->configure (0, 0, $self->{w}, $self->{h}); + $self->{current}->set_invisible if $self->{current} && $self->{visible}; - $self->emit (page_changed => $self->{current}); + if (($self->{current} = $widget)) { + $self->{current}->set_visible if $self->{current} && $self->{visible}; + $self->{current}->configure (0, 0, $self->{w}, $self->{h}); + + $self->emit (page_changed => $self->{current}); + } $self->realloc; } sub visible_children { - $_[0]{current} + $_[0]{current} || () } sub size_request { my ($self) = @_; - $self->{current}->size_request + $self->{current} + ? $self->{current}->size_request + : (0, 0) } sub invoke_size_allocate { my ($self, $w, $h) = @_; - $self->{current}->configure (0, 0, $w, $h); + $self->{current}->configure (0, 0, $w, $h) + if $self->{current}; 1 } @@ -3992,7 +4128,8 @@ sub _draw { my ($self) = @_; - $self->{current}->draw; + $self->{current}->draw + if $self->{current}; } ############################################################################# @@ -4212,7 +4349,7 @@ sub reorder { my ($self) = @_; - my $NOW = Time::HiRes::time; + my $NOW = AE::time; # freeze display when hovering over any label return if $DC::UI::TOOLTIP->{owner} @@ -4273,6 +4410,10 @@ push @widgets, $label; } + my $hash = join ",", @widgets; + return if $hash eq $self->{last_widget_hash}; + $self->{last_widget_hash} = $hash; + $self->clear; $self->SUPER::add (reverse @widgets); } @@ -4317,6 +4458,16 @@ }); } +sub clr_group { + my ($self, $group) = @_; + + if (delete $self->{item}{$group}) { + $ROOT->on_refresh (reorder => sub { + $self->reorder; + }); + } +} + sub reconfigure { my ($self) = @_; @@ -4480,7 +4631,7 @@ $widget = pop @{ $queue[-1] || [] } and last; - + pop @queue; } @@ -4586,4 +4737,3 @@ $TOOLTIP = new DC::UI::Tooltip z => 900; 1 -