--- deliantra/Deliantra-Client/DC/UI.pm 2007/07/20 18:30:03 1.388 +++ deliantra/Deliantra-Client/DC/UI.pm 2007/08/21 02:41:52 1.431 @@ -275,7 +275,7 @@ } sub TO_JSON { - { __widget_ref__ => $_[0]{s_id} } + { __w_ => $_[0]{s_id} } } sub show { @@ -595,52 +595,8 @@ # functions seems pointless. our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn -sub draw { - my ($self) = @_; - - return unless $self->{h} && $self->{w}; - - # update screen rectangle - local $draw_x = $draw_x + $self->{x}; - local $draw_y = $draw_y + $self->{y}; - - # skip widgets that are entirely outside the drawing area - return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w) - || ($draw_y + $self->{h} < 0) || ($draw_y >= $draw_h); - - glPushMatrix; - glTranslate $self->{x}, $self->{y}, 0; - - if ($self == $HOVER && $self->{can_hover}) { - glColor 1*0.2, 0.8*0.2, 0.5*0.2, 0.2; - glEnable GL_BLEND; - glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; - glBegin GL_QUADS; - glVertex 0 , 0; - glVertex $self->{w}, 0; - glVertex $self->{w}, $self->{h}; - glVertex 0 , $self->{h}; - glEnd; - glDisable GL_BLEND; - } - - if ($ENV{CFPLUS_DEBUG} & 1) { - glPushMatrix; - glColor 1, 1, 0, 1; - glTranslate 0.375, 0.375; - glBegin GL_LINE_LOOP; - glVertex 0 , 0; - glVertex $self->{w} - 1, 0; - glVertex $self->{w} - 1, $self->{h} - 1; - glVertex 0 , $self->{h} - 1; - glEnd; - glPopMatrix; - #CFPlus::UI::Label->new (w => $self->{w}, h => $self->{h}, text => $self, fontsize => 0)->_draw; - } - - $self->_draw; - glPopMatrix; -} +#sub draw { +#CFPlus.xs sub _draw { my ($self) = @_; @@ -648,12 +604,12 @@ warn "no draw defined for $self\n"; } -my $cntx;#d# sub DESTROY { my ($self) = @_; return if CFPlus::in_destruct; + local $@; eval { $self->destroy }; warn "exception during widget destruction: $@" if $@ & $@ != /during global destruction/; @@ -672,8 +628,6 @@ sub new { my $class = shift; - # range [value, low, high, page] - $class->SUPER::new ( #bg => [0, 0, 0, 0.2], #active_bg => [1, 1, 1, 0.5], @@ -694,14 +648,7 @@ glEnable GL_BLEND; glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; glColor_premultiply @$color; - - glBegin GL_QUADS; - glVertex 0 , 0; - glVertex 0 , $h; - glVertex $w, $h; - glVertex $w, 0; - glEnd; - + glRect 0, 0, $w, $h; glDisable GL_BLEND; } } @@ -743,7 +690,7 @@ ); $self->add (@$children) - if $children; + if $children && @$children; $self } @@ -762,6 +709,7 @@ $_->set_parent ($self) for @widgets; + # TODO: only do this in widgets that need it, e.g. root, fixed use sort 'stable'; $self->{children} = [ @@ -770,6 +718,10 @@ ]; $self->realloc; + + $self->emit (c_add => \@widgets); + + map $_+0, @widgets } sub children { @@ -777,12 +729,15 @@ } sub remove { - my ($self, $child) = @_; + my ($self, @widgets) = @_; - delete $child->{parent}; - $child->hide; + $self->emit (c_remove => \@widgets); - $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; + for my $child (@widgets) { + delete $child->{parent}; + $child->hide; + $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; + } $self->realloc; } @@ -790,7 +745,7 @@ sub clear { my ($self) = @_; - my $children = delete $self->{children}; + my $children = $self->{children}; $self->{children} = []; for (@$children) { @@ -820,7 +775,7 @@ sub _draw { my ($self) = @_; - $_->draw for @{$self->{children}}; + $_->draw for $self->visible_children; } ############################################################################# @@ -840,7 +795,7 @@ sub add { my ($self, $child) = @_; - $self->SUPER::remove ($_) for @{ $self->{children} }; + $self->clear; $self->SUPER::add ($child); } @@ -960,8 +915,8 @@ my ($w, $h) = @{$self->child}{qw(req_w req_h)}; - $w = 10 if $self->{scroll_x}; - $h = 10 if $self->{scroll_y}; + $w = 1 if $self->{scroll_x}; + $h = 1 if $self->{scroll_y}; ($w, $h) } @@ -995,6 +950,24 @@ } } +sub set_center { + my ($self, $x, $y) = @_; + + $self->set_offset ($x - $self->{w} * .5, $y - $self->{h} * .5); +} + +sub make_visible { + my ($self, $x, $y, $border) = @_; + + if ( $x < $self->{view_x} + $self->{w} * $border + || $x > $self->{view_x} + $self->{w} * (1 - $border) + || $y < $self->{view_y} + $self->{h} * $border + || $y > $self->{view_y} + $self->{h} * (1 - $border) + ) { + $self->set_center ($x, $y); + } +} + # hmm, this does not work for topleft of $self... but we should not ask for that sub coord2local { my ($self, $x, $y) = @_; @@ -1048,6 +1021,8 @@ my $self; my $hslider = new CFPlus::UI::Slider + c_col => 0, + c_row => 1, vertical => 0, range => [0, 0, 1, 0.01], # HACK fix on_changed => sub { @@ -1057,6 +1032,8 @@ ; my $vslider = new CFPlus::UI::Slider + c_col => 1, + c_row => 0, vertical => 1, range => [0, 0, 1, 0.01], # HACK fix on_changed => sub { @@ -1077,6 +1054,8 @@ ); $self->{vp} = new CFPlus::UI::ViewPort + c_col => 0, + c_row => 0, expand => 1, scroll_x => $self->{scroll_x}, scroll_y => $self->{scroll_y}, @@ -1088,62 +1067,83 @@ 0 }, + on_size_allocate => sub { + my ($vp, $w, $h) = @_; + $vp->{parent}->update_slider; + 0 + }, ; - $self->SUPER::add_at (0, 0, $self->{vp}); + $self->SUPER::add ($self->{vp}); $self->add ($child) if $child; $self } -#TODO# update range on size_allocate depending on child - sub add { my ($self, $widget) = @_; $self->{vp}->add ($self->{child} = $widget); } +sub set_offset { shift->{vp}->set_offset (@_) } +sub set_center { shift->{vp}->set_center (@_) } +sub make_visible { shift->{vp}->make_visible (@_) } + sub update_slider { my ($self) = @_; my $child = ($self->{vp} or return)->child; - my ($w1, $w2) = ($child->{w}, $self->{vp}{w}); - $self->{hslider}->set_range ([$self->{hslider}{range}[0], 0, $w1, $w2, 1]); + if ($self->{scroll_x}) { + my ($w1, $w2) = ($child->{req_w}, $self->{vp}{w}); + $self->{hslider}->set_range ([$self->{hslider}{range}[0], 0, $w1, $w2, 1]); + + my $visible = $w1 > $w2; + if ($visible != $self->{hslider_visible}) { + $self->{hslider_visible} = $visible; + $visible ? $self->SUPER::add ($self->{hslider}) + : $self->SUPER::remove ($self->{hslider}); + } + } - my $visible = $w1 > $w2; - if ($visible != $self->{hslider}{visible}) { - $visible ? $self->SUPER::add_at (0, 1, $self->{hslider}) - : $self->{hslider}->hide; - } - - my ($h1, $h2) = ($child->{h}, $self->{vp}{h}); - $self->{vslider}->set_range ([$self->{vslider}{range}[0], 0, $h1, $h2, 1]); - - my $visible = $h1 > $h2; - if ($visible != $self->{vslider}{visible}) { - $visible ? $self->SUPER::add_at (1, 0, $self->{vslider}) - : $self->{vslider}->hide; + if ($self->{scroll_y}) { + my ($h1, $h2) = ($child->{req_h}, $self->{vp}{h}); + $self->{vslider}->set_range ([$self->{vslider}{range}[0], 0, $h1, $h2, 1]); + + my $visible = $h1 > $h2; + if ($visible != $self->{vslider_visible}) { + $self->{vslider_visible} = $visible; + $visible ? $self->SUPER::add ($self->{vslider}) + : $self->SUPER::remove ($self->{vslider}); + } } } -sub update { - my ($self) = @_; +sub start_dragging { + my ($self, $ev) = @_; - $self->update_slider; - $self->SUPER::update; + $self->grab_focus; + + my $ox = $self->{vp}{view_x}; + my $oy = $self->{vp}{view_y}; + + $self->{motion} = sub { + my ($ev, $x, $y) = @_; + + $ox -= $ev->{xrel}; + $oy -= $ev->{yrel}; + + $self->{vp}->set_offset ($ox, $oy); + }; } sub invoke_mouse_wheel { my ($self, $ev) = @_; - $self->{vslider}->emit (mouse_wheel => $ev) - if $ev->{dy}; - - $self->{hslider}->emit (mouse_wheel => $ev) - if $ev->{dx}; + $self->{vslider}->emit (mouse_wheel => $ev) if $self->{vslider_visible}; + $self->{hslider}->emit (mouse_wheel => $ev) if $self->{hslider_visible}; 1 } @@ -1152,18 +1152,7 @@ my ($self, $ev, $x, $y) = @_; if ($ev->{button} == 2) { - $self->grab_focus; - - my $ox = $self->{vp}{view_x} + $ev->{x}; - my $oy = $self->{vp}{view_y} + $ev->{y}; - - $self->{motion} = sub { - my ($ev, $x, $y) = @_; - - $self->{vp}->set_offset ($ox - $ev->{x}, $oy - $ev->{y}); - $self->update; - }; - + $self->start_dragging ($ev); return 1; } @@ -1224,14 +1213,7 @@ glEnable GL_BLEND; glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; glColor_premultiply @{ $self->{bg} }; - - glBegin GL_QUADS; - glVertex 0 , 0; - glVertex 0 , $h; - glVertex $w, $h; - glVertex $w, 0; - glEnd; - + glRect 0, 0, $w, $h; glDisable GL_BLEND; } @@ -1583,7 +1565,7 @@ package CFPlus::UI::Table; -our @ISA = CFPlus::UI::Base::; +our @ISA = CFPlus::UI::Container::; use List::Util qw(max sum); @@ -1593,81 +1575,66 @@ my $class = shift; $class->SUPER::new ( - children => [], col_expand => [], row_expand => [], @_, ) } -sub children { - grep $_, map @$_, grep $_, @{ $_[0]{children} } -} - -# TODO: store row/col info in child widget and use standard add/del sub add { - my $self = shift; + my ($self, @widgets) = @_; + + for my $child (@widgets) { + $child->{c_rowspan} ||= 1; + $child->{c_colspan} ||= 1; + } - Carp::cluck "please use the add_at method instead of calling add, thank you.\n";#d# - $self->add_at (@_); + $self->SUPER::add (@widgets); } sub add_at { my $self = shift; + my @widgets; + while (@_) { my ($col, $row, $child) = splice @_, 0, 3, (); - $child->set_parent ($self); - $self->{children}[$row][$col] = $child; - } - - $self->{force_realloc} = 1; - $self->{force_size_alloc} = 1; - $self->realloc; -} - -sub remove { - my ($self, $child) = @_; + $child->{c_row} = $row; + $child->{c_col} = $col; - for (@{ $self->{children} }) { - for (@{ $_ || [] }) { - $_ = undef if $_ == $child; - } + push @widgets, $child; } + + $self->add (@widgets); } -# TODO: move to container class maybe? send children a signal on removal? -sub clear { +sub get_wh { my ($self) = @_; + my (@w, @h); + my @children = $self->children; - delete $self->{children}; - - for (@children) { - delete $_->{parent}; - $_->hide; - } - $self->realloc; -} + # first pass, columns + for my $widget (sort { $a->{c_colspan} <=> $b->{c_colspan} } @children) { + my ($c, $w, $cs) = @$widget{qw(c_col req_w c_colspan)}; -sub get_wh { - my ($self) = @_; + my $sw = sum @w[$c .. $c + $cs - 1]; - my (@w, @h); + if ($w > $sw) { + $_ += ($w - $sw) / ($sw ? $sw / $_ : $cs) for @w[$c .. $c + $cs - 1]; + } + } + + # second pass, rows + for my $widget (sort { $a->{c_rowspan} <=> $b->{c_rowspan} } @children) { + my ($r, $h, $rs) = @$widget{qw(c_row req_h c_rowspan)}; - for my $y (0 .. $#{$self->{children}}) { - my $row = $self->{children}[$y] - or next; - - for my $x (0 .. $#$row) { - my $widget = $row->[$x] - or next; - my ($w, $h) = @$widget{qw(req_w req_h)}; + my $sh = sum @h[$r .. $r + $rs - 1]; - $w[$x] = max $w[$x], $w; - $h[$y] = max $h[$y], $h; + if ($h > $sh) { + $_ += ($h - $sh) / ($sh ? $sh / $_ : $rs) for @h[$r .. $r + $rs - 1]; } } @@ -1693,7 +1660,7 @@ my $req_w = (sum @$ws) || 1; my $req_h = (sum @$hs) || 1; - # TODO: nicer code + # now linearly scale the rows/columns to the allocated size my @col_expand = @{$self->{col_expand}}; @col_expand = (1) x @$ws unless @col_expand; my $col_expand = (sum @col_expand) || 1; @@ -1710,55 +1677,21 @@ CFPlus::UI::harmonize $hs; - my $y; + my @x; for (0 .. $#$ws) { $x[$_ + 1] = $x[$_] + $ws->[$_] } + my @y; for (0 .. $#$hs) { $y[$_ + 1] = $y[$_] + $hs->[$_] } - for my $r (0 .. $#{$self->{children}}) { - my $row = $self->{children}[$r] - or next; + for my $widget ($self->children) { + my ($r, $c, $w, $h, $rs, $cs) = @$widget{qw(c_row c_col req_w req_h c_rowspan c_colspan)}; - my $x = 0; - my $row_h = $hs->[$r]; - - for my $c (0 .. $#$row) { - my $col_w = $ws->[$c]; - - if (my $widget = $row->[$c]) { - $widget->configure ($x, $y, $col_w, $row_h); - } - - $x += $col_w; - } - - $y += $row_h; + $widget->configure ( + $x[$c], $y[$r], + $x[$c + $cs] - $x[$c], $y[$r + $rs] - $y[$r], + ); } 1 } -sub find_widget { - my ($self, $x, $y) = @_; - - $x -= $self->{x}; - $y -= $self->{y}; - - my $res; - - for (grep $_, map @$_, grep $_, @{ $self->{children} }) { - $res = $_->find_widget ($x, $y) - and return $res; - } - - $self->SUPER::find_widget ($x + $self->{x}, $y + $self->{y}) -} - -sub _draw { - my ($self) = @_; - - for (grep $_, @{$self->{children}}) { - $_->draw for grep $_, @$_; - } -} - ############################################################################# package CFPlus::UI::Fixed; @@ -1767,19 +1700,10 @@ our @ISA = CFPlus::UI::Container::; -sub add { - my ($self, $child, $posmode, $x, $y, $sizemode, $w, $h) = @_; - - $child->{_fixed} = [$posmode, $x, $y, $sizemode, $w, $h]; - $self->SUPER::add ($child); -} - sub _scale($$$) { - my ($mode, $val, $max) = @_; + my ($rel, $val, $max) = @_; - $mode eq "abs" ? $val - : $mode eq "rel" ? $val * $max - : 0 + $rel ? $val * $max : $val } sub size_request { @@ -1789,14 +1713,12 @@ # determine overall size by querying abs widgets for my $child ($self->visible_children) { - my ($pos, $x, $y, $size, $w, $h) = @{ $child->{_fixed} }; - - if ($pos eq "abs") { - $w = _scale $size, $w, $child->{req_w}; - $h = _scale $size, $h, $child->{req_h}; + unless ($child->{c_rel}) { + my $x = $child->{c_x}; + my $y = $child->{c_y}; - $x1 = min $x1, $x; $x2 = max $x2, $x + $w; - $y1 = min $y1, $y; $y2 = max $y2, $y + $h; + $x1 = min $x1, $x; $x2 = max $x2, $x + $child->{req_w}; + $y1 = min $y1, $y; $y2 = max $y2, $y + $child->{req_h}; } } @@ -1805,16 +1727,12 @@ # now layout remaining widgets for my $child ($self->visible_children) { - my ($pos, $x, $y, $size, $w, $h) = @{ $child->{_fixed} }; - - if ($pos ne "abs") { - $x = _scale $pos, $x, $W; - $y = _scale $pos, $x, $H; - $w = _scale $size, $w, $child->{req_w}; - $h = _scale $size, $h, $child->{req_h}; + if ($child->{c_rel}) { + my $x = _scale $child->{c_rel}, $child->{c_x}, $W; + my $y = _scale $child->{c_rel}, $child->{c_y}, $H; - $x1 = min $x1, $x; $x2 = max $x2, $x + $w; - $y1 = min $y1, $y; $y2 = max $y2, $y + $h; + $x1 = min $x1, $x; $x2 = max $x2, $x + $child->{req_w}; + $y1 = min $y1, $y; $y2 = max $y2, $y + $child->{req_h}; } } @@ -1828,14 +1746,13 @@ my ($self, $W, $H) = @_; for my $child ($self->visible_children) { - my ($pos, $x, $y, $size, $w, $h) = @{ $child->{_fixed} }; + my $x = _scale $child->{c_rel}, $child->{c_x}, $W; + my $y = _scale $child->{c_rel}, $child->{c_y}, $H; - $x = _scale $pos, $x, $W; - $y = _scale $pos, $x, $H; - $w = _scale $size, $w, $child->{req_w}; - $h = _scale $size, $h, $child->{req_h}; + $x += $child->{c_halign} * $child->{req_w}; + $y += $child->{c_valign} * $child->{req_h}; - $child->configure ($x, $y, $w, $h); + $child->configure (int $x, int $y, $child->{req_w}, $child->{req_h}); } 1 @@ -1850,14 +1767,16 @@ sub size_request { my ($self) = @_; + my @children = $self->visible_children; + $self->{vertical} ? ( - (List::Util::max map $_->{req_w}, @{$self->{children}}), - (List::Util::sum map $_->{req_h}, @{$self->{children}}), + (List::Util::max map $_->{req_w}, @children), + (List::Util::sum map $_->{req_h}, @children), ) : ( - (List::Util::sum map $_->{req_w}, @{$self->{children}}), - (List::Util::max map $_->{req_h}, @{$self->{children}}), + (List::Util::sum map $_->{req_w}, @children), + (List::Util::max map $_->{req_h}, @children), ) } @@ -1991,6 +1910,12 @@ $self->SUPER::realloc; } +sub clear { + my ($self) = @_; + + $self->set_text (""); +} + sub set_text { my ($self, $text) = @_; @@ -2106,13 +2031,29 @@ $self->{oy} = int ($self->{valign} < 0 ? $self->{padding_y} : $self->{valign} > 0 ? $self->{h} - $size->[1] - $self->{padding_y} : ($self->{h} - $size->[1]) * 0.5); - }; - my $w = List::Util::min $self->{w} + 4, $size->[0]; - my $h = List::Util::min $self->{h} + 2, $size->[1]; + $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style}); + }; - $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style}); -} +# unless ($self->{list}) { +# $self->{list} = CFPlus::OpenGL::glGenList; +# CFPlus::OpenGL::glNewList $self->{list}; +# $self->{layout}->render ($self->{ox}, $self->{oy}, $self->{style}); +# CFPlus::OpenGL::glEndList; +# } +# +# CFPlus::OpenGL::glCallList $self->{list}; + + $self->{layout}->draw; +} + +#sub destroy { +# my ($self) = @_; +# +# CFPlus::OpenGL::glDeleteList delete $self->{list} if $self->{list}; +# +# $self->SUPER::destroy; +#} ############################################################################# @@ -2128,8 +2069,10 @@ $class->SUPER::new ( fg => [1, 1, 1], bg => [0, 0, 0, 0.2], - active_bg => [1, 1, 1, 0.5], - active_fg => [0, 0, 0], + outline => [0.6, 0.3, 0.1], + active_bg => [0, 0, 1, .2], + active_fg => [1, 1, 1], + active_outline => [1, 1, 0], can_hover => 1, can_focus => 1, valign => 0, @@ -2279,12 +2222,7 @@ glEnable GL_BLEND; glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; - glBegin GL_QUADS; - glVertex 0 , 0; - glVertex 0 , $self->{h}; - glVertex $self->{w}, $self->{h}; - glVertex $self->{w}, 0; - glEnd; + glRect 0, 0, $self->{w}, $self->{h}; glDisable GL_BLEND; $self->SUPER::_draw; @@ -2296,13 +2234,29 @@ my $text = substr $self->{text}, 0, $self->{cursor}; utf8::encode $text; - @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text) + @$self{qw(cur_x cur_y cur_h)} = $self->{layout}->cursor_pos (length $text); } + glColor_premultiply @{$self->{active_fg}}; glBegin GL_LINES; glVertex 0.5 + $self->{cur_x} + $self->{ox}, $self->{cur_y} + $self->{oy}; glVertex 0.5 + $self->{cur_x} + $self->{ox}, $self->{cur_y} + $self->{oy} + $self->{cur_h}; glEnd; + + glLineWidth 3; + glColor @{$self->{active_outline}}; + glRect_lineloop 0, 0, $self->{w} - 1, $self->{h} - 1; + glLineWidth 1; + + } else { + glColor @{$self->{outline}}; + glTranslate .375, .375; + glBegin GL_LINE_STRIP; + glVertex 0, $self->{h} * .5; + glVertex 0, $self->{h} - 3; + glVertex $self->{w} - 1, $self->{h} - 3; + glVertex $self->{w} - 1, $self->{h} * .5; + glEnd; } } @@ -2398,6 +2352,55 @@ ############################################################################# +package CFPlus::UI::ButtonBin; + +our @ISA = CFPlus::UI::Bin::; + +use CFPlus::OpenGL; + +my @tex = + map { new_from_file CFPlus::Texture CFPlus::find_rcfile $_, mipmap => 1 } + qw(b1_button_inactive.png b1_button_active.png); + +sub new { + my $class = shift; + + $class->SUPER::new ( + can_hover => 1, + align => 0, + valign => 0, + can_events => 1, + @_ + ) +} + +sub invoke_button_up { + my ($self, $ev, $x, $y) = @_; + + $self->emit ("activate") + if $x >= 0 && $x < $self->{w} + && $y >= 0 && $y < $self->{h}; + + 1 +} + +sub _draw { + my ($self) = @_; + + glEnable GL_TEXTURE_2D; + glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; + glColor 0, 0, 0, 1; + + my $tex = $tex[$GRAB == $self]; + $tex->draw_quad_alpha (0, 0, $self->{w}, $self->{h}); + + glDisable GL_TEXTURE_2D; + + $self->SUPER::_draw; +} + +############################################################################# + package CFPlus::UI::Button; our @ISA = CFPlus::UI::Label::; @@ -2541,6 +2544,7 @@ my $self = $class->SUPER::new ( can_events => 0, + scale => 1, @_, ); @@ -2575,7 +2579,7 @@ sub size_request { my ($self) = @_; - ($self->{tex}{w}, $self->{tex}{h}) + (int $self->{tex}{w} * $self->{scale}, int $self->{tex}{h} * $self->{scale}) } sub _draw { @@ -2595,7 +2599,7 @@ glEnable GL_TEXTURE_2D; glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; - $tex->draw_quad (0, 0, $w, $h); + $tex->draw_quad_alpha (0, 0, $w, $h); glDisable GL_TEXTURE_2D; } @@ -2626,6 +2630,12 @@ ); } +sub invoke_button_down { + my ($self, $ev, $x, $y) = @_; + + 1 +} + sub invoke_button_up { my ($self, $ev, $x, $y) = @_; @@ -2773,6 +2783,119 @@ ############################################################################# +package CFPlus::UI::Progress; + +our @ISA = CFPlus::UI::Label::; + +use CFPlus::OpenGL; + +sub new { + my ($class, %arg) = @_; + + my $self = $class->SUPER::new ( + fg => [1, 1, 1], + bg => [0, 0, 1, 0.2], + bar => [0.7, 0.5, 0.1, 0.8], + outline => [0.4, 0.3, 0], + fontsize => 0.9, + valign => 0, + align => 0, + can_events => 1, + ellipsise => 1, + %arg, + ); + + $self->set_value ($arg{value} || -1); + + $self +} + +sub set_value { + my ($self, $value) = @_; + + if ($self->{value} != $value) { + $self->{value} = $value; + + if ($value < 0) { + $self->set_text ("-"); + } else { + $self->set_text (sprintf "%d%%", $value * 100); + } + $self->update; + } +} + +sub _draw { + my ($self) = @_; + + glEnable GL_BLEND; + glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA; + + if ($self->{value} >= 0) { + my $s = 3 + ($self->{w} - 6) * $self->{value}; + + glColor_premultiply @{$self->{bar}}; + glRect 2, 2, $s, $self->{h} - 2; + glColor_premultiply @{$self->{bg}}; + glRect $s + 1, 0, $self->{w} - 2, $self->{h} - 2; + } + + glColor_premultiply @{$self->{outline}}; + glRect_lineloop 1, 1, $self->{w} - 2, $self->{h} - 2; + + glDisable GL_BLEND; + + { + local $self->{bg}; # do not draw background + $self->SUPER::_draw; + } +} + +############################################################################# + +package CFPlus::UI::ExperienceProgress; + +our @ISA = CFPlus::UI::Progress::; + +sub new { + my ($class, %arg) = @_; + + my $self = $class->SUPER::new ( + %arg + ); + + $::CONN->{on_exp_update}{$self+0} = sub { $self->set_value ($self->{value}) } + if $::CONN; + + $self +} + +sub DESTROY { + my ($self) = @_; + + delete $::CONN->{on_exp_update}{$self+0} + if $::CONN; + + $self->SUPER::DESTROY; +} + +sub set_value { + my ($self, $lvl, $exp) = @_; + + my $v = -1; + + if ($::CONN && (my $table = $::CONN->{exp_table})) { + my $l0 = $table->[$lvl - 1]; + my $l1 = $table->[$lvl]; + + $v = ($exp - $l0) / ($l1 - $l0); + } + + $self->SUPER::set_value ($v); +} + +############################################################################# + package CFPlus::UI::Gauge; our @ISA = CFPlus::UI::VBox::; @@ -2907,7 +3030,9 @@ $self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x]; - $self->invoke_mouse_motion ($ev, $x, $y) + $self->invoke_mouse_motion ($ev, $x, $y); + + 1 } sub invoke_mouse_motion { @@ -2937,7 +3062,7 @@ $self->set_value ($self->{range}[0] + $delta * $self->{range}[3] * $pagepart); - ! ! $delta + 1 } sub update { @@ -3078,7 +3203,7 @@ sub size_request { my ($self) = @_; - my ($empty, $slider) = @{ $self->{children} }; + my ($empty, $slider) = $self->visible_children; local $self->{children} = [$empty, $slider]; $self->SUPER::size_request @@ -3277,6 +3402,7 @@ my $layout = $self->get_layout ($para); $layout->render ($para->{indent}, $y - $y0); + $layout->draw; if (my @w = @{ $para->{widget} }) { my @s = $layout->get_shapes; @@ -3477,20 +3603,10 @@ my ($w, $h) = @$self{qw(w h)}; glColor 1, 0.8, 0.4; - glBegin GL_QUADS; - glVertex 0 , 0; - glVertex 0 , $h; - glVertex $w, $h; - glVertex $w, 0; - glEnd; + glRect 0, 0, $w, $h; glColor 0, 0, 0; - glBegin GL_LINE_LOOP; - glVertex 0 , 0; - glVertex 0 , $h; - glVertex $w, $h; - glVertex $w, 0; - glEnd; + glRect_lineloop 0, 0, $w, $h; glTranslate 2 - 0.375, 2 - 0.375; @@ -3520,17 +3636,21 @@ CFPlus::weaken (my $widget = $self); $widget->{animspeed} = List::Util::max 0.05, $widget->{animspeed}; - $widget->{anim_start} = $self->{animspeed} * Event::time / $self->{animspeed}; + $widget->{anim_start} = $self->{animspeed} * int Event::time / $self->{animspeed}; $self->{timer} = Event->timer ( parked => 1, cb => sub { - return unless $::CONN && $widget; + return unless $::CONN; - ++$widget->{frame}; - $widget->update_face; - $widget->update; + my $w = $widget + or return; - $widget->update_timer; + ++$w->{frame}; + $w->update_face; + + # somehow, $widget can go away + $w->update; + $w->update_timer; }, ); @@ -3561,12 +3681,18 @@ sub update_face { my ($self) = @_; - return unless $::CONN; - - if (my $anim = $::CONN->{anim}[$self->{anim}]) { - if ($anim && @$anim) { - delete $self->{wait_face}; - $self->{face} = $anim->[ $self->{frame} % @$anim ]; + if ($::CONN) { + if (my $anim = $::CONN->{anim}[$self->{anim}]) { + if ($anim && @$anim) { + $self->{face} = $anim->[ $self->{frame} % @$anim ]; + delete $self->{face_change_cb}; + + if (my $tex = $self->{tex} = $::CONN->{texture}[ $::CONN->{face}[$self->{face}]{id} ]) { + unless ($tex->{name} || $tex->{loading}) { + $tex->upload (sub { $self->reconfigure }); + } + } + } } } } @@ -3575,14 +3701,16 @@ my ($self) = @_; if ($::CONN) { - if (my $faceid = $::CONN->{faceid}[$self->{face}]) { - if (my $tex = $::CONN->{texture}[$faceid]) { - return ($self->{size_w} || $tex->{w}, $self->{size_h} || $tex->{h}); - } else { - $self->{wait_face} ||= $::CONN->connect_face_update ($faceid, sub { - $self->realloc; - }); + if (my $faceid = $::CONN->{face}[$self->{face}]{id}) { + if (my $tex = $self->{tex} = $::CONN->{texture}[$faceid]) { + if ($tex->{name}) { + return ($self->{size_w} || $tex->{w}, $self->{size_h} || $tex->{h}); + } elsif (!$tex->{loading}) { + $tex->upload (sub { $self->reconfigure }); + } } + + $self->{face_change_cb} ||= $::CONN->on_face_change ($self->{face}, sub { $self->reconfigure }); } } @@ -3608,16 +3736,9 @@ sub _draw { my ($self) = @_; - return unless $::CONN; - $self->SUPER::_draw; - my $faceid = $::CONN->{faceid}[$self->{face}] - or return; - - my $tex = $::CONN->{texture}[$faceid]; - - if ($tex) { + if (my $tex = $self->{tex}) { glEnable GL_TEXTURE_2D; glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE; glColor 0, 0, 0, 1; @@ -3679,7 +3800,7 @@ (new CFPlus::UI::Label markup => $right, align => +1), ], ; - + } else { $widget = new CFPlus::UI::Label can_hover => 1, @@ -3825,25 +3946,71 @@ # filter => # will be put between multiplexer and $self @_, ); - + $self->{filter}->add ($self->{multiplexer}) if $self->{filter}; $self->SUPER::add ($self->{buttonbar}, $self->{filter} || $self->{multiplexer}); + { + Scalar::Util::weaken (my $wself = $self); + + $self->{multiplexer}->connect (c_add => sub { + my ($mplex, $widgets) = @_; + + for my $child (@$widgets) { + Scalar::Util::weaken $child; + $child->{c_tab_} ||= do { + my $tab = + (UNIVERSAL::isa $child->{c_tab}, "CFPlus::UI::Base") + ? $child->{c_tab} + : new CFPlus::UI::Button markup => $child->{c_tab}[0], tooltip => $child->{c_tab}[1]; + + $tab->connect (activate => sub { + $wself->set_current_page ($child); + }); + + $tab + }; + + $self->{buttonbar}->add ($child->{c_tab_}); + } + }); + + $self->{multiplexer}->connect (c_remove => sub { + my ($mplex, $widgets) = @_; + + for my $child (@$widgets) { + $wself->{buttonbar}->remove ($child->{c_tab_}); + } + }); + } + $self } sub add { - my ($self, $title, $widget, $tooltip) = @_; + my ($self, @widgets) = @_; - CFPlus::weaken $self; + $self->{multiplexer}->add (@widgets) +} - $self->{buttonbar}->add (new CFPlus::UI::Button - markup => $title, - tooltip => $tooltip, - on_activate => sub { $self->set_current_page ($widget) }, - ); +sub remove { + my ($self, @widgets) = @_; - $self->{multiplexer}->add ($widget); + $self->{multiplexer}->remove (@widgets) +} + +sub pages { + my ($self) = @_; + $self->{multiplexer}->children +} + +sub add_tab { + my ($self, $title, $widget, $tooltip) = @_; + + $title = [$title, $tooltip] unless ref $title; + $widget->{c_tab} = $title; + + $self->add ($widget); } sub get_current_page { @@ -4136,7 +4303,7 @@ sub update { my ($self) = @_; - $::WANT_REFRESH++; + $::WANT_REFRESH->start; } sub add { @@ -4183,7 +4350,7 @@ for values %{delete $self->{refresh_hook}}; } - if ($self->{realloc}) { + while ($self->{realloc}) { my %queue; my @queue; my $widget; @@ -4240,40 +4407,37 @@ delete $self->{realloc}{$widget+0}; } - } - - while (my $size_alloc = delete $self->{size_alloc}) { - my @queue = sort { $b->{visible} <=> $a->{visible} } - values %$size_alloc; - while () { - my $widget = pop @queue || last; + while (my $size_alloc = delete $self->{size_alloc}) { + my @queue = sort { $a->{visible} <=> $b->{visible} } + values %$size_alloc; - my ($w, $h) = @$widget{qw(alloc_w alloc_h)}; + while () { + my $widget = pop @queue || last; - $w = 0 if $w < 0; - $h = 0 if $h < 0; + my ($w, $h) = @$widget{qw(alloc_w alloc_h)}; - $w = max $widget->{min_w}, $w; - $h = max $widget->{min_h}, $h; + $w = max $widget->{min_w}, $w; + $h = max $widget->{min_h}, $h; # $w = min $self->{w} - $widget->{x}, $w if $self->{w}; # $h = min $self->{h} - $widget->{y}, $h if $self->{h}; - $w = min $widget->{max_w}, $w if exists $widget->{max_w}; - $h = min $widget->{max_h}, $h if exists $widget->{max_h}; + $w = min $widget->{max_w}, $w if exists $widget->{max_w}; + $h = min $widget->{max_h}, $h if exists $widget->{max_h}; - $w = int $w + 0.5; - $h = int $h + 0.5; + $w = int $w + 0.5; + $h = int $h + 0.5; - if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) { - $widget->{old_w} = $widget->{w}; - $widget->{old_h} = $widget->{h}; + if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) { + $widget->{old_w} = $widget->{w}; + $widget->{old_h} = $widget->{h}; - $widget->{w} = $w; - $widget->{h} = $h; + $widget->{w} = $w; + $widget->{h} = $h; - $widget->emit (size_allocate => $w, $h); + $widget->emit (size_allocate => $w, $h); + } } } }