--- deliantra/Deliantra-Client/DC/UI.pm 2006/06/18 17:13:12 1.305
+++ deliantra/Deliantra-Client/DC/UI.pm 2006/07/02 18:52:05 1.317
@@ -337,6 +337,10 @@
Carp::confess "size_request is abstract";
}
+sub baseline_shift {
+ 0
+}
+
sub configure {
my ($self, $x, $y, $w, $h) = @_;
@@ -421,10 +425,9 @@
return if $FOCUS == $self;
return unless $self->{can_focus};
- my $focus = $FOCUS; $FOCUS = $self;
+ $FOCUS = $self;
- $focus->update if $focus;
- $FOCUS->update;
+ $self->update;
0
}
@@ -434,9 +437,9 @@
return unless $FOCUS == $self;
- my $focus = $FOCUS; undef $FOCUS;
+ undef $FOCUS;
- $focus->update if $focus; #?
+ $self->update;
$::MAPWIDGET->grab_focus #d# focus mapwidget if no other widget has focus
unless $FOCUS;
@@ -447,6 +450,7 @@
sub grab_focus {
my ($self) = @_;
+ $FOCUS->emit ("focus_out") if $FOCUS;
$self->emit ("focus_in");
}
@@ -472,9 +476,10 @@
sub emit {
my ($self, $signal, @args) = @_;
- (List::Util::sum +(map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}), # before
- ($self->can ("invoke_$signal") || sub { 1 })->($self, @args)) # closure
- || ($self->{parent} && $self->{parent}->emit ($signal, @args)) # parent
+ #d##TODO# stop propagating at first true, do not use sum
+ (List::Util::sum map $_->($self, @args), @{$self->{signal_cb}{$signal} || []}) # before
+ || ($self->can ("invoke_$signal") || sub { 1 })->($self, @args) # closure
+ || ($self->{parent} && $self->{parent}->emit ($signal, @args)) # parent
}
sub find_widget {
@@ -536,8 +541,6 @@
# update screen rectangle
local $draw_x = $draw_x + $self->{x};
local $draw_y = $draw_y + $self->{y};
- local $draw_w = $draw_x + $self->{w};
- local $draw_h = $draw_y + $self->{h};
# skip widgets that are entirely outside the drawing area
return if ($draw_x + $self->{w} < 0) || ($draw_x >= $draw_w)
@@ -985,34 +988,36 @@
$self
}
+#TODO# update range on size_allocate depending on child
+
sub add {
my ($self, $widget) = @_;
$self->{vp}->add ($self->{child} = $widget);
}
+sub update_slider {
+ my ($self) = @_;
+
+ $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $self->{vp}->child->{h}, $self->{vp}{h}, 1]);
+}
+
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]);
+ $self->update_slider;
}
sub invoke_size_allocate {
my ($self, $w, $h) = @_;
- my $child = $self->{vp}->child;
- $self->{slider}->set_range ([$self->{slider}{range}[0], 0, $child->{h}, $self->{vp}{h}, 1]);
+ $self->update_slider;
$self->SUPER::invoke_size_allocate ($w, $h)
}
-#TODO# update range on size_allocate depending on child
-# update viewport offset on scroll
-
#############################################################################
package CFClient::UI::Frame;
@@ -1092,8 +1097,8 @@
if ($self->{has_close_button}) {
$self->{close_button} =
new CFClient::UI::ImageButton
- image => 'x1_close.png',
- on_activate => sub { $self->hide };
+ path => 'x1_close.png',
+ on_activate => sub { $self->emit ("delete") };
$self->CFClient::UI::Container::add ($self->{close_button});
}
@@ -1152,6 +1157,14 @@
1
}
+sub invoke_delete {
+ my ($self) = @_;
+
+ $self->hide;
+
+ 1
+}
+
sub invoke_button_down {
my ($self, $ev, $x, $y) = @_;
@@ -1439,35 +1452,35 @@
my ($self, $w, $h) = @_;
my $space = $self->{vertical} ? $h : $w;
- my $children = $self->{children};
+ my @children = $self->visible_children;
my @req;
if ($self->{homogeneous}) {
- @req = ($space / (@$children || 1)) x @$children;
+ @req = ($space / (@children || 1)) x @children;
} else {
- @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @$children;
+ @req = map $_->{$self->{vertical} ? "req_h" : "req_w"}, @children;
my $req = List::Util::sum @req;
if ($req > $space) {
# ah well, not enough space
$_ *= $space / $req for @req;
} else {
- my $expand = (List::Util::sum map $_->{expand}, @$children) || 1;
+ my $expand = (List::Util::sum map $_->{expand}, @children) || 1;
$space = ($space - $req) / $expand; # remaining space to give away
- $req[$_] += $space * $children->[$_]{expand}
- for 0 .. $#$children;
+ $req[$_] += $space * $children[$_]{expand}
+ for 0 .. $#children;
}
}
CFClient::UI::harmonize \@req;
my $pos = 0;
- for (0 .. $#$children) {
+ for (0 .. $#children) {
my $alloc = $req[$_];
- $children->[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h));
+ $children[$_]->configure ($self->{vertical} ? (0, $pos, $w, $alloc) : ($pos, 0, $alloc, $h));
$pos += $alloc;
}
@@ -1632,6 +1645,10 @@
@{ $self->{size_req} }
}
+sub baseline_shift {
+ $_[0]{layout}->descent
+}
+
sub invoke_size_allocate {
my ($self, $w, $h) = @_;
@@ -1647,6 +1664,7 @@
my ($self, $fontsize) = @_;
$self->{fontsize} = $fontsize;
+ delete $self->{size_req};
delete $self->{texture};
$self->realloc;
@@ -1893,6 +1911,7 @@
if ($sym == 13) {
unshift @{$self->{history}},
my $txt = $self->get_text;
+
$self->{history_pointer} = -1;
$self->{history_saveback} = '';
$self->emit (activate => $txt);
@@ -2092,25 +2111,26 @@
our @ISA = CFClient::UI::Base::;
use CFClient::OpenGL;
-use Carp qw/confess/;
-our %loaded_images;
+our %texture_cache;
sub new {
my $class = shift;
- my $self = $class->SUPER::new (can_events => 0, @_);
-
- $self->{image} or confess "Image has 'image' not set. This is a fatal error!";
+ my $self = $class->SUPER::new (
+ can_events => 0,
+ @_,
+ );
- $loaded_images{$self->{image}} ||=
- new_from_file CFClient::Texture CFClient::find_rcfile $self->{image}, mipmap => 1;
+ $self->{path}
+ or Carp::croak "required attribute 'path' not set";
- my $tex = $self->{tex} = $loaded_images{$self->{image}};
+ $self->{tex} = $texture_cache{$self->{path}} ||=
+ new_from_file CFClient::Texture CFClient::find_rcfile $self->{path}, mipmap => 1;
- Scalar::Util::weaken $loaded_images{$self->{image}};
+ Scalar::Util::weaken $texture_cache{$self->{path}};
- $self->{aspect} = $tex->{w} / $tex->{h};
+ $self->{aspect} ||= $self->{tex}{w} / $self->{tex}{h};
$self
}
@@ -2118,7 +2138,7 @@
sub size_request {
my ($self) = @_;
- ($self->{tex}->{w}, $self->{tex}->{h})
+ ($self->{tex}{w}, $self->{tex}{h})
}
sub _draw {
@@ -2237,6 +2257,9 @@
my $h1 = $self->{h} * (1 - $ycut1);
my $h2 = $self->{h} * (1 - $ycut2);
+ my $h3 = $self->{h};
+
+ $_ = $_ * (284-4)/288 + 4/288 for ($h1, $h2, $h3);
glEnable GL_BLEND;
glBlendFuncSeparate GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA,
@@ -2265,8 +2288,8 @@
glBindTexture GL_TEXTURE_2D, $t3->{name};
glBegin GL_QUADS;
glTexCoord 0 , $t3->{t} * (1 - $ycut2); glVertex 0 , $h2;
- glTexCoord 0 , $t3->{t}; glVertex 0 , $self->{h};
- glTexCoord $t3->{s}, $t3->{t}; glVertex $w, $self->{h};
+ glTexCoord 0 , $t3->{t}; glVertex 0 , $h3;
+ glTexCoord $t3->{s}, $t3->{t}; glVertex $w, $h3;
glTexCoord $t3->{s}, $t3->{t} * (1 - $ycut2); glVertex $w, $h2;
glEnd;
}
@@ -2411,7 +2434,7 @@
$self->{click} = [$self->{range}[0], $self->{vertical} ? $y : $x];
- $self->mouse_motion ($ev, $x, $y)
+ $self->invoke_mouse_motion ($ev, $x, $y)
}
sub invoke_mouse_motion {
@@ -2566,30 +2589,51 @@
$self->reflow;
}
+sub size_request {
+ my ($self) = @_;
+
+ my ($empty, $slider) = @{ $self->{children} };
+
+ local $self->{children} = [$empty, $slider];
+ $self->SUPER::size_request
+}
+
sub invoke_size_allocate {
my ($self, $w, $h) = @_;
+ my ($empty, $slider, @other) = @{ $self->{children} };
+ $_->configure (@$_{qw(x y req_w req_h)}) for @other;
+
$self->{layout}->set_font ($self->{font}) if $self->{font};
$self->{layout}->set_height ($self->{fontsize} * $::FONTSIZE);
- $self->{layout}->set_width ($self->{children}[0]{w});
+ $self->{layout}->set_width ($empty->{w});
$self->{layout}->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
$self->reflow;
+ local $self->{children} = [$empty, $slider];
$self->SUPER::invoke_size_allocate ($w, $h)
}
-sub text_size {
- my ($self, $text, $indent) = @_;
+sub get_layout {
+ my ($self, $para) = @_;
my $layout = $self->{layout};
+ $layout->set_font ($self->{font}) if $self->{font};
+ $layout->set_foreground (@{$para->{fg}});
$layout->set_height ($self->{fontsize} * $::FONTSIZE);
- $layout->set_width ($self->{children}[0]{w} - $indent);
+ $layout->set_width ($self->{children}[0]{w} - $para->{indent});
$layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
- $layout->set_markup ($text);
-
- $layout->size
+ $layout->set_markup ($para->{markup});
+
+ $layout->set_shapes (
+ map
+ +(0, $_->baseline_shift +$_->{padding_y} - $_->{h}, $_->{w}, $_->{h}),
+ @{$para->{widget}}
+ );
+
+ $layout
}
sub reflow {
@@ -2609,21 +2653,40 @@
sub clear {
my ($self) = @_;
+ my (undef, undef, @other) = @{ $self->{children} };
+ $self->remove ($_) for @other;
+
$self->{par} = [];
$self->{height} = 0;
$self->{children}[1]->set_range ([0, 0, 0, 1, 1]);
}
sub add_paragraph {
- my ($self, $color, $text, $indent) = @_;
+ my ($self, $color, $para, $indent) = @_;
- for my $line (split /\n/, $text) {
- my ($w, $h) = $self->text_size ($line);
- $self->{height} += $h;
- push @{$self->{par}}, [$w + $indent, $h, $color, $indent, $line];
- }
+ my ($text, @w) = ref $para ? @$para : $para;
+
+ $para = {
+ w => 1e10,
+ wrapped => 1,
+ fg => $color,
+ indent => $indent,
+ markup => $text,
+ widget => \@w,
+ };
+
+ $self->add (@w) if @w;
+ push @{$self->{par}}, $para;
+
+ $self->{need_reflow}++;
+ $self->update;
+}
+
+sub scroll_to_bottom {
+ my ($self) = @_;
- $self->{children}[1]->set_range ([$self->{height}, 0, $self->{height}, $self->{h}, 1]);
+ $self->{scroll_to_bottom} = 1;
+ $self->update;
}
sub update {
@@ -2641,30 +2704,30 @@
if (delete $self->{need_reflow}) {
my $height = 0;
- my $layout = $self->{layout};
-
- $layout->set_height ($self->{fontsize} * $::FONTSIZE);
-
- for (@{$self->{par}}) {
- if (1 || $_->[0] >= $W) { # TODO: works,but needs reconfigure etc. support
- $layout->set_width ($W - $_->[3]);
- $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
- $layout->set_markup ($_->[4]);
+ for my $para (@{$self->{par}}) {
+ if ($para->{w} != $W && ($para->{wrapped} || $para->{w} > $W)) {
+ my $layout = $self->get_layout ($para);
my ($w, $h) = $layout->size;
- $_->[0] = $w + $_->[3];
- $_->[1] = $h;
+
+ $para->{w} = $w + $para->{indent};
+ $para->{h} = $h;
+ $para->{wrapped} = $layout->has_wrapped;
}
- $height += $_->[1];
+ $height += $para->{h};
}
$self->{height} = $height;
- $self->{children}[1]->set_range ([$height, 0, $height, $H, 1]);
+ $self->{children}[1]->set_range ([$self->{children}[1]{range}[0], 0, $height, $H, 1]);
delete $self->{texture};
}
+ if (delete $self->{scroll_to_bottom}) {
+ $self->{children}[1]->set_value (1e10);
+ }
+
$self->{texture} ||= new_from_opengl CFClient::Texture $W, $H, sub {
glClearColor 0, 0, 0, 0;
glClear GL_COLOR_BUFFER_BIT;
@@ -2676,27 +2739,37 @@
my $y = 0;
- my $layout = $self->{layout};
-
- $layout->set_font ($self->{font}) if $self->{font};
-
glEnable GL_BLEND;
#TODO# not correct in windows where rgba is forced off
glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
- for my $par (@{$self->{par}}) {
- my $h = $par->[1];
+ for my $para (@{$self->{par}}) {
+ my $h = $para->{h};
if ($y0 < $y + $h && $y < $y1) {
- $layout->set_foreground (@{ $par->[2] });
- $layout->set_width ($W - $par->[3]);
- $layout->set_indent ($self->{fontsize} * $::FONTSIZE * $self->{indent});
- $layout->set_markup ($par->[4]);
+
+ my $layout = $self->get_layout ($para);
my ($w, $h, $data, $format, $internalformat) = $layout->render;
- glRasterPos $par->[3], $y - $y0;
+ glRasterPos $para->{indent}, $y - $y0;
glDrawPixels $w, $h, $format, GL_UNSIGNED_BYTE, $data;
+
+ if (my @w = @{ $para->{widget} }) {
+ my @s = $layout->get_shapes;
+
+ glDisable GL_BLEND;
+ for (@w) {
+ my ($dx, $dy) = splice @s, 0, 2, ();
+
+ $_->{x} = $dx + $para->{indent};
+ $_->{y} = $dy + $y - $y0;
+
+ $_->draw;
+ }
+ glEnable GL_BLEND;
+ glBlendFunc GL_ONE, GL_ONE_MINUS_SRC_ALPHA;
+ }
}
$y += $h;
@@ -2707,6 +2780,15 @@
});
}
+sub reconfigure {
+ my ($self) = @_;
+
+ $self->SUPER::reconfigure;
+
+ $_->{w} = 1e10 for @{ $self->{par} };
+ $self->reflow;
+}
+
sub _draw {
my ($self) = @_;
@@ -2717,7 +2799,6 @@
glDisable GL_TEXTURE_2D;
$self->{children}[1]->draw;
-
}
#############################################################################
@@ -3343,7 +3424,9 @@
};
}
- $self->reorder;
+ $ROOT->on_refresh (reorder => sub {
+ $self->reorder;
+ });
}
sub reconfigure {
@@ -3390,7 +3473,7 @@
my @items = sort {
($a->{type} <=> $b->{type})
or ($a->{name} cmp $b->{name})
- } @$items;
+ } values %$items;
$self->{real_items} = \@items;
@@ -3408,204 +3491,6 @@
#############################################################################
-package CFClient::UI::BindEditor;
-
-our @ISA = CFClient::UI::FancyFrame::;
-
-sub new {
- my $class = shift;
-
- my $self = $class->SUPER::new (binding => [], commands => [], @_);
-
- $self->add (my $vb = new CFClient::UI::VBox);
-
-
- $vb->add ($self->{rec_btn} = new CFClient::UI::Button
- text => "start recording",
- tooltip => "Start/Stops recording of actions."
- ."All subsequent actions after the recording started will be captured."
- ."The actions are displayed after the record was stopped."
- ."To bind the action you have to click on the 'Bind' button",
- on_activate => sub {
- unless ($self->{recording}) {
- $self->start;
- } else {
- $self->stop;
- }
- });
-
- $vb->add (new CFClient::UI::Label text => "Actions:");
- $vb->add ($self->{cmdbox} = new CFClient::UI::VBox);
-
- $vb->add (new CFClient::UI::Label text => "Bound to: ");
- $vb->add (my $hb = new CFClient::UI::HBox);
- $hb->add ($self->{keylbl} = new CFClient::UI::Label expand => 1);
- $hb->add (new CFClient::UI::Button
- text => "bind",
- tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
- on_activate => sub {
- $self->ask_for_bind;
- });
-
- $vb->add (my $hb = new CFClient::UI::HBox);
- $hb->add (new CFClient::UI::Button
- text => "ok",
- expand => 1,
- tooltip => "This closes the binding editor and saves the binding",
- on_activate => sub {
- $self->hide;
- $self->commit;
- });
-
- $hb->add (new CFClient::UI::Button
- text => "cancel",
- expand => 1,
- tooltip => "This closes the binding editor without saving",
- on_activate => sub {
- $self->hide;
- $self->{binding_cancel}->()
- if $self->{binding_cancel};
- });
-
- $self->update_binding_widgets;
-
- $self
-}
-
-sub commit {
- my ($self) = @_;
- my ($mod, $sym, $cmds) = $self->get_binding;
- if ($sym != 0 && @$cmds > 0) {
- $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym)
- ."'. Don't forget 'Save Config'!");
- $self->{binding_change}->($mod, $sym, $cmds)
- if $self->{binding_change};
- } else {
- $::STATUSBOX->add ("No action bound, no key or action specified!");
- $self->{binding_cancel}->()
- if $self->{binding_cancel};
- }
-}
-
-sub start {
- my ($self) = @_;
-
- $self->{rec_btn}->set_text ("stop recording");
- $self->{recording} = 1;
- $self->clear_command_list;
- $::CONN->start_record if $::CONN;
-}
-
-sub stop {
- my ($self) = @_;
-
- $self->{rec_btn}->set_text ("start recording");
- $self->{recording} = 0;
-
- my $rec;
- $rec = $::CONN->stop_record if $::CONN;
- return unless ref $rec eq 'ARRAY';
- $self->set_command_list ($rec);
-}
-
-
-sub ask_for_bind_and_commit {
- my ($self) = @_;
- $self->ask_for_bind (1);
-}
-
-sub ask_for_bind {
- my ($self, $commit, $end_cb) = @_;
-
- CFClient::Binder::open_binding_dialog (sub {
- my ($mod, $sym) = @_;
- $self->{binding} = [$mod, $sym]; # XXX: how to stop that memleak?
- $self->update_binding_widgets;
- $self->commit if $commit;
- $end_cb->() if $end_cb;
- });
-}
-
-# $mod and $sym are the modifiers and key symbol
-# $cmds is a array ref of strings (the commands)
-# $cb is the callback that is executed on OK
-# $ccb is the callback that is executed on CANCEL and
-# when the binding was unsuccessful on OK
-sub set_binding {
- my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
-
- $self->clear_command_list;
- $self->{recording} = 0;
- $self->{rec_btn}->set_text ("start recording");
-
- $self->{binding} = [$mod, $sym];
- $self->{commands} = $cmds;
-
- $self->{binding_change} = $cb;
- $self->{binding_cancel} = $ccb;
-
- $self->update_binding_widgets;
-}
-
-# this is a shortcut method that asks for a binding
-# and then just binds it.
-sub do_quick_binding {
- my ($self, $cmds, $end_cb) = @_;
- $self->set_binding (undef, undef, $cmds, sub {
- $::CFG->{bindings}->{$_[0]}->{$_[1]} = $_[2];
- });
- $self->ask_for_bind (1, $end_cb);
-}
-
-sub update_binding_widgets {
- my ($self) = @_;
- my ($mod, $sym, $cmds) = $self->get_binding;
- $self->{keylbl}->set_text (CFClient::Binder::keycombo_to_name ($mod, $sym));
- $self->set_command_list ($cmds);
-}
-
-sub get_binding {
- my ($self) = @_;
- return (
- $self->{binding}->[0],
- $self->{binding}->[1],
- [ grep { defined $_ } @{$self->{commands}} ]
- );
-}
-
-sub clear_command_list {
- my ($self) = @_;
- $self->{cmdbox}->clear ();
-}
-
-sub set_command_list {
- my ($self, $cmds) = @_;
-
- $self->{cmdbox}->clear ();
- $self->{commands} = $cmds;
-
- my $idx = 0;
-
- for (@$cmds) {
- $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox);
-
- my $i = $idx;
- $hb->add (new CFClient::UI::Label text => $_);
- $hb->add (new CFClient::UI::Button
- text => "delete",
- tooltip => "Deletes the action from the record",
- on_activate => sub {
- $self->{cmdbox}->remove ($hb);
- $cmds->[$i] = undef;
- });
-
-
- $idx++
- }
-}
-
-#############################################################################
-
package CFClient::UI::SpellList;
our @ISA = CFClient::UI::Table::;
@@ -3664,7 +3549,7 @@
} elsif ($ev->{button} == 3) {
(new CFClient::UI::Menu
items => [
- ["bind cast $spell->{name} to a key" => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) }],
+ ["bind cast $spell->{name} to a key" => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) }],
["bind invoke $spell->{name} to a key" => sub { $::BIND_EDITOR->do_quick_binding (["invoke $spell->{name}"]) }],
],
)->popup ($ev);