a3c9708e5d7ee5c68d67e9b6901ab3d4b1d4e642
[catagits/Reaction.git] / lib / Reaction / UI / Window.pm
1 package Reaction::UI::Window;
2
3 use Reaction::Class;
4 use Reaction::UI::FocusStack;
5
6 class Window which {
7
8   has ctx => (isa => 'Catalyst', is => 'ro', required => 1);
9   has view_name => (isa => 'Str', is => 'ro', lazy_fail => 1);
10   has content_type => (isa => 'Str', is => 'ro', lazy_fail => 1);
11   has title => (isa => 'Str', is => 'rw', default => sub { 'Untitled window' });
12   has view => (
13     # XXX compile failure because the Catalyst::View constraint would be
14     # auto-generated which doesn't work with unions. ::Types::Catalyst needed.
15     #isa => 'Catalyst::View|Reaction::UI::View',
16     isa => 'Object', is => 'ro', lazy_build => 1
17   );
18   has focus_stack => (
19     isa => 'Reaction::UI::FocusStack',
20     is => 'ro', required => 1,
21     default => sub { Reaction::UI::FocusStack->new },
22   );
23
24   implements _build_view => as {
25     my ($self) = @_;
26     return $self->ctx->view($self->view_name);
27   };
28
29   implements flush => as {
30     my ($self) = @_;
31     $self->flush_events;
32     $self->flush_view;
33   };
34
35   implements flush_events => as {
36     my ($self) = @_;
37     my $ctx = $self->ctx;
38
39     #I really think we should make a copies of the parameter hashes here
40     #and then as we handle events, delete ethem from the event hashref, so
41     #that it thins down as it makes it down the viewport tree. which would
42     #limit the number of events that get to the children viewports. it wont
43     #save that many subcalls unless there is a lot of child_items, but it's
44     #more about doing the correct thing. It also avoids children viewports
45     #being able to see their parents' events, which leaves the door open for
46     #abuse of the system.  thoughts anyone?
47
48     foreach my $type (qw/query body/) {
49       my $meth = "${type}_parameters";
50       my $param_hash = $ctx->req->$meth;
51       $self->focus_stack->apply_events($ctx, $param_hash)
52         if keys %$param_hash;
53     }
54   };
55
56   implements flush_view => as {
57     my ($self) = @_;
58     my $res = $self->ctx->res;
59     #$res->content_type($self->content_type);
60     return if $res->status =~ /^3/ || length($res->body);
61     $res->body($self->view->render_window($self));
62     $res->content_type($self->content_type);
63   };
64
65   # required by old Renderer::XHTML
66
67   implements render_viewport => as {
68     my ($self, $vp) = @_;
69     return unless $vp;
70     return $self->view->render_viewport($self, $vp);
71   };
72
73 };
74
75 1;
76
77 =head1 NAME
78
79 Reaction::UI::Window - Container for rendering the UI elements in
80
81 =head1 SYNOPSIS
82
83   my $window = Reaction::UI::Window->new(
84     ctx => $ctx,
85     view_name => $view_name,
86     content_type => $content_type,
87     title => $window_title,
88   );
89
90   # More commonly, as Reaction::UI::Controller::Root creates one for you:
91   my $window = $ctx->stash->{window};
92
93   # Resolve current events and render the view of the UI
94   #  elements of this Window:
95   # This is called by the end action of Reaction::UI::Controller::Root
96   $window->flush();
97
98   # Resolve current events:
99   $window->flush_events();
100
101   # Render the top ViewPort in the FocusStack of this Window:
102   $window->flush_view();
103
104   # Render a particular ViewPort:
105   $window->render_viewport($viewport);
106
107   # Or in a template:
108   [% window.render_viewport(self.inner) %]
109
110   # Add a ViewPort to the UI:
111   $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
112
113 =head1 DESCRIPTION
114
115 A Window object is created and stored in the stash by
116 L<Reaction::UI::Controller::Root>, it is used to contain all the
117 elements (ViewPorts) that make up the UI. The Window is rendered in
118 the end action of the Root Controller to make up the page.
119
120 To add L<ViewPorts|Reaction::UI::ViewPort> to the stack, read the
121 L<Reaction::UI::FocusStack> and L<Reaction::UI::ViewPort> documentation.
122
123 Several Window attributes are set by
124 L<Reaction::UI::Controller::Root/begin> when a new Window is created,
125 these are as follows:
126
127 =over
128
129 =item ctx
130
131 The current L<Catalyst> context object is set.
132
133 =item view_name
134
135 The view_name is set from the L<Reaction::UI::Controller::Root> attributes.
136
137 =item content_type
138
139 The content_type is set from the L<Reaction::UI::Controller::Root> attributes.
140
141 =item title
142
143 The title is set from the L<Reaction::UI::Controller::Root>
144 window_title attribute.
145
146 =back
147
148 =head1 METHODS
149
150 =head2 ctx
151
152 =over
153
154 =item Arguments: $ctx?
155
156 =back
157
158 Retrieve/set the current L<Catalyst> context object.
159
160 =head2 view_name
161
162 =over
163
164 =item Arguments: %viewname?
165
166 =back
167
168 Retrieve/set the name of the L<Catalyst::View> component used to render
169 this Window. If this has not been set, rendering the Window will fail.
170
171 =head2 content_type
172
173 =over
174
175 =item Arguments: $contenttype?
176
177 =back
178
179 Retrieve the content_type for the page. If this has not been set,
180 rendering the Window will fail.
181
182 =head2 title
183
184 =over
185
186 =item Arguments: $title?
187
188 =back
189
190   [% window.title %]
191
192 Retrieve/set the title of this page, if not set, it will default to
193 "Untitled window".
194
195 =head2 view
196
197 =over
198
199 =item Arguments: none
200
201 =back
202
203 Retrieve the L<Catalyst::View> instance, this can be set, or will be
204 instantiated using the L<view_name> class.
205
206 =head2 focus_stack
207
208 =over
209
210 =item Arguments: none
211
212 =back
213
214   $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
215
216 Retrieve the L<stack|Reaction::UI::FocusStack> of
217 L<ViewPorts|Reaction::UI::ViewPorts> that contains all the UI elements
218 for this Window. Use L<Reaction::UI::FocusStack/push_viewport> on this
219 to create more elements. An empty FocusStack is created by the
220 Controller::Root when the Window is created.
221
222 =head2 render_viewport
223
224 =over
225
226 =item Arguments: $viewport
227
228 =back
229
230   $window->render_viewport($viewport);
231
232   [% window.render_viewport(self.inner) %]
233
234 Calls render on the L<view> object used by this Window. The following
235 arguments are given:
236
237 =over
238
239 =item ctx
240
241 The L<Catalyst> context object.
242
243 =item self
244
245 The ViewPort object to be rendered.
246
247 =item window
248
249 The Window object.
250
251 =item type
252
253 The string that describes the layout from L<Reaction::UI::ViewPort/layout>.
254
255 =back
256
257 =head2 flush
258
259 =over
260
261 =item Arguments: none
262
263 =back
264
265 Synchronize the current events with all the L<Reaction::UI::ViewPort>
266 objects in the UI, then render the root ViewPort. This is called for
267 you by L<Reaction::UI::Controller::Root/end>.
268
269 =head2 flush_events
270
271 =over
272
273 =item Arguments: none
274
275 =back
276
277 Resolves all the current events, first the query parameters then the
278 body parameters, with all the L<Reaction::UI::ViewPort> objects in the
279 UI. This calls L<Reaction::UI::FocusStack/apply_events>. This method
280 is called by L<flush>.
281
282 =head2 flush_view
283
284 =over
285
286 =item Arguments: none
287
288 =back
289
290 Renders the page into the L<Catalyst::Response> body, unless the
291 response status is already set to 3xx, or the body has already been
292 filled. This calls L<render_viewport> with the root
293 L<Reaction::UI::ViewPort> from the L<focus_stack>. This method is
294 called by L<flush>.
295
296 =head1 AUTHORS
297
298 See L<Reaction::Class> for authors.
299
300 =head1 LICENSE
301
302 See L<Reaction::Class> for the license.
303
304 =cut