1 package Reaction::UI::ViewPort;
7 has location => (isa => 'Str', is => 'rw', required => 1);
8 has layout => (isa => 'Str', is => 'rw', lazy_build => 1);
9 has outer => (isa => 'Reaction::UI::ViewPort', is => 'rw', weak_ref => 1);
10 has inner => (isa => 'Reaction::UI::ViewPort', is => 'rw');
12 isa => 'Reaction::UI::FocusStack', is => 'rw', weak_ref => 1
14 has _tangent_stacks => (
15 isa => 'HashRef', is => 'ro', default => sub { {} }
17 has ctx => (isa => 'Catalyst', is => 'ro', required => 1);
18 has column_order => (is => 'rw');
20 implements build_layout => as {
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}";
30 my $loc = join('.', $self->location, $name);
31 my $tangent = Reaction::UI::FocusStack->new(loc_prefix => $loc);
32 $t_map->{$name} = $tangent;
36 implements focus_tangent => as {
37 my ($self, $name) = @_;
38 if (my $tangent = $self->_tangent_stacks->{$name}) {
45 implements focus_tangents => as {
46 return keys %{shift->_tangent_stacks};
49 implements child_event_sinks => as {
51 return values %{$self->_tangent_stacks};
54 implements apply_events => as {
55 my ($self, $ctx, $events) = @_;
56 $self->apply_child_events($ctx, $events);
57 $self->apply_our_events($ctx, $events);
60 implements apply_child_events => as {
61 my ($self, $ctx, $events) = @_;
62 foreach my $child ($self->child_event_sinks) {
63 $child->apply_events($ctx, $events);
67 implements apply_our_events => as {
68 my ($self, $ctx, $events) = @_;
69 my $loc = $self->location;
71 foreach my $key (keys %$events) {
72 if ($key =~ m/^${loc}:(.*)$/) {
73 $our_events{$1} = $events->{$key};
76 if (keys %our_events) {
77 #warn "$self: events ".join(', ', %our_events)."\n";
78 $self->handle_events(\%our_events);
82 implements handle_events => as {
83 my ($self, $events) = @_;
84 foreach my $event ($self->accept_events) {
85 if (exists $events->{$event}) {
86 $self->$event($events->{$event});
91 implements accept_events => as { () };
93 implements event_id_for => as {
94 my ($self, $name) = @_;
95 return join(':', $self->location, $name);
98 implements sort_by_spec => as {
99 my ($self, $spec, $items) = @_;
100 return $items if not defined $spec;
103 if (ref $spec eq 'ARRAY') {
106 elsif (not ref $spec) {
107 return $items unless length $spec;
108 @order = split /\s+/, $spec;
111 my %order_map = map {$_ => 0} @$items;
112 for my $order_num (0..$#order) {
113 $order_map{ $order[$order_num] } = ($#order - $order_num) + 1;
116 return [sort {$order_map{$b} <=> $order_map{$a}} @$items];
126 Reaction::UI::ViewPort - Page layout building block
130 # Create a new ViewPort:
131 # $stack isa Reaction::UI::FocusStack object
132 my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xthml');
134 # Fetch ViewPort higher up the stack (further out)
135 my $outer = $vp->outer();
137 # Fetch ViewPort lower down (further in)
138 my $inner = $vp->inner();
140 # Create a named tangent stack for this ViewPort
141 my $substack = $vp->create_tangent('name');
143 # Retrieve a tangent stack for this ViewPort
144 my $substack = $vp->forcus_tangent('name');
146 # Get the names of all the tangent stacks for this ViewPort
147 my @names = $vp->focus_tangents();
149 # Fetch all the tangent stacks for this ViewPort
150 # This is called by apply_events
151 my $stacks = $vp->child_event_sinks();
154 ### The following methods are all called automatically when using
155 ### Reaction::UI::Controller(s)
156 # Resolve current events with this ViewPort
157 $vp->apply_events($ctx, $param_hash);
159 # Apply current events to all tangent stacks
160 # This is called by apply_events
161 $vp->apply_child_events($ctx, $params_hash);
163 # Apply current events to this ViewPort
164 # This is called by apply_events
165 $vp->apply_our_events($ctx, $params_hash);
169 A ViewPort describes part of a page, it can be a field, a form or
170 an entire page. ViewPorts are created on a
171 L<Reaction::UI::FocusStack>, usually belonging to a controller or
172 another ViewPort. Each ViewPort knows it's own position in the stack
173 it is in, as well as the stack containing it.
175 Each ViewPort has a specific location in the heirarchy of viewports
176 making up a page. The hierarchy is determined as follows: The first
177 ViewPort in a stack is labeled C<0>, the second is C<1> and so on. If
178 a ViewPort is in a named tangent, it's location will contain the name
179 of the tangent in it's location.
181 For example, the first ViewPort in the 'left' tangent of the main
182 ViewPort has location C<0.left.0>.
184 Several ViewPort attributes are set by
185 L<Reaction::UI::FocusStack/push_viewport> when new ViewPorts are
186 created, these are as follows:
196 The outer attribute is set to the previous ViewPort in the stack when
197 creating a ViewPort, if the ViewPort is the first in the stack, it
202 The inner attribute is set to the next ViewPort down in the stack when
203 it is created, if this is the last ViewPort in the stack, it will be
208 The focus_stack attribute is set to the L<Reaction::UI::FocusStack>
209 object that created the ViewPort.
213 The ctx attribute will be passed automatically when using
214 L<Reaction::UI::Controller/push_viewport> to create a ViewPort in the
215 base stack of a controller. When creating tangent stacks, you may have
216 to pass it in yourself.
228 The layout attribute can either be specifically passed when calling
229 C<push_viewport>, or it will be determined using the last part of the
234 This is generally used by more specialised ViewPorts such as the
235 L<ListView|Reaction::UI::ViewPort::ListView> or
236 L<ActionForm|Reaction::UI::ViewPort::ActionForm>. It can be either a
237 space separated list of column names, or an arrayref of column names.
249 =item Arguments: none
253 Fetch the ViewPort outside this one in the page hierarchy.
259 =item Arguments: none
263 Fetch the ViewPort inside this one in the page hierarchy.
265 =head2 create_tangent
269 =item Arguments: $tangent_name
273 Create a new named L<Reaction::UI::FocusStack> inside this
274 ViewPort. The created FocusStack is returned.
280 =item Arguments: $tangent_name
284 Fetch a named FocusStack from this ViewPort.
286 =head2 focus_tangents
290 =item Arguments: none
294 Returns a list of names of all the known tangents in this ViewPort.
298 Return the L<Reaction::UI::FocusStack> object that this ViewPort is in.
304 =item Arguments: $ctx, $params_hashref
308 This method is called by the FocusStack object to resolve all events
311 =head2 apply_child_events
315 =item Arguments: $ctx, $params_hashref
319 Resolve the given events for all the tangents of this ViewPort. Called
322 =head2 apply_our_events
326 =item Arguments: $ctx, $events
330 Resolve the given events that match the location of this
331 ViewPort. Called by L<apply_events>.
337 =item Arguments: $events
341 Actually call the event handlers for this ViewPort. Called by
342 L<apply_our_events>. By default this will do nothing, subclass
343 ViewPort and implement L<accept_events>.
349 =item Arguments: none
353 Implement this method in a subclass and return a list of events that
354 your ViewPort is accepting.
360 =item Arguments: $name
364 Create an id for the given event name and this ViewPort. Generally
365 returns the location and the name, joined with a colon.
371 =item Arguments: $spec, $items
375 Sorts the given list of items such that the ones that also appear in
376 the spec are at the beginning. This is called by
377 L<Reaction::UI::ViewPort::ActionForm> and
378 L<Reaction::UI::ViewPort::ListView>, and gets passed L<column_order>
379 as the spec argument.
383 See L<Reaction::Class> for authors.
387 See L<Reaction::Class> for the license.