--- deliantra/Deliantra-Client/DC/UI.pm 2006/05/25 02:23:14 1.232 +++ deliantra/Deliantra-Client/DC/UI.pm 2006/05/27 21:15:57 1.245 @@ -7,15 +7,35 @@ use List::Util (); use CFClient; +use CFClient::Texture; our ($FOCUS, $HOVER, $GRAB); # various widgets +our $LAYOUT; our $ROOT; our $TOOLTIP; our $BUTTON_STATE; our %WIDGET; # all widgets, weak-referenced +sub get_layout { + for (grep { $_->{name} } values %WIDGET) { + $LAYOUT->{$_->{name}} = { + x => $_->{x} / $::WIDTH, + y => $_->{y} / $::HEIGHT, + w => $_->{w} / $::WIDTH, + h => $_->{h} / $::HEIGHT + }; + } + + return $LAYOUT; +} + +sub set_layout { + my ($layout) = @_; + $LAYOUT = $layout; +} + sub check_tooltip { if (!$GRAB) { for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) { @@ -188,16 +208,33 @@ }, $class; for (keys %$self) { - if (/^connect_(.*)$/) { + if (/^on_(.*)$/) { $self->connect ($1 => delete $self->{$_}); } } Scalar::Util::weaken ($CFClient::UI::WIDGET{$self+0} = $self); + if (my $layout = $CFClient::UI::LAYOUT->{$self->{name}}) { + $self->{user_x} = $layout->{x} * $::WIDTH; + $self->{user_y} = $layout->{y} * $::HEIGHT; + $self->{user_w} = ($layout->{w} != 0 ? $layout->{w} : 1) * $::WIDTH; + $self->{user_h} = ($layout->{h} != 0 ? $layout->{h} : 1) * $::HEIGHT; + } + $self } +sub toggle_visibility { + my ($self) = @_; + + if ($self->{visible}) { + $self->hide; + } else { + $self->show; + } +} + sub destroy { my ($self) = @_; @@ -228,8 +265,11 @@ sub set_invisible { my ($self) = @_; + return unless $self->{visible}; + # broken show/hide model + delete $self->{root}; delete $self->{visible}; undef $GRAB if $GRAB == $self; @@ -239,6 +279,8 @@ if $CFClient::UI::TOOLTIP->{owner} == $self; $self->focus_out; + + $self->emit (visibility_change => 0); } sub hide { @@ -324,6 +366,11 @@ sub set_tooltip { my ($self, $tooltip) = @_; + $tooltip =~ s/^\s+//; + $tooltip =~ s/\s+$//; + + return if $self->{tooltip} eq $tooltip; + $self->{tooltip} = $tooltip; if ($CFClient::UI::TOOLTIP->{owner} == $self) { @@ -422,10 +469,10 @@ glColor 1, 1, 0, 1; glTranslate $self->{x} + 0.375, $self->{y} + 0.375; glBegin GL_LINE_LOOP; - glVertex 0 , 0; - glVertex $self->{w}, 0; - glVertex $self->{w}, $self->{h}; - glVertex 0 , $self->{h}; + glVertex 0 , 0; + glVertex $self->{w} - 1, 0; + glVertex $self->{w} - 1, $self->{h} - 1; + glVertex 0 , $self->{h} - 1; glEnd; glPopMatrix; #CFClient::UI::Label->new (w => $self->{w}, h => $self->{h}, text => $self, fontsize => 0)->_draw; @@ -455,6 +502,14 @@ Scalar::Util::weaken ($self->{parent} = $parent); + if ($parent->{visible} || 1) { + $self->{root} = $parent->{root}; + $self->{visible} = $parent->{visible} + 1; + + $self->emit (visibility_change => 1) + unless $self->{parent}{visible}; + } + # TODO: req_w _does_change after ->reconfigure $self->check_size unless exists $self->{req_w}; @@ -495,6 +550,10 @@ || $self->$signal (@args); } +sub visibility_change { + #my ($self, $visible) = @_; +} + sub DESTROY { my ($self) = @_; @@ -615,7 +674,7 @@ $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; - $self->check_size; + $self->check_size (1); $self->update; } @@ -765,6 +824,16 @@ our @ISA = CFClient::UI::Window::; +sub new { + my $class = shift; + + $class->SUPER::new ( + scroll_x => 0, + scroll_y => 1, + @_, + ) +} + sub size_request { my ($self) = @_; @@ -776,9 +845,10 @@ sub size_allocate { my ($self, $w, $h) = @_; - my ($cw, $ch) = @$self{qw(child_w child_h)}; -# $w = $self->{w}; - $self->child->configure (0, 0, $cw, $ch); + $w = $self->{child_w} if $self->{scroll_x} && $self->{child_w}; + $h = $self->{child_h} if $self->{scroll_y} && $self->{child_h}; + + $self->child->configure (0, 0, $w, $h); $self->update; } @@ -839,9 +909,9 @@ my $self; my $slider = new CFClient::UI::Slider - vertical => 1, - range => [0, 0, 1, 0.01], # HACK fix - connect_changed => sub { + vertical => 1, + range => [0, 0, 1, 0.01], # HACK fix + on_changed => sub { $self->{vp}->set_offset (0, $_[1]); }, ; @@ -859,6 +929,16 @@ $self } +sub update { + my ($self) = @_; + + $self->SUPER::update; + + # todo: overwrite size_allocate of child + my $child = $self->{vp}->child; + $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]); +} + sub size_allocate { my ($self, $w, $h) = @_; @@ -996,9 +1076,11 @@ my $dx = $ev->{x} - $ox; my $dy = $ev->{y} - $oy; + $self->{user_x} = $wx + $dx * $mx; + $self->{user_y} = $wy + $dy * $my; $self->{user_w} = $bw + $dx * ($mx ? -1 : 1); $self->{user_h} = $bh + $dy * ($my ? -1 : 1); - $self->move ($wx + $dx * $mx, $wy + $dy * $my); + $self->move ($self->{user_x}, $self->{user_y}); $self->check_size; }; @@ -1011,7 +1093,9 @@ ($x, $y) = ($ev->{x}, $ev->{y}); - $self->move ($bx + $x - $ox, $by + $y - $oy); + $self->{user_x} = $bx + $x - $ox; + $self->{user_y} = $by + $y - $oy; + $self->move ($self->{user_x}, $self->{user_y}); $self->update; }; } @@ -1083,24 +1167,24 @@ $class->SUPER::new ( col_expand => [], - @_ + @_, ) } +sub children { + grep $_, map @$_, grep $_, @{ $_[0]{children} } +} + sub add { my ($self, $x, $y, $child) = @_; $child->set_parent ($self); $self->{children}[$y][$x] = $child; - $child->check_size; -} - -sub children { - grep $_, map @$_, grep $_, @{ $_[0]{children} } + $self->check_size (1); } -# TODO: move to container class maybe? send childs a signal on removal? +# TODO: move to container class maybe? send children a signal on removal? sub clear { my ($self) = @_; @@ -1112,6 +1196,7 @@ $_->hide; } + $self->check_size (1); $self->update; } @@ -1153,8 +1238,8 @@ my ($ws, $hs) = $self->get_wh; - my $req_w = sum @$ws; - my $req_h = sum @$hs; + my $req_w = (sum @$ws) || 1; + my $req_h = (sum @$hs) || 1; # TODO: nicer code && do row_expand my @col_expand = @{$self->{col_expand}}; @@ -2118,9 +2203,10 @@ sub set_range { my ($self, $range) = @_; - $self->{range} = $range; + ($range, $self->{range}) = ($self->{range}, $range); - $self->update; + $self->update + if "@$range" ne "@{$self->{range}}"; } sub set_value { @@ -2513,33 +2599,18 @@ my $class = shift; my $self = $class->SUPER::new ( - state => 0, - connect_activate => \&toggle_flopper, + state => 0, + on_activate => \&toggle_flopper, @_ ); - if ($self->{state}) { - $self->{state} = 0; - $self->toggle_flopper; - } - $self } sub toggle_flopper { my ($self) = @_; - # TODO: use animation - if ($self->{state} = !$self->{state}) { - $CFClient::UI::ROOT->add ($self->{other}); - $self->{other}->move ($self->coord2global (0, $self->{h})); - $self->_emit ("open"); - } else { - $CFClient::UI::ROOT->remove ($self->{other}); - $self->_emit ("close"); - } - - $self->_emit (changed => $self->{state}); + $self->{other}->toggle_visibility; } ############################################################################# @@ -2625,7 +2696,8 @@ my $class = shift; my $self = $class->SUPER::new ( - aspect => 1, + aspect => 1, + can_events => 0, @_, ); @@ -2694,118 +2766,6 @@ ############################################################################# -package CFClient::UI::InventoryItem; - -our @ISA = CFClient::UI::HBox::; - -sub _item_to_desc { - my ($item) = @_; - - my $desc = - $item->{nrof} < 2 - ? $item->{name} - : "$item->{nrof} × $item->{name_pl}"; - - $item->{flags} & Crossfire::Protocol::F_OPEN - and $desc .= " (open)"; - $item->{flags} & Crossfire::Protocol::F_APPLIED - and $desc .= " (applied)"; - $item->{flags} & Crossfire::Protocol::F_UNPAID - and $desc .= " (unpaid)"; - $item->{flags} & Crossfire::Protocol::F_MAGIC - and $desc .= " (magic)"; - $item->{flags} & Crossfire::Protocol::F_CURSED - and $desc .= " (cursed)"; - $item->{flags} & Crossfire::Protocol::F_DAMNED - and $desc .= " (damned)"; - $item->{flags} & Crossfire::Protocol::F_LOCKED - and $desc .= " *"; - - $desc -} - -sub new { - my $class = shift; - - my %args = @_; - - my $item = delete $args{item}; - - my $desc = _item_to_desc ($item); - - my $self = $class->SUPER::new ( - can_hover => 1, - can_events => 1, - tooltip => ((CFClient::UI::Label::escape $desc) - . "\nleftclick - examine\nshift+leftclick - move/pickup/drop\nmiddle click - apply\nrightclick - menu"), - connect_button_down => sub { - my ($self, $ev, $x, $y) = @_; - - # todo: maybe put examine on 1? but should just be a tooltip :( - if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) { - my $targ = $::CONN->{player}{tag}; - - if ($item->{container} == $::CONN->{player}{tag}) { - $targ = $main::OPENCONT; - } - - $::CONN->send ("move $targ $item->{tag} 0"); - } elsif ($ev->{button} == 1) { - $::CONN->send ("examine $item->{tag}"); - } elsif ($ev->{button} == 2) { - $::CONN->send ("apply $item->{tag}"); - } elsif ($ev->{button} == 3) { - my @menu_items = ( - ["examine", sub { $::CONN->send ("examine $item->{tag}") }], - ["mark", sub { $::CONN->send ("mark ". pack "N", $item->{tag}) }], - ["apply", sub { $::CONN->send ("apply $item->{tag}") }], - ( - $item->{flags} & Crossfire::Protocol::F_LOCKED - ? ( - ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $item->{tag}) }], - ) - : ( - ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $item->{tag}) }], - ["drop", sub { $::CONN->send ("move $main::OPENCONT $item->{tag} 0") }], - ) - ), - ); - - CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev); - } - - 1 - }, - %args - ); - - - $self->add (new CFClient::UI::Face - can_events => 0, - face => $item->{face}, - anim => $item->{anim}, - animspeed => $item->{animspeed}, - ); - - $self->add ($self->{name_lbl} = new CFClient::UI::Label can_events => 0); - - $self->{item} = $item; - - $self->update_item; - - $self -} - -sub update_item { - my ($self) = @_; - - my $desc = _item_to_desc ($self->{item}); - - $self->{name_lbl}->set_text ($desc); -} - -############################################################################# - package CFClient::UI::Inventory; our @ISA = CFClient::UI::ScrolledWindow::; @@ -2814,7 +2774,7 @@ my $class = shift; my $self = $class->SUPER::new ( - scrolled => (new CFClient::UI::Table), + scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]), @_, ); @@ -2834,20 +2794,16 @@ $self->{real_items} = \@items; + my $row = 0; for my $item (@items) { - $item->{item} = $item; - $item = $item->{widget} ||= new CFClient::UI::InventoryItem item => $item; - $item->update_item (); - } + CFClient::Item::update_widgets $item; - my $i = 0; - for (@items) { - $self->{scrolled}->add (0, $i, $_); - my $nrof = $_->{item}->{nrof} || 1; - $self->{scrolled}->add (1, $i++, new CFClient::UI::Label text => ($_->{item}->{weight} * $nrof) / 1000); - } + $self->{scrolled}->add (0, $row, $item->{face_widget}); + $self->{scrolled}->add (1, $row, $item->{desc_widget}); + $self->{scrolled}->add (2, $row, $item->{weight_widget}); -# $range->{range} = [$self->{pos}, 0, $self->{max_pos}, $page]; + $row++; + } } sub size_request { @@ -2995,6 +2951,8 @@ $text =~ s/^\s+//; $text =~ s/\s+$//; + return unless $text; + my $timeout = time + ((delete $arg{timeout}) || 60); my $group = exists $arg{group} ? $arg{group} : ++$self->{id}; @@ -3047,6 +3005,7 @@ my $class = shift; $class->SUPER::new ( + visible => 1, @_, ) } @@ -3083,6 +3042,9 @@ $Y = $child->{req_y} > 0 ? $child->{req_y} : $h - $H - $child->{req_y} + 1 if exists $child->{req_y}; + $X = $self->{user_x} if exists $self->{user_x}; + $Y = $self->{user_y} if exists $self->{user_y}; + $X = List::Util::max 0, List::Util::min $w - $W, int $X + 0.5; $Y = List::Util::max 0, List::Util::min $h - $H, int $Y + 0.5; @@ -3114,8 +3076,8 @@ for (my @widgets = @children; my $w = pop @widgets; ) { push @widgets, $w->children; - $w->{root} = $self; - $w->{visible} = 1; + $w->{root} = $self; + $w->{visible} = $self->{visible} + 1; } for my $child (@children) { @@ -3162,16 +3124,19 @@ } if ($self->{check_size}) { - my @queue = ([], []); + my @queue; for (;;) { if ($self->{check_size}) { - # heuristic: check containers last - push @{ $queue[ ! ! $_->isa ("CFClient::UI::Container") ] }, $_ - for values %{delete $self->{check_size}} + #TODO use array-of-depth approach + + @queue = sort { $a->{visible} <=> $b->{visible} } + @queue, values %{delete $self->{check_size}}; } - my $widget = (pop @{ $queue[0] }) || (pop @{ $queue[1] }) || last; + my $widget = pop @queue || last; + + defined $widget->{visible} or last; # do not resize invisible widgets my ($w, $h) = $widget->{user_w} && $widget->{user_h} ? @$widget{qw(user_w user_h)}