ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/ext/widget.ext
Revision: 1.19
Committed: Sat Sep 1 07:22:20 2007 UTC (16 years, 8 months ago) by root
Branch: MAIN
CVS Tags: rel-2_2, rel-2_3, rel-2_32
Changes since 1.18: +76 -31 lines
Log Message:
simpler, more powerful and slightly less compact widget protocol

File Contents

# User Rev Content
1 root 1.1 #! perl # mandatory depends=login
2    
3     # sends the following ext message types
4 root 1.17 # ws_a id name... # associate well-known widget with given id
5 root 1.14 # ws_n ws # widgetset new
6     # ws_d ws # widgetset destroy
7 root 1.17 # ws_c ws id class @args # widgetset create
8     # w_c id rid name @args # widget method call
9 root 1.10 # w_s id @attr # widget member set
10     # w_g id rid @attr # widget member get
11 root 1.1 #
12     # and expects the following exti message types
13 root 1.19 # w_e id @args # widget_call
14 root 1.1
15 root 1.13 our $DEBUG = 1;
16    
17 root 1.1 cf::client->attach (
18     on_connect => sub {
19     my ($ns) = @_;
20    
21 root 1.4 Scalar::Util::weaken (my $weakns = $ns);
22    
23 root 1.1 $ns->{id} = "a";
24 root 1.19 $ns->{json_coder}->filter_json_single_key_object ("\fw" => sub {
25     $weakns->{widget}{$_[0]}
26 root 1.4 });
27 root 1.1 },
28     );
29    
30 root 1.2 sub csc_update_stats {
31     my ($ns) = @_;
32    
33     while (my ($k, $v) = each %{ $ns->{csc}{stat} }) {
34     $v->set_text ($ns->pl->ob->stats->$k);
35     }
36     }
37    
38 root 1.17 sub demo_start {
39     my ($ns) = @_;
40    
41     my $ws = $ns->{csc} = $ns->new_widgetset;
42    
43     $ws->{tab} = $ws->new (Label => text => "dumb tst", c_tab => ["hull"]);
44    
45     $ws->find ("setup_notebook")->add ($ws->{tab});
46     $ws->find ("setup_dialog")->toggle_visibility;
47     }
48    
49 root 1.2 sub csc_start {
50     my ($ns) = @_;
51    
52     my $ws = $ns->{csc} = $ns->new_widgetset;
53    
54     my $w = $ws->new (Toplevel =>
55     min_w => 600,
56     min_h => 400,
57     x => "center",
58     y => "center",
59     title => "Character Creation",
60 root 1.3 has_close_button => 1,
61     on_delete => sub {
62     $ws->destroy;
63     },
64 root 1.2 );
65    
66     $w->add (my $ntb = $ws->new (Notebook => expand => 1));
67    
68 root 1.17 $ntb->add_tab (Statistics => (my $stats = $ws->new (Table => expand => 1)), "Basic statistics of your new character");
69 root 1.2
70 root 1.17 $stats->add_at (0, 0, (my $statstable = $ws->new ("Table")));
71 root 1.2
72     for (
73 root 1.3 [0, "Str"],
74     [1, "Dex"],
75     [2, "Con"],
76     [3, "Int"],
77     [4, "Wis"],
78     [5, "Pow"],
79     [6, "Cha"],
80 root 1.2 ) {
81 root 1.3 my ($x, $label) = @$_;
82 root 1.2
83 root 1.9 $statstable->add_at ($x, 0, $ws->new (Label =>
84 root 1.2 can_hover => 1, can_events => 1,
85     align => +1, text => $label, tooltip => "#stat_$label",
86     ));
87 root 1.9 $statstable->add_at ($x, 1, $ws->{stat}{$label} = $ws->new (Label =>
88 root 1.2 can_hover => 1, can_events => 1,
89     align => +1, template => "88", tooltip => "#stat_$label",
90     ));
91     }
92    
93     csc_update_stats $ns;
94    
95 root 1.17 $ws->{tl} = $w;
96 root 1.2 $w->show;
97     }
98    
99 root 1.1 cf::player->attach (
100     on_login => sub {
101     my ($pl) = @_;
102    
103 root 1.8 return unless $cf::CFG{devel};
104 root 1.1
105     my $ns = $pl->ns;
106    
107 root 1.2 return unless $ns->{can_widget};
108 root 1.5 #csc_start $ns;
109 root 1.18 #demo_start $ns;
110 root 1.1 },
111     );
112    
113     cf::register_exticmd w_e => sub {
114 root 1.19 my ($ns, $id, @args) = @_;
115 root 1.1
116 root 1.19 if (my $cb = $ns->{widget_cb}{$id}) {
117     $cb->(@args);
118 root 1.1 }
119    
120     ()
121     };
122    
123     sub cf::client::new_widgetset {
124     my ($self) = @_;
125    
126     my $id = ++$self->{id};
127    
128     my $ws = bless {
129     id => $id,
130     ns => $self,
131 root 1.12 _w => {},
132 root 1.1 }, "ext::widget::set";
133    
134 root 1.14 $ws->msg (ws_n => $id);
135 root 1.1
136     $ws
137     }
138    
139 root 1.19 sub cf::client::alloc_wid {
140     pop @{ $_[0]{ids} }
141     or ++$_[0]{id}
142     }
143    
144     sub cf::client::free_wid {
145     push @{ $_[0]{ids} }, $_[1];
146     }
147    
148 root 1.1 #############################################################################
149    
150     package ext::widget::set;
151    
152     sub DESTROY {
153     $_[0]->destroy;
154     }
155    
156     sub destroy {
157     my ($self) = @_;
158    
159 root 1.14 $self->msg (ws_d => $self->{id});
160 root 1.1 delete $self->{ns};
161     $_->destroy
162     for values %{ $self->{w} };
163     }
164    
165     sub msg {
166 root 1.14 my ($self, @msg) = @_;
167 root 1.1
168     if (my $ns = shift->{ns}) {
169 root 1.14 warn "msg " . $ns->{json_coder}->encode (\@msg) if $DEBUG;#d#
170     $ns->send_packet ("ext " . $ns->{json_coder}->encode (\@msg));
171 root 1.1 }
172     }
173    
174 root 1.17 sub alloc {
175     my ($self) = @_;
176 root 1.1
177 root 1.19 my $id = $self->{ns}->alloc_wid;
178 root 1.1
179 root 1.13 my $proxy = bless {
180 root 1.1 id => $id,
181     }, "ext::widget::proxy";
182    
183     Scalar::Util::weaken ($proxy->{ns} = $self->{ns});
184     Scalar::Util::weaken ($self->{ns}{widget}{$id} = $proxy);
185    
186 root 1.17 $proxy
187     }
188    
189     sub new {
190     my ($self, $class, %args) = @_;
191    
192     my $proxy = $self->alloc;
193    
194     Scalar::Util::weaken ($self->{_w}{$proxy->{id}} = $proxy);
195     Scalar::Util::weaken ($proxy->{ws} = $self);
196    
197 root 1.1 for my $ev (grep /^on_/, keys %args) {
198 root 1.19 $args{$ev} = $proxy->{"_$ev"} = $proxy->cb ($args{$ev});
199 root 1.1 }
200    
201     $self->msg (ws_c =>
202 root 1.16 $self->{id},
203 root 1.17 $proxy->{id},
204 root 1.14 $class,
205     \%args,
206 root 1.1 );
207    
208     $proxy
209     }
210    
211 root 1.17 sub find {
212     my ($self, @names) = @_;
213    
214     my @res;
215     my @alloc;
216    
217     for my $name (@names) {
218     push @res, $self->{ns}{widget_wkw}{$name} ||= do {
219     my $proxy = $self->alloc;
220    
221     push @alloc, $proxy->{id} => $name;
222    
223     $proxy
224     };
225     }
226    
227     $self->msg (ws_a => @alloc)
228     if @alloc;
229    
230     wantarray ? @res : $res[0]
231     }
232    
233 root 1.1 #############################################################################
234    
235     package ext::widget::proxy;
236    
237     sub DESTROY {
238     my ($self) = @_;
239    
240     delete $self->{ns}{widget}{$self->{id}};
241    
242     if (my $ws = $self->{ws}) {
243 root 1.14 $self->msg (w_c => 0, "destroy");
244 root 1.12 delete $ws->{_w}{$self->{id}};
245 root 1.1 }
246     }
247    
248 root 1.19 sub cb {
249     my ($self, $cb) = @_;
250    
251     my $proxy = bless {
252     ns => $self->{ns},
253     id => $self->{ns}->alloc_wid,
254     }, "ext::widget::callback";
255    
256     Scalar::Util::weaken $proxy->{ns};
257    
258     $self->{ns}{widget_cb}{$proxy->{id}} = $cb;
259    
260     $proxy
261     }
262    
263     sub oneshot_cb {
264     my ($self, $cb) = @_;
265    
266     if ("CODE" eq ref $cb) {
267     my $ocb = $cb;
268     $cb = $self->cb (sub {
269     undef $cb;
270     &$ocb
271     });
272     }
273    
274     $cb
275     }
276    
277 root 1.1 sub msg {
278 root 1.14 my ($self, $type, @arg) = @_;
279 root 1.1
280 root 1.17 if (my $ns = $self->{ns}) {
281     my @msg = ($type, $self->{id}, @arg);
282     warn "MSG " . $ns->{json_coder}->encode (\@msg) if $DEBUG;#d#
283     $ns->send_packet ("ext " . $ns->{json_coder}->encode (\@msg));
284 root 1.1 }
285     }
286    
287     sub msg_cb {
288 root 1.14 my ($self, $cb, $type, @arg) = @_;
289 root 1.1
290     if (my $ws = $self->{ws}) {
291 root 1.19 my $rid = $ws->{ns}->alloc_wid;
292 root 1.1
293 root 1.19 if ($cb) {
294     $ws->{ns}{widget_cb}{$rid} = sub {
295     delete $ws->{ns}{widget_cb}{$rid};
296     $ws->{ns}->free_wid ($rid);
297     &$cb
298     };
299 root 1.1
300 root 1.19 $self->msg ($type, $rid, @arg);
301 root 1.1 } else {
302     # synchronous case
303     my $wait = new Coro::Signal;
304     my @res;
305    
306 root 1.19 $ws->{ns}{widget_cb}{$rid} = sub {
307     delete $ws->{ns}{widget_cb}{$rid};
308     $ws->{ns}->free_wid ($rid);
309    
310 root 1.1 @res = @_;
311     $wait->send;
312     };
313 root 1.19 $self->msg ($type, $rid, @arg);
314 root 1.1 $wait->wait;
315    
316     return @res;
317     }
318     }
319    
320     ()
321     }
322    
323     sub set {
324 root 1.12 my ($self, @kv) = @_;
325 root 1.1
326 root 1.14 $self->msg (w_s => \@kv);
327 root 1.1 }
328    
329     sub get {
330     my ($self, $member, $cb) = @_;
331    
332 root 1.19 $self->msg_cb ($cb, w_g => ref $member ? @$member : $member);
333 root 1.1 }
334    
335     sub TO_JSON {
336 root 1.19 { "\fw" => $_[0]{id} }
337 root 1.1 }
338    
339     our $AUTOLOAD;
340    
341     sub AUTOLOAD {
342     $AUTOLOAD =~ s/^.*://
343     or return;
344    
345     my $self = shift;
346    
347 root 1.17 #TODO: handle non-void context
348     $self->msg (w_c => 0, $AUTOLOAD, @_);
349 root 1.1
350     ()
351     }
352    
353 root 1.19 package ext::widget::callback;
354    
355     sub DESTROY {
356     my ($self) = @_;
357    
358     if (my $ns = $self->{ns}) {
359     delete $ns->{widget_cb}{$self->{id}};
360     $ns->free_wid ($self->{id});
361     }
362     }
363    
364     sub TO_JSON {
365     { "\fc" => $_[0]{id} }
366     }
367