formatting fix
[catagits/Reaction.git] / lib / Reaction / UI / FocusStack.pm
CommitLineData
7adfd53f 1package Reaction::UI::FocusStack;
2
3use Reaction::Class;
4
81393881 5use namespace::clean -except => [ qw(meta) ];
7adfd53f 6
7adfd53f 7
81393881 8has vp_head => (
9 isa => 'Reaction::UI::ViewPort', is => 'rw',
10 clearer => 'clear_vp_head',
11);
12has vp_tail => (
13 isa => 'Reaction::UI::ViewPort', is => 'rw',
14 clearer => 'clear_vp_tail',
15);
16has vp_count => (
17 isa => 'Int', is => 'rw', required => 1, default => sub { 0 }
18);
19has loc_prefix => (isa => 'Str', is => 'rw', predicate => 'has_loc_prefix');
20sub push_viewport {
21 my ($self, $class, %create) = @_;
22 my $tail = $self->vp_tail;
23 my $loc = $self->vp_count;
24 if ($self->has_loc_prefix) {
25 $loc = join('.', $self->loc_prefix, $loc);
26 }
27 my $vp = $class->new(
28 %create,
29 location => $loc,
30 focus_stack => $self,
31 (defined $tail ? ( outer => $tail ) : ()), # XXX possibly a bug in
32 #immutable?
33 );
34 if ($tail) { # if we already have a tail (non-empty vp stack)
35 $tail->inner($vp); # set the current tail's inner vp to the new vp
36 } else { # else we're currently an empty stack
37 $self->vp_head($vp); # so set the head to the new vp
38 }
39 $self->vp_count($self->vp_count + 1);
40 $self->vp_tail($vp);
41 return $vp;
42};
f1cd5548 43
81393881 44sub pop_viewport {
45 my ($self) = @_;
46 my $head = $self->vp_head;
47 confess "Can't pop from empty focus stack" unless defined($head);
48 my $vp = $self->vp_tail;
49 if ($vp eq $head) {
50 $self->clear_vp_head;
51 $self->clear_vp_tail;
52 } else {
53 $self->vp_tail($vp->outer);
54 }
55 $self->vp_count($self->vp_count - 1);
56 return $vp;
7adfd53f 57};
f1cd5548 58
81393881 59sub pop_viewports_to {
60 my ($self, $vp) = @_;
61 1 while ($self->pop_viewport ne $vp);
62 return $vp;
63};
f1cd5548 64
81393881 65sub apply_events {
66 my $self = shift;
67 my $vp = $self->vp_tail;
68 while (defined $vp) {
69 $vp->apply_events(@_);
70 $vp = $vp->outer;
71 }
72};
73
74
75__PACKAGE__->meta->make_immutable;
76
7adfd53f 77
781;
79
80=head1 NAME
81
82Reaction::UI::FocusStack - A linked list of ViewPort-based objects
83
84=head1 SYNOPSIS
85
86 my $stack = Reaction::UI::FocusStack->new();
87
88 # Or more commonly, in a Reaction::UI::RootController based
89 # Catalyst Controller:
90 my $stack = $ctx->focus_stack;
91
92 # Add a new basic viewport inside the last viewport on the stack:
93 my $vp = $stack->push_viewport('Reaction::UI::ViewPort' =>
94 layout => 'xhtml'
95 );
96
97 # Fetch the innermost viewport from the stack:
98 my $vp = $stack->pop_viewport();
99
100 # Remove all viewports inside a given viewport:
101 $stack->pop_viewports_to($vp);
102
103 # Create a named stack as a tangent to an existing viewport:
104 my $newstack = $vp->create_tangent('somename');
105
106 # Resolve current events using your stack:
107 # This is called by Reaction::UI::RootController in the end action.
108 $stack->apply_events($ctx, $param_hash);
109
110=head1 DESCRIPTION
111
112A FocusStack represents a list of related L<ViewPort|Reaction::UI::ViewPort>
113objects. The L<Reaction::UI::RootController> creates an empty stack for you in
114it's begin action, which represents the main thread/container of the page.
115Typically you add new ViewPorts to this stack as the main parts of your page.
116To add multiple parallel page subparts, create a tangent from the outer
117viewport, and add more viewports as normal.
118
119=head1 METHODS
120
121=head2 new
122
123=over
124
125=item Arguments: none
126
127=back
128
129Create a new empty FocusStack. This is done for you in
130L<Reaction::UI::RootController>.
131
132=head2 push_viewport
133
134=over
135
136=item Arguments: $class, %options
137
138=back
139
140Creates a new L<Reaction::UI::ViewPort> based object and adds it to the stack.
141
142The following attributes of the new ViewPort are set:
143
144=over
145
146=item outer
147
148Is set to the preceding ViewPort in the stack.
149
150=item focus_stack
151
152Is set to the FocusStack object that created the ViewPort.
153
154=item location
155
156Is set to the location of the ViewPort in the stack.
157
158=back
159
160=head2 pop_viewport
161
162=over
163
164=item Arguments: none
165
166=back
167
168Removes the last/innermost ViewPort from the stack and returns it.
169
170=head2 pop_viewports_to
171
172=over
173
174=item Arguments: $viewport
175
176=back
177
178Pops all ViewPorts off the stack until the given ViewPort object
179remains as the last item. If passed a $viewport not on the stack, this
180will empty the stack completely (and then die complainingly).
181
182TODO: Should pop_viewports_to check $vp->focus_stack eq $self first?
183
184=head2 vp_head
185
186=over
187
188=item Arguments: none
189
190=back
191
192Retrieve the first ViewPort in this stack. Useful for calling
193L<Reaction::UI::Window/render_viewport> on a
194L<Reaction::UI::ViewPort/focus_tangent>.
195
196=head2 vp_head
197
198=over
199
200=item Arguments: none
201
202=back
203
204Retrieve the first ViewPort in this stack. Useful for calling
205L<Reaction::UI::Window/render_viewport> on a
206L<Reaction::UI::ViewPort/focus_tangent>.
207
208=head2 vp_tail
209
210=over
211
212=item Arguments: none
213
214=back
215
216Retrieve the last ViewPort in this stack. Useful for calling
217L<Reaction::UI::Window/render_viewport> on a
218L<Reaction::UI::ViewPort/focus_tangent>.
219
220=head2 vp_count
221
222=over
223
224=item Arguments: none
225
226=back
227
228=head2 loc_prefix
229
230=head2 apply_events
231
232=over
233
234=item Arguments: $ctx, $params_hashref
235
236=back
237
238Instruct each of the ViewPorts in the stack to apply the given events
239to each of it's tangent stacks, and then to itself. These are applied
240starting with the last/innermost ViewPort first.
241
242=head1 AUTHORS
243
244See L<Reaction::Class> for authors.
245
246=head1 LICENSE
247
248See L<Reaction::Class> for the license.
249
250=cut