ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/UI.pm
(Generate patch)

Comparing deliantra/Deliantra-Client/DC/UI.pm (file contents):
Revision 1.260 by elmex, Tue May 30 14:35:09 2006 UTC vs.
Revision 1.267 by root, Thu Jun 1 04:10:29 2006 UTC

40 40
41 $LAYOUT = $layout; 41 $LAYOUT = $layout;
42} 42}
43 43
44sub check_tooltip { 44sub check_tooltip {
45 return if $ENV{CFPLUS_DEBUG} & 8;
46
45 if (!$GRAB) { 47 if (!$GRAB) {
46 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) { 48 for (my $widget = $HOVER; $widget; $widget = $widget->{parent}) {
47 if (length $widget->{tooltip}) { 49 if (length $widget->{tooltip}) {
48
49 if ($TOOLTIP->{owner} != $widget) { 50 if ($TOOLTIP->{owner} != $widget) {
50 $TOOLTIP->hide; 51 $TOOLTIP->hide;
51 52
52 $TOOLTIP->{owner} = $widget; 53 $TOOLTIP->{owner} = $widget;
53 54
449 my ($self, $ev, $x, $y) = @_; 450 my ($self, $ev, $x, $y) = @_;
450 451
451 $self->focus_in; 452 $self->focus_in;
452} 453}
453 454
454sub w { $_[0]{w} = $_[1] if @_ > 1; $_[0]{w} }
455sub h { $_[0]{h} = $_[1] if @_ > 1; $_[0]{h} }
456sub x { $_[0]{x} = $_[1] if @_ > 1; $_[0]{x} }
457sub y { $_[0]{y} = $_[1] if @_ > 1; $_[0]{y} }
458sub z { $_[0]{z} = $_[1] if @_ > 1; $_[0]{z} }
459
460sub find_widget { 455sub find_widget {
461 my ($self, $x, $y) = @_; 456 my ($self, $x, $y) = @_;
462 457
463 return () unless $self->{can_events}; 458 return () unless $self->{can_events};
464 459
525 520
526 $self->realloc; 521 $self->realloc;
527 $self->update; 522 $self->update;
528} 523}
529 524
525# using global variables seems a bit hacky, but passing through all drawing
526# functions seems pointless.
527our ($draw_x, $draw_y, $draw_w, $draw_h); # screen rectangle being drawn
528
530sub draw { 529sub draw {
531 my ($self) = @_; 530 my ($self) = @_;
532 531
533 return unless $self->{h} && $self->{w}; 532 return unless $self->{h} && $self->{w};
533
534 local $draw_x = $draw_x + $self->{x};
535 local $draw_y = $draw_y + $self->{y};
536
537 return if $draw_x + $self->{w} < 0;
538 return if $draw_x >= $draw_w;
539 return if $draw_y + $self->{h} < 0;
540 return if $draw_y >= $draw_h;
534 541
535 glPushMatrix; 542 glPushMatrix;
536 glTranslate $self->{x}, $self->{y}, 0; 543 glTranslate $self->{x}, $self->{y}, 0;
537 $self->_draw; 544 $self->_draw;
538 glPopMatrix; 545 glPopMatrix;
803 $self->SUPER::size_allocate ($w, $h); 810 $self->SUPER::size_allocate ($w, $h);
804 $self->update; 811 $self->update;
805} 812}
806 813
807sub _render { 814sub _render {
815 my ($self) = @_;
816
808 $_[0]{children}[0]->draw; 817 $self->{children}[0]->draw;
809} 818}
810 819
811sub render_child { 820sub render_child {
812 my ($self) = @_; 821 my ($self) = @_;
813 822
814 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub { 823 $self->{texture} = new_from_opengl CFClient::Texture $self->{w}, $self->{h}, sub {
815 glClearColor 0, 0, 0, 0; 824 glClearColor 0, 0, 0, 0;
816 glClear GL_COLOR_BUFFER_BIT; 825 glClear GL_COLOR_BUFFER_BIT;
817 826
827 {
828 package CFClient::UI::Base;
829
830 ($draw_x, $draw_y, $draw_w, $draw_h) =
831 (0, 0, $self->{w}, $self->{h});
832 }
833
818 $self->_render; 834 $self->_render;
819 }; 835 };
820} 836}
821 837
822sub _draw { 838sub _draw {
823 my ($self) = @_; 839 my ($self) = @_;
824 840
825 my ($w, $h) = ($self->w, $self->h); 841 my ($w, $h) = @$self{qw(w h)};
826 842
827 my $tex = $self->{texture} 843 my $tex = $self->{texture}
828 or return; 844 or return;
829 845
830 glEnable GL_TEXTURE_2D; 846 glEnable GL_TEXTURE_2D;
912 } 928 }
913} 929}
914 930
915sub _render { 931sub _render {
916 my ($self) = @_; 932 my ($self) = @_;
933
934 local $CFClient::UI::Base::draw_x = $CFClient::UI::Base::draw_x - $self->{view_x};
935 local $CFClient::UI::Base::draw_y = $CFClient::UI::Base::draw_y - $self->{view_y};
917 936
918 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y}; 937 CFClient::OpenGL::glTranslate -$self->{view_x}, -$self->{view_y};
919 938
920 $self->SUPER::_render; 939 $self->SUPER::_render;
921} 940}
1522} 1541}
1523 1542
1524sub size_allocate { 1543sub size_allocate {
1525 my ($self, $w, $h) = @_; 1544 my ($self, $w, $h) = @_;
1526 1545
1527 delete $self->{texture}; 1546 delete $self->{texture}
1547 unless $w >= $self->{req_w} && $self->{old_w} >= $self->{req_w};
1528} 1548}
1529 1549
1530sub set_fontsize { 1550sub set_fontsize {
1531 my ($self, $fontsize) = @_; 1551 my ($self, $fontsize) = @_;
1532 1552
2776 2796
2777 $self->{timer}->cancel 2797 $self->{timer}->cancel
2778 if $self->{timer}; 2798 if $self->{timer};
2779 2799
2780 $self->SUPER::DESTROY; 2800 $self->SUPER::DESTROY;
2781}
2782
2783#############################################################################
2784
2785package CFClient::UI::Inventory;
2786
2787our @ISA = CFClient::UI::ScrolledWindow::;
2788
2789sub new {
2790 my $class = shift;
2791
2792 my $self = $class->SUPER::new (
2793 scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]),
2794 @_,
2795 );
2796
2797 $self
2798}
2799
2800sub set_items {
2801 my ($self, $items) = @_;
2802
2803 $self->{scrolled}->clear;
2804 return unless $items;
2805
2806 my @items = sort {
2807 ($a->{type} <=> $b->{type})
2808 or ($a->{name} cmp $b->{name})
2809 } @$items;
2810
2811 $self->{real_items} = \@items;
2812
2813 my $row = 0;
2814 for my $item (@items) {
2815 CFClient::Item::update_widgets $item;
2816
2817 $self->{scrolled}->add (0, $row, $item->{face_widget});
2818 $self->{scrolled}->add (1, $row, $item->{desc_widget});
2819 $self->{scrolled}->add (2, $row, $item->{weight_widget});
2820
2821 $row++;
2822 }
2823} 2801}
2824 2802
2825############################################################################# 2803#############################################################################
2826 2804
2827package CFClient::UI::Menu; 2805package CFClient::UI::Menu;
3004 $self->SUPER::reconfigure; 2982 $self->SUPER::reconfigure;
3005} 2983}
3006 2984
3007############################################################################# 2985#############################################################################
3008 2986
3009package CFClient::UI::Root; 2987package CFClient::UI::Inventory;
3010 2988
3011our @ISA = CFClient::UI::Container::; 2989our @ISA = CFClient::UI::ScrolledWindow::;
3012
3013use CFClient::OpenGL;
3014 2990
3015sub new { 2991sub new {
3016 my $class = shift; 2992 my $class = shift;
3017 2993
3018 my $self = $class->SUPER::new ( 2994 my $self = $class->SUPER::new (
3019 visible => 1, 2995 scrolled => (new CFClient::UI::Table col_expand => [0, 1, 0]),
3020 @_, 2996 @_,
3021 ); 2997 );
3022 2998
3023 Scalar::Util::weaken ($self->{root} = $self);
3024
3025 $self 2999 $self
3026} 3000}
3027 3001
3028sub size_request { 3002sub set_items {
3029 my ($self) = @_; 3003 my ($self, $items) = @_;
3030 3004
3031 ($self->{w}, $self->{h}) 3005 $self->{scrolled}->clear;
3032} 3006 return unless $items;
3033 3007
3034sub _to_pixel { 3008 my @items = sort {
3035 my ($coord, $size, $max) = @_; 3009 ($a->{type} <=> $b->{type})
3010 or ($a->{name} cmp $b->{name})
3011 } @$items;
3036 3012
3037 $coord = 3013 $self->{real_items} = \@items;
3038 $coord eq "center" ? ($max - $size) * 0.5
3039 : $coord eq "max" ? $max
3040 : $coord;
3041 3014
3042 $coord = 0 if $coord < 0; 3015 my $row = 0;
3043 $coord = $max - $size if $coord > $max - $size; 3016 for my $item (@items) {
3017 CFClient::Item::update_widgets $item;
3044 3018
3045 int $coord + 0.5 3019 $self->{scrolled}->add (0, $row, $item->{face_widget});
3046} 3020 $self->{scrolled}->add (1, $row, $item->{desc_widget});
3021 $self->{scrolled}->add (2, $row, $item->{weight_widget});
3047 3022
3048sub size_allocate { 3023 $row++;
3049 my ($self, $w, $h) = @_;
3050
3051 for my $child ($self->children) {
3052 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)};
3053
3054 $X = $child->{force_x} if exists $child->{force_x};
3055 $Y = $child->{force_y} if exists $child->{force_y};
3056
3057 $X = _to_pixel $X, $W, $self->{w};
3058 $Y = _to_pixel $Y, $H, $self->{h};
3059
3060 $child->configure ($X, $Y, $W, $H);
3061 } 3024 }
3062}
3063
3064sub coord2local {
3065 my ($self, $x, $y) = @_;
3066
3067 ($x, $y)
3068}
3069
3070sub coord2global {
3071 my ($self, $x, $y) = @_;
3072
3073 ($x, $y)
3074}
3075
3076sub update {
3077 my ($self) = @_;
3078
3079 $::WANT_REFRESH++;
3080}
3081
3082sub add {
3083 my ($self, @children) = @_;
3084
3085 $_->{is_toplevel} = 1
3086 for @children;
3087
3088 $self->SUPER::add (@children);
3089}
3090
3091sub remove {
3092 my ($self, @children) = @_;
3093
3094 $self->SUPER::remove (@children);
3095
3096 delete $self->{is_toplevel}
3097 for @children;
3098
3099 while (@children) {
3100 my $w = pop @children;
3101 push @children, $w->children;
3102 $w->set_invisible;
3103 }
3104}
3105
3106sub on_refresh {
3107 my ($self, $id, $cb) = @_;
3108
3109 $self->{refresh_hook}{$id} = $cb;
3110}
3111
3112sub on_post_alloc {
3113 my ($self, $id, $cb) = @_;
3114
3115 $self->{post_alloc_hook}{$id} = $cb;
3116}
3117
3118sub draw {
3119 my ($self) = @_;
3120
3121 while ($self->{refresh_hook}) {
3122 $_->()
3123 for values %{delete $self->{refresh_hook}};
3124 }
3125
3126 if ($self->{realloc}) {
3127 my @queue;
3128
3129 while () {
3130 if ($self->{realloc}) {
3131 #TODO use array-of-depth approach
3132
3133 use sort 'stable';
3134
3135 @queue = sort { $a->{visible} <=> $b->{visible} }
3136 @queue, values %{delete $self->{realloc}};
3137 }
3138
3139 my $widget = pop @queue || last;
3140
3141 $widget->{visible} or last; # do not resize invisible widgets
3142
3143 my ($w, $h) = $widget->size_request;
3144
3145 $w = List::Util::max $widget->{min_w}, $w + $widget->{padding_x} * 2;
3146 $h = List::Util::max $widget->{min_h}, $h + $widget->{padding_y} * 2;
3147
3148 $w = $widget->{force_w} if exists $widget->{force_w};
3149 $h = $widget->{force_h} if exists $widget->{force_h};
3150
3151 if ($widget->{req_w} != $w || $widget->{req_h} != $h
3152 || delete $widget->{force_realloc}) {
3153 $widget->{req_w} = $w;
3154 $widget->{req_h} = $h;
3155
3156 $self->{size_alloc}{$widget+0} = $widget;
3157
3158 if (my $parent = $widget->{parent}) {
3159 $self->{realloc}{$parent+0} = $parent;
3160 #unshift @queue, $parent;
3161 $parent->{force_size_alloc} = 1;
3162 $self->{size_alloc}{$parent+0} = $parent;
3163 }
3164 }
3165
3166 delete $self->{realloc}{$widget+0};
3167 }
3168 }
3169
3170 while (my $size_alloc = delete $self->{size_alloc}) {
3171 my @queue = sort { $b->{visible} <=> $a->{visible} }
3172 values %$size_alloc;
3173
3174 while () {
3175 my $widget = pop @queue || last;
3176
3177 my ($w, $h) = @$widget{qw(alloc_w alloc_h)};
3178
3179 $w = 0 if $w < 0;
3180 $h = 0 if $h < 0;
3181
3182 $w = int $w + 0.5;
3183 $h = int $h + 0.5;
3184
3185 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) {
3186 $widget->{w} = $w;
3187 $widget->{h} = $h;
3188
3189 $widget->emit (size_allocate => $w, $h);
3190 }
3191 }
3192 }
3193
3194 while ($self->{post_alloc_hook}) {
3195 $_->()
3196 for values %{delete $self->{post_alloc_hook}};
3197 }
3198
3199
3200 glViewport 0, 0, $::WIDTH, $::HEIGHT;
3201 glClearColor +($::CFG->{fow_intensity}) x 3, 1;
3202 glClear GL_COLOR_BUFFER_BIT;
3203
3204 glMatrixMode GL_PROJECTION;
3205 glLoadIdentity;
3206 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000;
3207 glMatrixMode GL_MODELVIEW;
3208 glLoadIdentity;
3209
3210 $self->_draw;
3211} 3025}
3212 3026
3213############################################################################# 3027#############################################################################
3214 3028
3215package CFClient::UI::BindEditor; 3029package CFClient::UI::BindEditor;
3221 3035
3222 my $self = $class->SUPER::new (binding => [], commands => [], @_); 3036 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3223 3037
3224 $self->add (my $vb = new CFClient::UI::VBox); 3038 $self->add (my $vb = new CFClient::UI::VBox);
3225 3039
3226 $vb->add (my $hb = new CFClient::UI::HBox);
3227 $hb->add (new CFClient::UI::Label text => "Key: ");
3228 $hb->add ($self->{keylbl} = new CFClient::UI::Label);
3229 $hb->add (new CFClient::UI::Button
3230 text => "bind",
3231 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
3232 on_activate => sub {
3233 $self->ask_for_bind;
3234 });
3235 3040
3236 $vb->add (new CFClient::UI::Label text => "Actions:");
3237 $vb->add ($self->{rec_btn} = new CFClient::UI::Button 3041 $vb->add ($self->{rec_btn} = new CFClient::UI::Button
3238 text => "start recording", 3042 text => "start recording",
3239 tooltip => "Start/Stops recording of actions." 3043 tooltip => "Start/Stops recording of actions."
3240 ."All subsequent actions after the recording started will be captured." 3044 ."All subsequent actions after the recording started will be captured."
3241 ."The actions are displayed after the record was stopped." 3045 ."The actions are displayed after the record was stopped."
3246 } else { 3050 } else {
3247 $self->stop; 3051 $self->stop;
3248 } 3052 }
3249 }); 3053 });
3250 3054
3055 $vb->add (new CFClient::UI::Label text => "Actions:");
3251 $vb->add ($self->{cmdbox} = new CFClient::UI::VBox); 3056 $vb->add ($self->{cmdbox} = new CFClient::UI::VBox);
3057
3058 $vb->add (new CFClient::UI::Label text => "Bound to: ");
3059 $vb->add (my $hb = new CFClient::UI::HBox);
3060 $hb->add ($self->{keylbl} = new CFClient::UI::Label expand => 1);
3061 $hb->add (new CFClient::UI::Button
3062 text => "bind",
3063 tooltip => "This opens a query where you have to press the key combination to bind the recorded actions",
3064 on_activate => sub {
3065 $self->ask_for_bind;
3066 });
3252 3067
3253 $vb->add (my $hb = new CFClient::UI::HBox); 3068 $vb->add (my $hb = new CFClient::UI::HBox);
3254 $hb->add (new CFClient::UI::Button 3069 $hb->add (new CFClient::UI::Button
3255 text => "ok", 3070 text => "ok",
3071 expand => 1,
3256 tooltip => "This closes the binding editor and saves the binding", 3072 tooltip => "This closes the binding editor and saves the binding",
3257 on_activate => sub { 3073 on_activate => sub {
3258 $self->hide; 3074 $self->hide;
3259 $self->commit; 3075 $self->commit;
3260 }); 3076 });
3261 3077
3262 $hb->add (new CFClient::UI::Button 3078 $hb->add (new CFClient::UI::Button
3263 text => "cancel", 3079 text => "cancel",
3080 expand => 1,
3264 tooltip => "This closes the binding editor without saving", 3081 tooltip => "This closes the binding editor without saving",
3265 on_activate => sub { 3082 on_activate => sub {
3266 $self->hide; 3083 $self->hide;
3267 $self->{binding_cancel}->() 3084 $self->{binding_cancel}->()
3268 if $self->{binding_cancel}; 3085 if $self->{binding_cancel};
3280 $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym) 3097 $::STATUSBOX->add ("Bound actions to '".CFClient::Binder::keycombo_to_name ($mod, $sym)
3281 ."'. Don't forget 'Save Config'!"); 3098 ."'. Don't forget 'Save Config'!");
3282 $self->{binding_change}->($mod, $sym, $cmds) 3099 $self->{binding_change}->($mod, $sym, $cmds)
3283 if $self->{binding_change}; 3100 if $self->{binding_change};
3284 } else { 3101 } else {
3285 $::STATUSBOX->add ("No action bound, no keys specified!"); 3102 $::STATUSBOX->add ("No action bound, no key or action specified!");
3286 $self->{binding_cancel}->() 3103 $self->{binding_cancel}->()
3287 if $self->{binding_cancel}; 3104 if $self->{binding_cancel};
3288 } 3105 }
3289} 3106}
3290 3107
3307 $rec = $::CONN->stop_record if $::CONN; 3124 $rec = $::CONN->stop_record if $::CONN;
3308 return unless ref $rec eq 'ARRAY'; 3125 return unless ref $rec eq 'ARRAY';
3309 $self->set_command_list ($rec); 3126 $self->set_command_list ($rec);
3310} 3127}
3311 3128
3129# if $commit is true, the binding will be set after the user entered a key combo
3312sub ask_for_bind { 3130sub ask_for_bind {
3313 my ($self, $commit) = @_; 3131 my ($self, $commit) = @_;
3314 3132
3315 CFClient::Binder::open_binding_dialog (sub { 3133 CFClient::Binder::open_binding_dialog (sub {
3316 my ($mod, $sym) = @_; 3134 my ($mod, $sym) = @_;
3318 $self->update_binding_widgets; 3136 $self->update_binding_widgets;
3319 $self->commit if $commit; 3137 $self->commit if $commit;
3320 }); 3138 });
3321} 3139}
3322 3140
3141# $mod and $sym are the modifiers and key symbol
3142# $cmds is a array ref of strings (the commands)
3143# $cb is the callback that is executed on OK
3144# $ccb is the callback that is executed on CANCEL and
3145# when the binding was unsuccessful on OK
3323sub set_binding { 3146sub set_binding {
3324 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_; 3147 my ($self, $mod, $sym, $cmds, $cb, $ccb) = @_;
3325 3148
3326 $self->clear_command_list; 3149 $self->clear_command_list;
3327 $self->{recording} = 0; 3150 $self->{recording} = 0;
3332 3155
3333 $self->{binding_change} = $cb; 3156 $self->{binding_change} = $cb;
3334 $self->{binding_cancel} = $ccb; 3157 $self->{binding_cancel} = $ccb;
3335 3158
3336 $self->update_binding_widgets; 3159 $self->update_binding_widgets;
3160}
3161
3162# this is a shortcut method that asks for a binding
3163# and then just binds it.
3164sub do_quick_binding {
3165 my ($self, $cmds) = @_;
3166 $self->set_binding (undef, undef, $cmds, sub {
3167 $::CFG->{bindings}->{$_[0]}->{$_[1]} = $_[2];
3168 });
3169 $self->ask_for_bind (1);
3337} 3170}
3338 3171
3339sub update_binding_widgets { 3172sub update_binding_widgets {
3340 my ($self) = @_; 3173 my ($self) = @_;
3341 my ($mod, $sym, $cmds) = $self->get_binding; 3174 my ($mod, $sym, $cmds) = $self->get_binding;
3367 3200
3368 for (@$cmds) { 3201 for (@$cmds) {
3369 $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox); 3202 $self->{cmdbox}->add (my $hb = new CFClient::UI::HBox);
3370 3203
3371 my $i = $idx; 3204 my $i = $idx;
3205 $hb->add (new CFClient::UI::Label text => $_);
3372 $hb->add (new CFClient::UI::Button 3206 $hb->add (new CFClient::UI::Button
3373 text => "delete", 3207 text => "delete",
3374 tooltip => "Deletes the action from the record", 3208 tooltip => "Deletes the action from the record",
3375 on_activate => sub { 3209 on_activate => sub {
3376 $self->{cmdbox}->remove ($hb); 3210 $self->{cmdbox}->remove ($hb);
3377 $cmds->[$i] = undef; 3211 $cmds->[$i] = undef;
3378 }); 3212 });
3379 3213
3380 $hb->add (new CFClient::UI::Label text => $_);
3381 3214
3382 $idx++ 3215 $idx++
3383 } 3216 }
3384} 3217}
3385 3218
3219#############################################################################
3220
3221package CFClient::UI::SpellList;
3222
3223our @ISA = CFClient::UI::FancyFrame::;
3224
3225sub new {
3226 my $class = shift;
3227
3228 my $self = $class->SUPER::new (binding => [], commands => [], @_);
3229
3230 $self->add (new CFClient::UI::ScrolledWindow
3231 scrolled => $self->{spellbox} = new CFClient::UI::Table);
3232
3233 $self;
3234}
3235
3236# XXX: Do sorting? Argl...
3237sub add_spell {
3238 my ($self, $spell) = @_;
3239 $self->{spells}->{$spell->{name}} = $spell;
3240
3241 $self->{spellbox}->add (0, $self->{tbl_idx}, new CFClient::UI::Face
3242 face => $spell->{face},
3243 can_hover => 1,
3244 can_events => 1,
3245 tooltip => $spell->{message});
3246
3247 $self->{spellbox}->add (1, $self->{tbl_idx}, new CFClient::UI::Label
3248 text => $spell->{name},
3249 can_hover => 1,
3250 can_events => 1,
3251 tooltip => $spell->{message},
3252 expand => 1);
3253
3254 $self->{spellbox}->add (2, $self->{tbl_idx}, new CFClient::UI::Label
3255 text => (sprintf "lvl: %2d sp: %2d dmg: %2d",
3256 $spell->{level}, ($spell->{mana} || $spell->{grace}), $spell->{damage}),
3257 expand => 1);
3258
3259 $self->{spellbox}->add (3, $self->{tbl_idx}++, new CFClient::UI::Button
3260 text => "bind to key",
3261 on_activate => sub { $::BIND_EDITOR->do_quick_binding (["cast $spell->{name}"]) });
3262}
3263
3264sub rebuild_spell_list {
3265 my ($self) = @_;
3266 $self->{tbl_idx} = 0;
3267 $self->add_spell ($_) for values %{$self->{spells}};
3268}
3269
3270sub remove_spell {
3271 my ($self, $spell) = @_;
3272 delete $self->{spells}->{$spell->{name}};
3273 $self->rebuild_spell_list;
3274}
3275
3276#############################################################################
3277
3278package CFClient::UI::Root;
3279
3280our @ISA = CFClient::UI::Container::;
3281
3282use CFClient::OpenGL;
3283
3284sub new {
3285 my $class = shift;
3286
3287 my $self = $class->SUPER::new (
3288 visible => 1,
3289 @_,
3290 );
3291
3292 Scalar::Util::weaken ($self->{root} = $self);
3293
3294 $self
3295}
3296
3297sub size_request {
3298 my ($self) = @_;
3299
3300 ($self->{w}, $self->{h})
3301}
3302
3303sub _to_pixel {
3304 my ($coord, $size, $max) = @_;
3305
3306 $coord =
3307 $coord eq "center" ? ($max - $size) * 0.5
3308 : $coord eq "max" ? $max
3309 : $coord;
3310
3311 $coord = 0 if $coord < 0;
3312 $coord = $max - $size if $coord > $max - $size;
3313
3314 int $coord + 0.5
3315}
3316
3317sub size_allocate {
3318 my ($self, $w, $h) = @_;
3319
3320 for my $child ($self->children) {
3321 my ($X, $Y, $W, $H) = @$child{qw(x y req_w req_h)};
3322
3323 $X = $child->{force_x} if exists $child->{force_x};
3324 $Y = $child->{force_y} if exists $child->{force_y};
3325
3326 $X = _to_pixel $X, $W, $self->{w};
3327 $Y = _to_pixel $Y, $H, $self->{h};
3328
3329 $child->configure ($X, $Y, $W, $H);
3330 }
3331}
3332
3333sub coord2local {
3334 my ($self, $x, $y) = @_;
3335
3336 ($x, $y)
3337}
3338
3339sub coord2global {
3340 my ($self, $x, $y) = @_;
3341
3342 ($x, $y)
3343}
3344
3345sub update {
3346 my ($self) = @_;
3347
3348 $::WANT_REFRESH++;
3349}
3350
3351sub add {
3352 my ($self, @children) = @_;
3353
3354 $_->{is_toplevel} = 1
3355 for @children;
3356
3357 $self->SUPER::add (@children);
3358}
3359
3360sub remove {
3361 my ($self, @children) = @_;
3362
3363 $self->SUPER::remove (@children);
3364
3365 delete $self->{is_toplevel}
3366 for @children;
3367
3368 while (@children) {
3369 my $w = pop @children;
3370 push @children, $w->children;
3371 $w->set_invisible;
3372 }
3373}
3374
3375sub on_refresh {
3376 my ($self, $id, $cb) = @_;
3377
3378 $self->{refresh_hook}{$id} = $cb;
3379}
3380
3381sub on_post_alloc {
3382 my ($self, $id, $cb) = @_;
3383
3384 $self->{post_alloc_hook}{$id} = $cb;
3385}
3386
3387sub draw {
3388 my ($self) = @_;
3389
3390 while ($self->{refresh_hook}) {
3391 $_->()
3392 for values %{delete $self->{refresh_hook}};
3393 }
3394
3395 if ($self->{realloc}) {
3396 my %queue;
3397 my @queue;
3398 my $widget;
3399
3400 outer:
3401 while () {
3402 if (my $realloc = delete $self->{realloc}) {
3403 for $widget (values %$realloc) {
3404 $widget->{visible} or next; # do not resize invisible widgets
3405
3406 $queue{$widget+0}++ and next; # duplicates are common
3407
3408 push @{ $queue[$widget->{visible}] }, $widget;
3409 }
3410 }
3411
3412 while () {
3413 @queue or last outer;
3414
3415 $widget = pop @{ $queue[-1] || [] }
3416 and last;
3417
3418 pop @queue;
3419 }
3420
3421 delete $queue{$widget+0};
3422
3423 my ($w, $h) = $widget->size_request;
3424
3425 $w = List::Util::max $widget->{min_w}, $w + $widget->{padding_x} * 2;
3426 $h = List::Util::max $widget->{min_h}, $h + $widget->{padding_y} * 2;
3427
3428 $w = $widget->{force_w} if exists $widget->{force_w};
3429 $h = $widget->{force_h} if exists $widget->{force_h};
3430
3431 if ($widget->{req_w} != $w || $widget->{req_h} != $h
3432 || delete $widget->{force_realloc}) {
3433 $widget->{req_w} = $w;
3434 $widget->{req_h} = $h;
3435
3436 $self->{size_alloc}{$widget+0} = $widget;
3437
3438 if (my $parent = $widget->{parent}) {
3439 $self->{realloc}{$parent+0} = $parent
3440 unless $queue{$parent+0};
3441
3442 $parent->{force_size_alloc} = 1;
3443 $self->{size_alloc}{$parent+0} = $parent;
3444 }
3445 }
3446
3447 delete $self->{realloc}{$widget+0};
3448 }
3449 }
3450
3451 while (my $size_alloc = delete $self->{size_alloc}) {
3452 my @queue = sort { $b->{visible} <=> $a->{visible} }
3453 values %$size_alloc;
3454
3455 while () {
3456 my $widget = pop @queue || last;
3457
3458 my ($w, $h) = @$widget{qw(alloc_w alloc_h)};
3459
3460 $w = 0 if $w < 0;
3461 $h = 0 if $h < 0;
3462
3463 $w = int $w + 0.5;
3464 $h = int $h + 0.5;
3465
3466 if ($widget->{w} != $w || $widget->{h} != $h || delete $widget->{force_size_alloc}) {
3467 $widget->{old_w} = $widget->{w};
3468 $widget->{old_h} = $widget->{h};
3469
3470 $widget->{w} = $w;
3471 $widget->{h} = $h;
3472
3473 $widget->emit (size_allocate => $w, $h);
3474 }
3475 }
3476 }
3477
3478 while ($self->{post_alloc_hook}) {
3479 $_->()
3480 for values %{delete $self->{post_alloc_hook}};
3481 }
3482
3483
3484 glViewport 0, 0, $::WIDTH, $::HEIGHT;
3485 glClearColor +($::CFG->{fow_intensity}) x 3, 1;
3486 glClear GL_COLOR_BUFFER_BIT;
3487
3488 glMatrixMode GL_PROJECTION;
3489 glLoadIdentity;
3490 glOrtho 0, $::WIDTH, $::HEIGHT, 0, -10000, 10000;
3491 glMatrixMode GL_MODELVIEW;
3492 glLoadIdentity;
3493
3494 {
3495 package CFClient::UI::Base;
3496
3497 ($draw_x, $draw_y, $draw_w, $draw_h) =
3498 (0, 0, $self->{w}, $self->{h});
3499 }
3500
3501 $self->_draw;
3502}
3386 3503
3387############################################################################# 3504#############################################################################
3388 3505
3389package CFClient::UI; 3506package CFClient::UI;
3390 3507

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines