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->ctx->log->debug("Applying Event: $event with value: ". $events->{$event});
87 $self->$event($events->{$event});
92 implements accept_events => as { () };
94 implements event_id_for => as {
95 my ($self, $name) = @_;
96 return join(':', $self->location, $name);
99 implements sort_by_spec => as {
100 my ($self, $spec, $items) = @_;
101 return $items if not defined $spec;
104 if (ref $spec eq 'ARRAY') {
107 elsif (not ref $spec) {
108 return $items unless length $spec;
109 @order = split /\s+/, $spec;
112 my %order_map = map {$_ => 0} @$items;
113 for my $order_num (0..$#order) {
114 $order_map{ $order[$order_num] } = ($#order - $order_num) + 1;
117 return [sort {$order_map{$b} <=> $order_map{$a}} @$items];
127 Reaction::UI::ViewPort - Page layout building block
131 # Create a new ViewPort:
132 # $stack isa Reaction::UI::FocusStack object
133 my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xthml');
135 # Fetch ViewPort higher up the stack (further out)
136 my $outer = $vp->outer();
138 # Fetch ViewPort lower down (further in)
139 my $inner = $vp->inner();
141 # Create a named tangent stack for this ViewPort
142 my $substack = $vp->create_tangent('name');
144 # Retrieve a tangent stack for this ViewPort
145 my $substack = $vp->forcus_tangent('name');
147 # Get the names of all the tangent stacks for this ViewPort
148 my @names = $vp->focus_tangents();
150 # Fetch all the tangent stacks for this ViewPort
151 # This is called by apply_events
152 my $stacks = $vp->child_event_sinks();
155 ### The following methods are all called automatically when using
156 ### Reaction::UI::Controller(s)
157 # Resolve current events with this ViewPort
158 $vp->apply_events($ctx, $param_hash);
160 # Apply current events to all tangent stacks
161 # This is called by apply_events
162 $vp->apply_child_events($ctx, $params_hash);
164 # Apply current events to this ViewPort
165 # This is called by apply_events
166 $vp->apply_our_events($ctx, $params_hash);
170 A ViewPort describes part of a page, it can be a field, a form or
171 an entire page. ViewPorts are created on a
172 L<Reaction::UI::FocusStack>, usually belonging to a controller or
173 another ViewPort. Each ViewPort knows it's own position in the stack
174 it is in, as well as the stack containing it.
176 Each ViewPort has a specific location in the heirarchy of viewports
177 making up a page. The hierarchy is determined as follows: The first
178 ViewPort in a stack is labeled C<0>, the second is C<1> and so on. If
179 a ViewPort is in a named tangent, it's location will contain the name
180 of the tangent in it's location.
182 For example, the first ViewPort in the 'left' tangent of the main
183 ViewPort has location C<0.left.0>.
185 Several ViewPort attributes are set by
186 L<Reaction::UI::FocusStack/push_viewport> when new ViewPorts are
187 created, these are as follows:
197 The outer attribute is set to the previous ViewPort in the stack when
198 creating a ViewPort, if the ViewPort is the first in the stack, it
203 The inner attribute is set to the next ViewPort down in the stack when
204 it is created, if this is the last ViewPort in the stack, it will be
209 The focus_stack attribute is set to the L<Reaction::UI::FocusStack>
210 object that created the ViewPort.
214 The ctx attribute will be passed automatically when using
215 L<Reaction::UI::Controller/push_viewport> to create a ViewPort in the
216 base stack of a controller. When creating tangent stacks, you may have
217 to pass it in yourself.
229 The layout attribute can either be specifically passed when calling
230 C<push_viewport>, or it will be determined using the last part of the
235 This is generally used by more specialised ViewPorts such as the
236 L<ListView|Reaction::UI::ViewPort::ListView> or
237 L<ActionForm|Reaction::UI::ViewPort::ActionForm>. It can be either a
238 space separated list of column names, or an arrayref of column names.
250 =item Arguments: none
254 Fetch the ViewPort outside this one in the page hierarchy.
260 =item Arguments: none
264 Fetch the ViewPort inside this one in the page hierarchy.
266 =head2 create_tangent
270 =item Arguments: $tangent_name
274 Create a new named L<Reaction::UI::FocusStack> inside this
275 ViewPort. The created FocusStack is returned.
281 =item Arguments: $tangent_name
285 Fetch a named FocusStack from this ViewPort.
287 =head2 focus_tangents
291 =item Arguments: none
295 Returns a list of names of all the known tangents in this ViewPort.
299 Return the L<Reaction::UI::FocusStack> object that this ViewPort is in.
305 =item Arguments: $ctx, $params_hashref
309 This method is called by the FocusStack object to resolve all events
312 =head2 apply_child_events
316 =item Arguments: $ctx, $params_hashref
320 Resolve the given events for all the tangents of this ViewPort. Called
323 =head2 apply_our_events
327 =item Arguments: $ctx, $events
331 Resolve the given events that match the location of this
332 ViewPort. Called by L<apply_events>.
338 =item Arguments: $events
342 Actually call the event handlers for this ViewPort. Called by
343 L<apply_our_events>. By default this will do nothing, subclass
344 ViewPort and implement L<accept_events>.
350 =item Arguments: none
354 Implement this method in a subclass and return a list of events that
355 your ViewPort is accepting.
361 =item Arguments: $name
365 Create an id for the given event name and this ViewPort. Generally
366 returns the location and the name, joined with a colon.
372 =item Arguments: $spec, $items
376 Sorts the given list of items such that the ones that also appear in
377 the spec are at the beginning. This is called by
378 L<Reaction::UI::ViewPort::ActionForm> and
379 L<Reaction::UI::ViewPort::ListView>, and gets passed L<column_order>
380 as the spec argument.
384 See L<Reaction::Class> for authors.
388 See L<Reaction::Class> for the license.