--- deliantra/Deliantra-Client/DC/UI.pm 2006/05/28 01:40:02 1.249 +++ deliantra/Deliantra-Client/DC/UI.pm 2006/05/30 01:42:16 1.256 @@ -19,16 +19,18 @@ our %WIDGET; # all widgets, weak-referenced sub get_layout { + my $layout; + for (grep { $_->{name} } values %WIDGET) { - $LAYOUT->{$_->{name}} = { - x => $_->{x} / $::WIDTH, - y => $_->{y} / $::HEIGHT, - w => $_->{w} / $::WIDTH, - h => $_->{h} / $::HEIGHT - }; + 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}; + $win->{h} = $_->{h} / $::HEIGHT if defined $_->{h}; } - return $LAYOUT; + $layout } sub set_layout { @@ -42,6 +44,8 @@ if (length $widget->{tooltip}) { if ($TOOLTIP->{owner} != $widget) { + $TOOLTIP->hide; + $TOOLTIP->{owner} = $widget; my $tip = $widget->{tooltip}; @@ -50,15 +54,6 @@ $TOOLTIP->set_tooltip_from ($widget); $TOOLTIP->show; - - my ($x, $y) = $widget->coord2global ($widget->{w}, 0); - - ($x, $y) = $widget->coord2global (-$TOOLTIP->{w}, 0) - if $x + $TOOLTIP->{w} > $::WIDTH; - - $TOOLTIP->move ($x, $y); - $TOOLTIP->check_size; - $TOOLTIP->update; } return; @@ -174,12 +169,19 @@ for my $widget (values %WIDGET) { if ($widget->{is_toplevel}) { - $widget->{x} = int 0.5 + $widget->{x} * $sx if exists $widget->{x}; - $widget->{w} = int 0.5 + $widget->{w} * $sx if exists $widget->{w}; - $widget->{req_w} = int 0.5 + $widget->{req_w} * $sx if exists $widget->{req_w}; - $widget->{y} = int 0.5 + $widget->{y} * $sy if exists $widget->{y}; - $widget->{h} = int 0.5 + $widget->{h} * $sy if exists $widget->{h}; - $widget->{req_h} = int 0.5 + $widget->{req_h} * $sy if exists $widget->{req_h}; + $widget->{x} += $widget->{w} * 0.5 if $widget->{x} =~ /^[0-9.]+$/; + $widget->{y} += $widget->{h} * 0.5 if $widget->{y} =~ /^[0-9.]+$/; + + $widget->{x} = int 0.5 + $widget->{x} * $sx if $widget->{x} =~ /^[0-9.]+$/; + $widget->{w} = int 0.5 + $widget->{w} * $sx if exists $widget->{w}; + $widget->{force_w} = int 0.5 + $widget->{force_w} * $sx if exists $widget->{force_w}; + $widget->{y} = int 0.5 + $widget->{y} * $sy if $widget->{y} =~ /^[0-9.]+$/; + $widget->{h} = int 0.5 + $widget->{h} * $sy if exists $widget->{h}; + $widget->{force_h} = int 0.5 + $widget->{force_h} * $sy if exists $widget->{force_h}; + + $widget->{x} -= $widget->{w} * 0.5 if $widget->{x} =~ /^[0-9.]+$/; + $widget->{y} -= $widget->{h} * 0.5 if $widget->{y} =~ /^[0-9.]+$/; + } } @@ -198,41 +200,36 @@ my $class = shift; my $self = bless { - x => 0, - y => 0, + x => "center", + y => "center", z => 0, + w => undef, + h => undef, can_events => 1, @_ }, $class; - for (keys %$self) { - if (/^on_(.*)$/) { - $self->connect ($1 => delete $self->{$_}); - } + if (my $layout = $CFClient::UI::LAYOUT->{$self->{name}}) { + $self->{x} = $layout->{x} * $CFClient::UI::ROOT->{w} if exists $layout->{x}; + $self->{y} = $layout->{y} * $CFClient::UI::ROOT->{h} if exists $layout->{y}; + $self->{force_w} = $layout->{w} * $CFClient::UI::ROOT->{w} if exists $layout->{w}; + $self->{force_h} = $layout->{h} * $CFClient::UI::ROOT->{h} if exists $layout->{h}; + + $self->{x} -= $self->{force_w} * 0.5 if exists $layout->{x}; + $self->{y} -= $self->{force_h} * 0.5 if exists $layout->{y}; } Scalar::Util::weaken ($CFClient::UI::WIDGET{$self+0} = $self); - if (my $layout = $CFClient::UI::LAYOUT->{$self->{name}}) { - $self->{req_x} = $layout->{x} * $::WIDTH; - $self->{req_y} = $layout->{y} * $::HEIGHT; - $self->{def_w} = ($layout->{w} != 0 ? $layout->{w} : 1) * $::WIDTH; - $self->{def_h} = ($layout->{h} != 0 ? $layout->{h} : 1) * $::HEIGHT; + for (keys %$self) { + if (/^on_(.*)$/) { + $self->connect ($1 => delete $self->{$_}); + } } $self } -sub toggle_visibility { - my ($self) = @_; - - if ($self->{visible}) { - $self->hide; - } else { - $self->show; - } -} - sub destroy { my ($self) = @_; @@ -248,18 +245,6 @@ $CFClient::UI::ROOT->add ($self); } -sub center { - my ($self) = @_; - - $CFClient::UI::ROOT->on_post_alloc ( - "center_$self" => sub { - $self->move (($self->{parent}{w} - $self->{w}) * 0.5, ($self->{parent}{h} - $self->{h}) * 0.5); - }, - ); - - $self->update; -} - sub set_visible { my ($self) = @_; @@ -269,6 +254,10 @@ $self->{visible} = $self->{parent}{visible} + 1; $self->emit (visibility_change => 1); + + $self->realloc if !exists $self->{req_w}; + + $_->set_visible for $self->children; } sub set_invisible { @@ -276,7 +265,7 @@ return unless $self->{visible}; - # broken show/hide model + $_->set_invisible for $self->children; delete $self->{root}; delete $self->{visible}; @@ -285,13 +274,30 @@ undef $HOVER if $HOVER == $self; CFClient::UI::check_tooltip - if $CFClient::UI::TOOLTIP->{owner} == $self; + if $TOOLTIP->{owner} == $self; $self->focus_out; $self->emit (visibility_change => 0); } +sub set_visibility { + my ($self, $visible) = @_; + + return if $self->{visible} == $visible; + + $visible ? $self->hide + : $self->show; +} + +sub toggle_visibility { + my ($self) = @_; + + $self->{visible} + ? $self->hide + : $self->show; +} + sub hide { my ($self) = @_; @@ -301,11 +307,11 @@ if $self->{parent}; } -sub move { +sub move_abs { my ($self, $x, $y, $z) = @_; - $self->{x} = int $x; - $self->{y} = int $y; + $self->{x} = List::Util::max 0, int $x; + $self->{y} = List::Util::max 0, int $y; $self->{z} = $z if defined $z; $self->update; @@ -314,10 +320,10 @@ sub set_size { my ($self, $w, $h) = @_; - $self->{def_w} = $w; - $self->{def_h} = $h; + $self->{force_w} = $w; + $self->{force_h} = $h; - $self->check_size; + $self->realloc; } sub size_request { @@ -329,25 +335,27 @@ my ($self, $x, $y, $w, $h) = @_; if ($self->{aspect}) { - my $w2 = List::Util::min $w, int $h * $self->{aspect}; - my $h2 = List::Util::min $h, int $w / $self->{aspect}; + my ($ow, $oh) = ($w, $h); - # use alignment to adjust x, y + $w = List::Util::min $w, int $h * $self->{aspect}; + $h = List::Util::min $h, int $w / $self->{aspect}; - $x += int +($w - $w2) * 0.5; - $y += int +($h - $h2) * 0.5; + # use alignment to adjust x, y - ($w, $h) = ($w2, $h2); + $x += int 0.5 * ($ow - $w); + $y += int 0.5 * ($oh - $h); } - if ($self->{x} != $x || $self->{y} != $y) { + if ($self->{x} ne $x || $self->{y} ne $y) { $self->{x} = $x; $self->{y} = $y; $self->update; } if ($self->{w} != $w || $self->{h} != $h) { - $CFClient::UI::ROOT->{size_alloc}{$self} = [$self, $w, $h]; + return unless $self->{visible}; + + $self->{root}->{size_alloc}{$self+0} = [$self, $w, $h]; } } @@ -355,13 +363,6 @@ # nothing to be done } -sub reconfigure { - my ($self) = @_; - - $self->check_size (1); - $self->update; -} - sub children { } @@ -448,6 +449,76 @@ sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} } sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} } +sub find_widget { + my ($self, $x, $y) = @_; + + return () unless $self->{can_events}; + + return $self + if $x >= $self->{x} && $x < $self->{x} + $self->{w} + && $y >= $self->{y} && $y < $self->{y} + $self->{h}; + + () +} + +sub set_parent { + my ($self, $parent) = @_; + + Scalar::Util::weaken ($self->{parent} = $parent); + + $self->set_visible if $parent->{visible}; +} + +sub connect { + my ($self, $signal, $cb) = @_; + + push @{ $self->{signal_cb}{$signal} }, $cb; +} + +sub _emit { + my ($self, $signal, @args) = @_; + + List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []} +} + +sub emit { + my ($self, $signal, @args) = @_; + + $self->_emit ($signal, @args) + || $self->$signal (@args); +} + +sub visibility_change { + #my ($self, $visible) = @_; +} + +sub realloc { + my ($self) = @_; + + if ($self->{visible}) { + return if $self->{root}{realloc}{$self}; + + $self->{root}{realloc}{$self} = $self; + $self->{root}->update; + } else { + delete $self->{req_w}; + } +} + +sub update { + my ($self) = @_; + + $self->{parent}->update + if $self->{parent}; +} + +sub reconfigure { + my ($self) = @_; + + $self->realloc; + $self->update; +} + sub draw { my ($self) = @_; @@ -473,7 +544,7 @@ glDisable GL_BLEND; } - if ($ENV{PCLIENT_DEBUG}) { + if ($ENV{CFPLUS_DEBUG}) { glPushMatrix; glColor 1, 1, 0, 1; glTranslate $self->{x} + 0.375, $self->{y} + 0.375; @@ -494,65 +565,6 @@ warn "no draw defined for $self\n"; } -sub find_widget { - my ($self, $x, $y) = @_; - - return () unless $self->{can_events}; - - return $self - if $x >= $self->{x} && $x < $self->{x} + $self->{w} - && $y >= $self->{y} && $y < $self->{y} + $self->{h}; - - () -} - -sub set_parent { - my ($self, $parent) = @_; - - Scalar::Util::weaken ($self->{parent} = $parent); - - $self->set_visible; #TODO why breakssssss borked damn if $parent->{visible}; - - $self->check_size; -} - -sub check_size { - my ($self, $forced) = @_; - - $self->{force_alloc} = 1 if $forced; - $CFClient::UI::ROOT->{check_size}{$self} = $self; -} - -sub update { - my ($self) = @_; - - $self->{parent}->update - if $self->{parent}; -} - -sub connect { - my ($self, $signal, $cb) = @_; - - push @{ $self->{signal_cb}{$signal} }, $cb; -} - -sub _emit { - my ($self, $signal, @args) = @_; - - List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []} -} - -sub emit { - my ($self, $signal, @args) = @_; - - $self->_emit ($signal, @args) - || $self->$signal (@args); -} - -sub visibility_change { - #my ($self, $visible) = @_; -} - sub DESTROY { my ($self) = @_; @@ -618,7 +630,9 @@ } sub size_request { - (0, 0) + my ($self) = @_; + + ($self->{w} + 0, $self->{h} + 0) } sub draw { } @@ -657,8 +671,7 @@ @{$self->{children}}, @widgets ]; - $self->check_size (1); - $self->update; + $self->realloc; } sub children { @@ -673,8 +686,7 @@ $self->{children} = [ grep $_ != $child, @{ $self->{children} } ]; - $self->check_size (1); - $self->update; + $self->realloc; } sub clear { @@ -688,8 +700,7 @@ $_->hide; } - $self->check_size; - $self->update; + $self->realloc; } sub find_widget { @@ -752,7 +763,7 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; $self->{children}[0]->configure (0, 0, $w, $h); } @@ -779,10 +790,11 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; - $self->SUPER::size_allocate ($w, $h); - $self->update; + $self->SUPER::size_allocate ($w, $h, $changed); + $self->update + if $changed; } sub _render { @@ -845,7 +857,7 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; $w = $self->{child_w} if $self->{scroll_x} && $self->{child_w}; $h = $self->{child_h} if $self->{scroll_y} && $self->{child_h}; @@ -942,9 +954,9 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; - $self->SUPER::size_allocate ($w, $h); + $self->SUPER::size_allocate ($w, $h, $changed); my $child = $self->{vp}->child; $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]); @@ -1001,9 +1013,13 @@ use CFClient::OpenGL; -my @tex = +my $bg = + new_from_file CFClient::Texture CFClient::find_rcfile "d1_bg.png", + mipmap => 1, wrap => 1; + +my @border = map { new_from_file CFClient::Texture CFClient::find_rcfile $_, mipmap => 1 } - qw(d1_bg.png d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); + qw(d1_border_top.png d1_border_right.png d1_border_left.png d1_border_bottom.png); sub new { my $class = shift; @@ -1042,7 +1058,9 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; + + return unless $changed; $h -= List::Util::max 0, $self->border * 2; $w -= List::Util::max 0, $self->border * 2; @@ -1076,12 +1094,11 @@ my $dx = $ev->{x} - $ox; my $dy = $ev->{y} - $oy; - $self->{user_x} = $wx + $dx * $mx; - $self->{user_y} = $wy + $dy * $my; - $self->{def_w} = $bw + $dx * ($mx ? -1 : 1); - $self->{def_h} = $bh + $dy * ($my ? -1 : 1); - $self->move ($self->{user_x}, $self->{user_y}); - $self->check_size; + $self->{force_w} = $bw + $dx * ($mx ? -1 : 1); + $self->{force_h} = $bh + $dy * ($my ? -1 : 1); + + $self->realloc; + $self->move_abs ($wx + $dx * $mx, $wy + $dy * $my); }; } elsif ($lr ^ $td) { @@ -1093,11 +1110,7 @@ ($x, $y) = ($ev->{x}, $ev->{y}); - $self->{user_x} = $bx + $x - $ox; - $self->{user_y} = $by + $y - $oy; - - $self->move ($self->{user_x}, $self->{user_y}); - $self->update; + $self->move_abs ($bx + $x - $ox, $by + $y - $oy); }; } } @@ -1126,23 +1139,18 @@ my $border = $self->border; glColor @{ $self->{border_bg} }; - $tex[1]->draw_quad_alpha (0, 0, $w, $border); - $tex[3]->draw_quad_alpha (0, $border, $border, $ch); - $tex[2]->draw_quad_alpha ($w - $border, $border, $border, $ch); - $tex[4]->draw_quad_alpha (0, $h - $border, $w, $border); + $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); if (@{$self->{bg}} < 4 || $self->{bg}[3]) { - my $bg = $tex[0]; - - # TODO: repeat texture not scale - my $rep_x = $cw / $bg->{w}; - my $rep_y = $ch / $bg->{h}; - glColor @{ $self->{bg} }; - $bg->{s} = $rep_x; - $bg->{t} = $rep_y; - $bg->{wrap_mode} = 1; + # TODO: repeat texture not scale + # solve this better(?) + $bg->{s} = $cw / $bg->{w}; + $bg->{t} = $ch / $bg->{h}; $bg->draw_quad_alpha ($border, $border, $cw, $ch); } @@ -1182,7 +1190,7 @@ $child->set_parent ($self); $self->{children}[$y][$x] = $child; - $self->check_size (1); + $self->realloc; } # TODO: move to container class maybe? send children a signal on removal? @@ -1197,8 +1205,7 @@ $_->hide; } - $self->check_size (1); - $self->update; + $self->realloc; } sub get_wh { @@ -1235,7 +1242,7 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; my ($ws, $hs) = $self->get_wh; @@ -1323,7 +1330,7 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; my $space = $self->{vertical} ? $h : $w; my $children = $self->{children}; @@ -1462,8 +1469,8 @@ $self->{layout} = new CFClient::Layout if $self->{layout}->is_rgba; $self->{layout}->set_text ($text); + $self->realloc; $self->update; - $self->check_size; } sub set_markup { @@ -1477,8 +1484,8 @@ $self->{layout} = new CFClient::Layout $rgba if $self->{layout}->is_rgba != $rgba; $self->{layout}->set_markup ($markup); + $self->realloc; $self->update; - $self->check_size; } sub size_request { @@ -1509,9 +1516,10 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; - delete $self->{texture}; + delete $self->{texture} + if $changed; } sub set_fontsize { @@ -1520,8 +1528,7 @@ $self->{fontsize} = $fontsize; delete $self->{texture}; - $self->update; - $self->check_size; + $self->realloc; } sub _draw { @@ -1611,8 +1618,8 @@ $self->{cursor} = length $text; $self->_set_text ($text); - $self->update; - $self->check_size; + + $self->realloc; } sub get_text { @@ -1655,8 +1662,8 @@ } $self->_set_text ($text); - $self->update; - $self->check_size; + + $self->realloc; } sub focus_in { @@ -2173,6 +2180,8 @@ $self } +sub changed { } + sub set_range { my ($self, $range) = @_; @@ -2371,9 +2380,11 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; + + $self->SUPER::size_allocate ($w, $h, $changed); - $self->SUPER::size_allocate ($w, $h); + return unless $changed; $self->{layout}->set_font ($self->{font}) if $self->{font}; $self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE); @@ -2625,9 +2636,29 @@ } sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; + + return unless $changed; + + $self->SUPER::size_allocate ($w - 4, $h - 4, $changed); +} + +sub visibility_change { + my ($self, $visible) = @_; - $self->SUPER::size_allocate ($w - 4, $h - 4); + return unless $visible; + + $self->{root}->on_post_alloc ("move_$self" => sub { + my $widget = $self->{owner} + or return; + + my ($x, $y) = $widget->coord2global ($widget->{w}, 0); + + ($x, $y) = $widget->coord2global (-$self->{w}, 0) + if $x + $self->{w} > $::WIDTH; + + $self->move_abs ($x, $y); + }); } sub _draw { @@ -2654,6 +2685,7 @@ glEnd; glTranslate 2 - 0.375, 2 - 0.375; + $self->SUPER::_draw; } @@ -2972,10 +3004,14 @@ sub new { my $class = shift; - $class->SUPER::new ( + my $self = $class->SUPER::new ( visible => 1, @_, - ) + ); + + Scalar::Util::weaken ($self->{root} = $self); + + $self } sub configure { @@ -2985,10 +3021,12 @@ $self->{h} = $h; } -sub check_size { +sub reconfigure { my ($self) = @_; - $self->size_allocate ($self->{w}, $self->{h}) + $self->SUPER::reconfigure; + + $self->size_allocate ($self->{w}, $self->{h}, 1) if $self->{w}; } @@ -2998,22 +3036,31 @@ ($self->{w}, $self->{h}) } +sub _to_pixel { + my ($coord, $size, $max) = @_; + + $coord = + $coord eq "center" ? ($max - $size) * 0.5 + : $coord eq "max" ? $max + : $coord; + + $coord = 0 if $coord < 0; + $coord = $max - $size if $coord > $max - $size; + + int $coord + 0.5 +} + sub size_allocate { - my ($self, $w, $h) = @_; + my ($self, $w, $h, $changed) = @_; for my $child ($self->children) { my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)}; - $X = $child->{req_x} > 0 ? $child->{req_x} : $w - $W - $child->{req_x} + 1 - if exists $child->{req_x}; - - $Y = $child->{req_y} > 0 ? $child->{req_y} : $h - $H - $child->{req_y} + 1 - if exists $child->{req_y}; - - delete @$child{qw(req_x req_y)}; + $X = $child->{force_x} if exists $child->{force_x}; + $Y = $child->{force_y} if exists $child->{force_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; + $X = _to_pixel $X, $W, $self->{w}; + $Y = _to_pixel $Y, $H, $self->{h}; $child->configure ($X, $Y, $W, $H); } @@ -3034,28 +3081,16 @@ sub update { my ($self) = @_; - $self->check_size; $::WANT_REFRESH++; } sub add { my ($self, @children) = @_; - for my $child (@children) { - $child->{is_toplevel} = 1; - - # integerise window positions - $child->{x} = int $child->{x}; - $child->{y} = int $child->{y}; - } + $_->{is_toplevel} = 1 + for @children; $self->SUPER::add (@children); - - for (my @widgets = @children; my $w = pop @widgets; ) { - push @widgets, $w->children; - $w->set_visible; - } - } sub remove { @@ -3063,6 +3098,9 @@ $self->SUPER::remove (@children); + delete $self->{is_toplevel} + for @children; + while (@children) { my $w = pop @children; push @children, $w->children; @@ -3090,53 +3128,62 @@ for values %{delete $self->{refresh_hook}}; } - if ($self->{check_size}) { + if ($self->{realloc}) { my @queue; - for (;;) { - if ($self->{check_size}) { + while () { + if ($self->{realloc}) { #TODO use array-of-depth approach @queue = sort { $a->{visible} <=> $b->{visible} } - @queue, values %{delete $self->{check_size}}; + @queue, values %{delete $self->{realloc}}; } my $widget = pop @queue || last; - defined $widget->{visible} or last; # do not resize invisible widgets + $widget->{visible} or last; # do not resize invisible widgets - my ($w, $h) = $widget->{def_w} && $widget->{def_h} - ? @$widget{qw(def_w def_h)} - : $widget->size_request; - - if (delete $widget->{force_alloc} - or $w != $widget->{req_w} or $h != $widget->{req_h}) { - Carp::confess "$widget: size_request is negative" if $w < 0 || $h < 0;#d# - - $widget->{req_w} = $w; - $widget->{req_h} = $h; + my ($w, $h) = exists $widget->{force_w} && exists $widget->{force_h} + ? @$widget{qw(force_w force_h)} + : $widget->size_request; - $self->{size_alloc}{$widget} = [$widget, $widget->{w} || $w, $widget->{h} || $h]; + my $min_size = $widget->{is_toplevel} ? 16 : 0; - $widget->{parent}->check_size - if $widget->{parent}; - } + $w = $min_size if $w < $min_size; + $h = $min_size if $h < $min_size; + + $widget->{req_w} = $w; + $widget->{req_h} = $h; + + $self->{size_alloc}{$widget} = [$widget, undef, undef]; + + push @queue, $widget->{parent} + if $widget->{parent}; } } - while ($self->{size_alloc}) { - for ( - sort { $a->[0]{visible} <=> $b->[0]{visible} } - values %{delete $self->{size_alloc}} - ) { - my ($widget, $w, $h) = @$_; + while (my $size_alloc = delete $self->{size_alloc}) { + my @queue = sort $b->[0]{visible} <=> $a->[0]{visible}, + values %$size_alloc; + + while () { + my ($widget, $w, $h) = @{ pop @queue or last }; + + $w = $widget->{w} || $widget->{req_w} unless defined $w; + $h = $widget->{h} || $widget->{req_h} unless defined $h; $w = 0 if $w < 0; $h = 0 if $h < 0; + $w = int $w + 0.5; + $h = int $h + 0.5; + + my $changed = $widget->{w} != $w || $widget->{h} != $h; + $widget->{w} = $w; $widget->{h} = $h; - $widget->emit (size_allocate => $w, $h); + + $widget->emit (size_allocate => $w, $h, $changed); } }