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