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

# Content
1 #! perl # mandatory depends=login
2
3 # sends the following ext message types
4 # ws_a id name... # associate well-known widget with given id
5 # ws_n ws # widgetset new
6 # ws_d ws # widgetset destroy
7 # ws_c ws id class @args # widgetset create
8 # w_c id rid name @args # widget method call
9 # w_s id @attr # widget member set
10 # w_g id rid @attr # widget member get
11 #
12 # and expects the following exti message types
13 # w_e id @args # widget_call
14
15 our $DEBUG = 1;
16
17 cf::client->attach (
18 on_connect => sub {
19 my ($ns) = @_;
20
21 Scalar::Util::weaken (my $weakns = $ns);
22
23 $ns->{id} = "a";
24 $ns->{json_coder}->filter_json_single_key_object ("\fw" => sub {
25 $weakns->{widget}{$_[0]}
26 });
27 },
28 );
29
30 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 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 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 has_close_button => 1,
61 on_delete => sub {
62 $ws->destroy;
63 },
64 );
65
66 $w->add (my $ntb = $ws->new (Notebook => expand => 1));
67
68 $ntb->add_tab (Statistics => (my $stats = $ws->new (Table => expand => 1)), "Basic statistics of your new character");
69
70 $stats->add_at (0, 0, (my $statstable = $ws->new ("Table")));
71
72 for (
73 [0, "Str"],
74 [1, "Dex"],
75 [2, "Con"],
76 [3, "Int"],
77 [4, "Wis"],
78 [5, "Pow"],
79 [6, "Cha"],
80 ) {
81 my ($x, $label) = @$_;
82
83 $statstable->add_at ($x, 0, $ws->new (Label =>
84 can_hover => 1, can_events => 1,
85 align => +1, text => $label, tooltip => "#stat_$label",
86 ));
87 $statstable->add_at ($x, 1, $ws->{stat}{$label} = $ws->new (Label =>
88 can_hover => 1, can_events => 1,
89 align => +1, template => "88", tooltip => "#stat_$label",
90 ));
91 }
92
93 csc_update_stats $ns;
94
95 $ws->{tl} = $w;
96 $w->show;
97 }
98
99 cf::player->attach (
100 on_login => sub {
101 my ($pl) = @_;
102
103 return unless $cf::CFG{devel};
104
105 my $ns = $pl->ns;
106
107 return unless $ns->{can_widget};
108 #csc_start $ns;
109 #demo_start $ns;
110 },
111 );
112
113 cf::register_exticmd w_e => sub {
114 my ($ns, $id, @args) = @_;
115
116 if (my $cb = $ns->{widget_cb}{$id}) {
117 $cb->(@args);
118 }
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 _w => {},
132 }, "ext::widget::set";
133
134 $ws->msg (ws_n => $id);
135
136 $ws
137 }
138
139 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 #############################################################################
149
150 package ext::widget::set;
151
152 sub DESTROY {
153 $_[0]->destroy;
154 }
155
156 sub destroy {
157 my ($self) = @_;
158
159 $self->msg (ws_d => $self->{id});
160 delete $self->{ns};
161 $_->destroy
162 for values %{ $self->{w} };
163 }
164
165 sub msg {
166 my ($self, @msg) = @_;
167
168 if (my $ns = shift->{ns}) {
169 warn "msg " . $ns->{json_coder}->encode (\@msg) if $DEBUG;#d#
170 $ns->send_packet ("ext " . $ns->{json_coder}->encode (\@msg));
171 }
172 }
173
174 sub alloc {
175 my ($self) = @_;
176
177 my $id = $self->{ns}->alloc_wid;
178
179 my $proxy = bless {
180 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 $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 for my $ev (grep /^on_/, keys %args) {
198 $args{$ev} = $proxy->{"_$ev"} = $proxy->cb ($args{$ev});
199 }
200
201 $self->msg (ws_c =>
202 $self->{id},
203 $proxy->{id},
204 $class,
205 \%args,
206 );
207
208 $proxy
209 }
210
211 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 #############################################################################
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 $self->msg (w_c => 0, "destroy");
244 delete $ws->{_w}{$self->{id}};
245 }
246 }
247
248 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 sub msg {
278 my ($self, $type, @arg) = @_;
279
280 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 }
285 }
286
287 sub msg_cb {
288 my ($self, $cb, $type, @arg) = @_;
289
290 if (my $ws = $self->{ws}) {
291 my $rid = $ws->{ns}->alloc_wid;
292
293 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
300 $self->msg ($type, $rid, @arg);
301 } else {
302 # synchronous case
303 my $wait = new Coro::Signal;
304 my @res;
305
306 $ws->{ns}{widget_cb}{$rid} = sub {
307 delete $ws->{ns}{widget_cb}{$rid};
308 $ws->{ns}->free_wid ($rid);
309
310 @res = @_;
311 $wait->send;
312 };
313 $self->msg ($type, $rid, @arg);
314 $wait->wait;
315
316 return @res;
317 }
318 }
319
320 ()
321 }
322
323 sub set {
324 my ($self, @kv) = @_;
325
326 $self->msg (w_s => \@kv);
327 }
328
329 sub get {
330 my ($self, $member, $cb) = @_;
331
332 $self->msg_cb ($cb, w_g => ref $member ? @$member : $member);
333 }
334
335 sub TO_JSON {
336 { "\fw" => $_[0]{id} }
337 }
338
339 our $AUTOLOAD;
340
341 sub AUTOLOAD {
342 $AUTOLOAD =~ s/^.*://
343 or return;
344
345 my $self = shift;
346
347 #TODO: handle non-void context
348 $self->msg (w_c => 0, $AUTOLOAD, @_);
349
350 ()
351 }
352
353 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