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