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