1 package Reaction::UI::ViewPort;
4 use Scalar::Util qw/blessed/;
6 sub DEBUG_EVENTS () { $ENV{REACTION_UI_VIEWPORT_DEBUG_EVENTS} }
10 has location => (isa => 'Str', is => 'rw', required => 1);
11 has layout => (isa => 'Str', is => 'rw', lazy_build => 1);
12 has layout_args => (isa => 'HashRef', is => 'ro', default => sub { {} });
13 has outer => (isa => 'Reaction::UI::ViewPort', is => 'rw', weak_ref => 1);
14 has inner => (isa => 'Reaction::UI::ViewPort', is => 'rw');
16 isa => 'Reaction::UI::FocusStack', is => 'rw', weak_ref => 1
18 has _tangent_stacks => (
19 isa => 'HashRef', is => 'ro', default => sub { {} }
21 has ctx => (isa => 'Catalyst', is => 'ro', required => 1);
23 implements _build_layout => as {
27 implements create_tangent => as {
28 my ($self, $name) = @_;
29 my $t_map = $self->_tangent_stacks;
30 if (exists $t_map->{$name}) {
31 confess "Can't create tangent with already existing name ${name}";
33 my $loc = join('.', $self->location, $name);
34 my $tangent = Reaction::UI::FocusStack->new(loc_prefix => $loc);
35 $t_map->{$name} = $tangent;
39 implements focus_tangent => as {
40 my ($self, $name) = @_;
41 if (my $tangent = $self->_tangent_stacks->{$name}) {
48 implements focus_tangents => as {
49 return keys %{shift->_tangent_stacks};
52 implements child_event_sinks => as {
54 return values %{$self->_tangent_stacks};
57 implements apply_events => as {
58 my ($self, $ctx, $events) = @_;
59 return unless keys %$events;
60 $self->apply_child_events($ctx, $events);
61 $self->apply_our_events($ctx, $events);
64 implements apply_child_events => as {
65 my ($self, $ctx, $events) = @_;
66 return unless keys %$events;
67 foreach my $child ($self->child_event_sinks) {
68 confess blessed($child) ."($child) is not a valid object"
69 unless blessed($child) && $child->can('apply_events');
70 $child->apply_events($ctx, $events);
74 implements apply_our_events => as {
75 my ($self, $ctx, $events) = @_;
76 my @keys = keys %$events;
78 my $loc = $self->location;
80 foreach my $key (keys %$events) {
81 if ($key =~ m/^${loc}:(.*)$/) {
82 $our_events{$1} = $events->{$key};
85 if (keys %our_events) {
86 #warn "$self: events ".join(', ', %our_events)."\n";
87 $self->handle_events(\%our_events);
91 implements handle_events => as {
92 my ($self, $events) = @_;
93 foreach my $event ($self->accept_events) {
94 if (exists $events->{$event}) {
96 my $name = join(' at ', $self, $self->location);
97 $self->ctx->log->debug(
98 "Applying Event: $event on $name with value: "
102 $self->$event($events->{$event});
107 implements accept_events => as { () };
109 implements event_id_for => as {
110 my ($self, $name) = @_;
111 return join(':', $self->location, $name);
114 implements sort_by_spec => as {
115 my ($self, $spec, $items) = @_;
116 return $items if not defined $spec;
119 if (ref $spec eq 'ARRAY') {
122 elsif (not ref $spec) {
123 return $items unless length $spec;
124 @order = split /\s+/, $spec;
127 my %order_map = map {$_ => 0} @$items;
128 for my $order_num (0..$#order) {
129 $order_map{ $order[$order_num] } = ($#order - $order_num) + 1;
132 return [sort {$order_map{$b} <=> $order_map{$a}} @$items];
142 Reaction::UI::ViewPort - Page layout building block
146 # Create a new ViewPort:
147 # $stack isa Reaction::UI::FocusStack object
148 my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xthml');
150 # Fetch ViewPort higher up the stack (further out)
151 my $outer = $vp->outer();
153 # Fetch ViewPort lower down (further in)
154 my $inner = $vp->inner();
156 # Create a named tangent stack for this ViewPort
157 my $substack = $vp->create_tangent('name');
159 # Retrieve a tangent stack for this ViewPort
160 my $substack = $vp->forcus_tangent('name');
162 # Get the names of all the tangent stacks for this ViewPort
163 my @names = $vp->focus_tangents();
165 # Fetch all the tangent stacks for this ViewPort
166 # This is called by apply_events
167 my $stacks = $vp->child_event_sinks();
170 ### The following methods are all called automatically when using
171 ### Reaction::UI::Controller(s)
172 # Resolve current events with this ViewPort
173 $vp->apply_events($ctx, $param_hash);
175 # Apply current events to all tangent stacks
176 # This is called by apply_events
177 $vp->apply_child_events($ctx, $params_hash);
179 # Apply current events to this ViewPort
180 # This is called by apply_events
181 $vp->apply_our_events($ctx, $params_hash);
185 A ViewPort describes part of a page, it can be a field, a form or
186 an entire page. ViewPorts are created on a
187 L<Reaction::UI::FocusStack>, usually belonging to a controller or
188 another ViewPort. Each ViewPort knows it's own position in the stack
189 it is in, as well as the stack containing it.
191 Each ViewPort has a specific location in the heirarchy of viewports
192 making up a page. The hierarchy is determined as follows: The first
193 ViewPort in a stack is labeled C<0>, the second is C<1> and so on. If
194 a ViewPort is in a named tangent, it's location will contain the name
195 of the tangent in it's location.
197 For example, the first ViewPort in the 'left' tangent of the main
198 ViewPort has location C<0.left.0>.
200 Several ViewPort attributes are set by
201 L<Reaction::UI::FocusStack/push_viewport> when new ViewPorts are
202 created, these are as follows:
212 The outer attribute is set to the previous ViewPort in the stack when
213 creating a ViewPort, if the ViewPort is the first in the stack, it
218 The inner attribute is set to the next ViewPort down in the stack when
219 it is created, if this is the last ViewPort in the stack, it will be
224 The focus_stack attribute is set to the L<Reaction::UI::FocusStack>
225 object that created the ViewPort.
229 The ctx attribute will be passed automatically when using
230 L<Reaction::UI::Controller/push_viewport> to create a ViewPort in the
231 base stack of a controller. When creating tangent stacks, you may have
232 to pass it in yourself.
244 The layout attribute can either be specifically passed when calling
245 C<push_viewport>, or it will be determined using the last part of the
250 This is generally used by more specialised ViewPorts such as the
251 L<ListView|Reaction::UI::ViewPort::ListView> or
252 L<Action|Reaction::UI::ViewPort::Action>. It can be either a
253 space separated list of column names, or an arrayref of column names.
265 =item Arguments: none
269 Fetch the ViewPort outside this one in the page hierarchy.
275 =item Arguments: none
279 Fetch the ViewPort inside this one in the page hierarchy.
281 =head2 create_tangent
285 =item Arguments: $tangent_name
289 Create a new named L<Reaction::UI::FocusStack> inside this
290 ViewPort. The created FocusStack is returned.
296 =item Arguments: $tangent_name
300 Fetch a named FocusStack from this ViewPort.
302 =head2 focus_tangents
306 =item Arguments: none
310 Returns a list of names of all the known tangents in this ViewPort.
314 Return the L<Reaction::UI::FocusStack> object that this ViewPort is in.
320 =item Arguments: $ctx, $params_hashref
324 This method is called by the FocusStack object to resolve all events
327 =head2 apply_child_events
331 =item Arguments: $ctx, $params_hashref
335 Resolve the given events for all the tangents of this ViewPort. Called
338 =head2 apply_our_events
342 =item Arguments: $ctx, $events
346 Resolve the given events that match the location of this
347 ViewPort. Called by L<apply_events>.
353 =item Arguments: $events
357 Actually call the event handlers for this ViewPort. Called by
358 L<apply_our_events>. By default this will do nothing, subclass
359 ViewPort and implement L<accept_events>.
365 =item Arguments: none
369 Implement this method in a subclass and return a list of events that
370 your ViewPort is accepting.
376 =item Arguments: $name
380 Create an id for the given event name and this ViewPort. Generally
381 returns the location and the name, joined with a colon.
387 =item Arguments: $spec, $items
391 Sorts the given list of items such that the ones that also appear in
392 the spec are at the beginning. This is called by
393 L<Reaction::UI::ViewPort::Action> and
394 L<Reaction::UI::ViewPort::ListView>, and gets passed L<column_order>
395 as the spec argument.
399 See L<Reaction::Class> for authors.
403 See L<Reaction::Class> for the license.