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