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