quick fix for value issue
[catagits/Reaction.git] / lib / Reaction / UI / ViewPort.pm
CommitLineData
7adfd53f 1package Reaction::UI::ViewPort;
2
3use Reaction::Class;
36d54b14 4use Scalar::Util qw/blessed/;
7adfd53f 5
6class ViewPort which {
7
8 has location => (isa => 'Str', is => 'rw', required => 1);
9 has layout => (isa => 'Str', is => 'rw', lazy_build => 1);
10 has outer => (isa => 'Reaction::UI::ViewPort', is => 'rw', weak_ref => 1);
11 has inner => (isa => 'Reaction::UI::ViewPort', is => 'rw');
12 has focus_stack => (
13 isa => 'Reaction::UI::FocusStack', is => 'rw', weak_ref => 1
14 );
15 has _tangent_stacks => (
16 isa => 'HashRef', is => 'ro', default => sub { {} }
17 );
18 has ctx => (isa => 'Catalyst', is => 'ro', required => 1);
b8faba69 19
89939ff9 20 implements _build_layout => as {
7adfd53f 21 '';
22 };
b8faba69 23
7adfd53f 24 implements create_tangent => as {
25 my ($self, $name) = @_;
26 my $t_map = $self->_tangent_stacks;
27 if (exists $t_map->{$name}) {
28 confess "Can't create tangent with already existing name ${name}";
29 }
30 my $loc = join('.', $self->location, $name);
31 my $tangent = Reaction::UI::FocusStack->new(loc_prefix => $loc);
32 $t_map->{$name} = $tangent;
33 return $tangent;
34 };
b8faba69 35
7adfd53f 36 implements focus_tangent => as {
37 my ($self, $name) = @_;
38 if (my $tangent = $self->_tangent_stacks->{$name}) {
39 return $tangent;
40 } else {
41 return;
42 }
43 };
b8faba69 44
7adfd53f 45 implements focus_tangents => as {
46 return keys %{shift->_tangent_stacks};
47 };
b8faba69 48
7adfd53f 49 implements child_event_sinks => as {
50 my $self = shift;
51 return values %{$self->_tangent_stacks};
52 };
b8faba69 53
7adfd53f 54 implements apply_events => as {
55 my ($self, $ctx, $events) = @_;
56 $self->apply_child_events($ctx, $events);
57 $self->apply_our_events($ctx, $events);
58 };
b8faba69 59
7adfd53f 60 implements apply_child_events => as {
61 my ($self, $ctx, $events) = @_;
62 foreach my $child ($self->child_event_sinks) {
36d54b14 63 confess blessed($child) ."($child) is not a valid object"
64 unless blessed($child) && $child->can('apply_events');
7adfd53f 65 $child->apply_events($ctx, $events);
66 }
67 };
b8faba69 68
7adfd53f 69 implements apply_our_events => as {
70 my ($self, $ctx, $events) = @_;
71 my $loc = $self->location;
72 my %our_events;
73 foreach my $key (keys %$events) {
74 if ($key =~ m/^${loc}:(.*)$/) {
75 $our_events{$1} = $events->{$key};
76 }
77 }
78 if (keys %our_events) {
79 #warn "$self: events ".join(', ', %our_events)."\n";
80 $self->handle_events(\%our_events);
81 }
82 };
b8faba69 83
7adfd53f 84 implements handle_events => as {
85 my ($self, $events) = @_;
86 foreach my $event ($self->accept_events) {
87 if (exists $events->{$event}) {
cc44a337 88 #my $name = eval{$self->name};
89 #$self->ctx->log->debug("Applying Event: $event on $name with value: ". $events->{$event});
7adfd53f 90 $self->$event($events->{$event});
91 }
92 }
93 };
b8faba69 94
7adfd53f 95 implements accept_events => as { () };
b8faba69 96
7adfd53f 97 implements event_id_for => as {
98 my ($self, $name) = @_;
99 return join(':', $self->location, $name);
100 };
b8faba69 101
7adfd53f 102 implements sort_by_spec => as {
103 my ($self, $spec, $items) = @_;
104 return $items if not defined $spec;
b8faba69 105
7adfd53f 106 my @order;
107 if (ref $spec eq 'ARRAY') {
108 @order = @$spec;
109 }
110 elsif (not ref $spec) {
111 return $items unless length $spec;
112 @order = split /\s+/, $spec;
113 }
b8faba69 114
7adfd53f 115 my %order_map = map {$_ => 0} @$items;
116 for my $order_num (0..$#order) {
117 $order_map{ $order[$order_num] } = ($#order - $order_num) + 1;
118 }
b8faba69 119
7adfd53f 120 return [sort {$order_map{$b} <=> $order_map{$a}} @$items];
121 };
122
123};
124
1251;
126
127
128=head1 NAME
129
130Reaction::UI::ViewPort - Page layout building block
131
132=head1 SYNOPSIS
133
134 # Create a new ViewPort:
135 # $stack isa Reaction::UI::FocusStack object
136 my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xthml');
137
138 # Fetch ViewPort higher up the stack (further out)
139 my $outer = $vp->outer();
140
141 # Fetch ViewPort lower down (further in)
142 my $inner = $vp->inner();
143
144 # Create a named tangent stack for this ViewPort
145 my $substack = $vp->create_tangent('name');
146
147 # Retrieve a tangent stack for this ViewPort
148 my $substack = $vp->forcus_tangent('name');
149
150 # Get the names of all the tangent stacks for this ViewPort
151 my @names = $vp->focus_tangents();
152
153 # Fetch all the tangent stacks for this ViewPort
154 # This is called by apply_events
155 my $stacks = $vp->child_event_sinks();
156
157
158 ### The following methods are all called automatically when using
159 ### Reaction::UI::Controller(s)
160 # Resolve current events with this ViewPort
161 $vp->apply_events($ctx, $param_hash);
162
b8faba69 163 # Apply current events to all tangent stacks
7adfd53f 164 # This is called by apply_events
165 $vp->apply_child_events($ctx, $params_hash);
166
167 # Apply current events to this ViewPort
168 # This is called by apply_events
169 $vp->apply_our_events($ctx, $params_hash);
170
171=head1 DESCRIPTION
172
173A ViewPort describes part of a page, it can be a field, a form or
174an entire page. ViewPorts are created on a
175L<Reaction::UI::FocusStack>, usually belonging to a controller or
176another ViewPort. Each ViewPort knows it's own position in the stack
177it is in, as well as the stack containing it.
178
179Each ViewPort has a specific location in the heirarchy of viewports
180making up a page. The hierarchy is determined as follows: The first
181ViewPort in a stack is labeled C<0>, the second is C<1> and so on. If
182a ViewPort is in a named tangent, it's location will contain the name
183of the tangent in it's location.
184
185For example, the first ViewPort in the 'left' tangent of the main
186ViewPort has location C<0.left.0>.
187
188Several ViewPort attributes are set by
189L<Reaction::UI::FocusStack/push_viewport> when new ViewPorts are
190created, these are as follows:
191
192=over
193
194=item Automatic:
195
196=over
197
198=item outer
199
200The outer attribute is set to the previous ViewPort in the stack when
201creating a ViewPort, if the ViewPort is the first in the stack, it
202will be undef.
203
204=item inner
205
206The inner attribute is set to the next ViewPort down in the stack when
207it is created, if this is the last ViewPort in the stack, it will be
208undef.
209
210=item focus_stack
211
212The focus_stack attribute is set to the L<Reaction::UI::FocusStack>
213object that created the ViewPort.
214
215=item ctx
216
217The ctx attribute will be passed automatically when using
218L<Reaction::UI::Controller/push_viewport> to create a ViewPort in the
219base stack of a controller. When creating tangent stacks, you may have
220to pass it in yourself.
221
222=back
223
224=item Optional:
225
226=over
227
228=item location
229
230=item layout
231
232The layout attribute can either be specifically passed when calling
233C<push_viewport>, or it will be determined using the last part of the
234ViewPorts classname.
235
236=item column_order
237
238This is generally used by more specialised ViewPorts such as the
239L<ListView|Reaction::UI::ViewPort::ListView> or
240L<ActionForm|Reaction::UI::ViewPort::ActionForm>. It can be either a
241space separated list of column names, or an arrayref of column names.
242
243=back
244
245=back
246
247=head1 METHODS
248
249=head2 outer
250
251=over
252
253=item Arguments: none
254
255=back
256
257Fetch the ViewPort outside this one in the page hierarchy.
258
259=head2 inner
260
261=over
262
263=item Arguments: none
264
265=back
266
267Fetch the ViewPort inside this one in the page hierarchy.
268
269=head2 create_tangent
270
271=over
272
273=item Arguments: $tangent_name
274
275=back
276
277Create a new named L<Reaction::UI::FocusStack> inside this
278ViewPort. The created FocusStack is returned.
279
280=head2 focus_tangent
281
282=over
283
284=item Arguments: $tangent_name
285
286=back
287
288Fetch a named FocusStack from this ViewPort.
289
290=head2 focus_tangents
291
292=over
293
294=item Arguments: none
295
296=back
297
298Returns a list of names of all the known tangents in this ViewPort.
299
300=head2 focus_stack
301
302Return the L<Reaction::UI::FocusStack> object that this ViewPort is in.
303
304=head2 apply_events
305
306=over
307
308=item Arguments: $ctx, $params_hashref
309
310=back
311
312This method is called by the FocusStack object to resolve all events
313for the ViewPort.
314
315=head2 apply_child_events
316
317=over
318
319=item Arguments: $ctx, $params_hashref
320
321=back
322
323Resolve the given events for all the tangents of this ViewPort. Called
324by L<apply_events>.
325
326=head2 apply_our_events
327
328=over
329
330=item Arguments: $ctx, $events
331
332=back
333
334Resolve the given events that match the location of this
335ViewPort. Called by L<apply_events>.
336
337=head2 handle_events
338
339=over
340
341=item Arguments: $events
342
343=back
344
345Actually call the event handlers for this ViewPort. Called by
346L<apply_our_events>. By default this will do nothing, subclass
347ViewPort and implement L<accept_events>.
348
349=head2 accept_events
350
351=over
352
353=item Arguments: none
354
355=back
356
357Implement this method in a subclass and return a list of events that
358your ViewPort is accepting.
359
360=head2 event_id_for
361
362=over
363
364=item Arguments: $name
365
366=back
367
368Create an id for the given event name and this ViewPort. Generally
369returns the location and the name, joined with a colon.
370
371=head2 sort_by_spec
372
373=over
374
375=item Arguments: $spec, $items
376
377=back
378
379Sorts the given list of items such that the ones that also appear in
380the spec are at the beginning. This is called by
381L<Reaction::UI::ViewPort::ActionForm> and
382L<Reaction::UI::ViewPort::ListView>, and gets passed L<column_order>
383as the spec argument.
384
385=head1 AUTHORS
386
387See L<Reaction::Class> for authors.
388
389=head1 LICENSE
390
391See L<Reaction::Class> for the license.
392
393=cut