fix for undef bodies
[catagits/Reaction.git] / lib / Reaction / UI / Window.pm
CommitLineData
7adfd53f 1package Reaction::UI::Window;
2
3use Reaction::Class;
4use Reaction::UI::FocusStack;
5
81393881 6use namespace::clean -except => [ qw(meta) ];
7
8
2082d8f2 9has ctx => (isa => 'Catalyst', is => 'ro', required => 1, weak_ref => 1);
81393881 10has view_name => (isa => 'Str', is => 'ro', lazy_fail => 1);
8b3b4820 11has content_type => (isa => 'Str', is => 'rw', lazy_fail => 1);
81393881 12has title => (isa => 'Str', is => 'rw', default => sub { 'Untitled window' });
13has 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);
19has focus_stack => (
20 isa => 'Reaction::UI::FocusStack',
21 is => 'ro', required => 1,
22 default => sub { Reaction::UI::FocusStack->new },
23);
24sub _build_view {
25 my ($self) = @_;
26 return $self->ctx->view($self->view_name);
27};
28sub flush {
29 my ($self) = @_;
bdfdc97f 30 my $res = $self->ctx->res;
4746f5d6 31 if ( $res->status =~ /^3/ || ( defined $res->body && length($res->body) ) ) {
bdfdc97f 32 $res->content_type('text/plain') unless $res->content_type;
33 return;
34 }
81393881 35 $self->flush_events;
36 $self->flush_view;
37};
38sub flush_events {
39 my ($self) = @_;
40 my $ctx = $self->ctx;
41
42 #I really think we should make a copies of the parameter hashes here
43 #and then as we handle events, delete ethem from the event hashref, so
44 #that it thins down as it makes it down the viewport tree. which would
45 #limit the number of events that get to the children viewports. it wont
46 #save that many subcalls unless there is a lot of child_items, but it's
47 #more about doing the correct thing. It also avoids children viewports
48 #being able to see their parents' events, which leaves the door open for
49 #abuse of the system. thoughts anyone?
50
51 foreach my $type (qw/query body/) {
52 my $meth = "${type}_parameters";
049d2234 53 my $req_param = $ctx->req->$meth;
54 my $param_hash = {
55 map {
56 $_ =~ m/(^r.+\:\w+)\.(x|y)/ ? # for <input type="image"... buttons
57 ( $1 => $req_param->{$_} )
58 : ( $_ => $req_param->{$_} )
59 } keys %$req_param
60 }; # yeah, FocusStack deletes it
4edebf11 61 my @param_keys = keys %$param_hash;
62 if (@param_keys) {
63 for (@param_keys) {
64 utf8::decode($param_hash->{$_})
65 unless (utf8::is_utf8($param_hash->{$_}));
66 }
67 $self->focus_stack->apply_events($param_hash);
68 }
81393881 69 }
70};
71sub flush_view {
72 my ($self) = @_;
73 my $res = $self->ctx->res;
4edebf11 74 my $res_body = $self->view->render_window($self);
75 utf8::encode($res_body) if utf8::is_utf8($res_body);
76 $res->body($res_body);
81393881 77 $res->content_type($self->content_type);
78};
7adfd53f 79
81393881 80# required by old Renderer::XHTML
81sub render_viewport {
82 my ($self, $vp) = @_;
83 return unless $vp;
84 return $self->view->render_viewport($self, $vp);
7adfd53f 85};
86
81393881 87__PACKAGE__->meta->make_immutable;
88
89
7adfd53f 901;
91
92=head1 NAME
93
94Reaction::UI::Window - Container for rendering the UI elements in
95
96=head1 SYNOPSIS
97
98 my $window = Reaction::UI::Window->new(
99 ctx => $ctx,
100 view_name => $view_name,
101 content_type => $content_type,
102 title => $window_title,
103 );
104
9964b409 105 # More commonly, as Reaction::UI::Controller::Root creates one for you:
7adfd53f 106 my $window = $ctx->stash->{window};
107
89939ff9 108 # Resolve current events and render the view of the UI
7adfd53f 109 # elements of this Window:
9964b409 110 # This is called by the end action of Reaction::UI::Controller::Root
7adfd53f 111 $window->flush();
112
113 # Resolve current events:
114 $window->flush_events();
115
116 # Render the top ViewPort in the FocusStack of this Window:
117 $window->flush_view();
118
119 # Render a particular ViewPort:
120 $window->render_viewport($viewport);
121
122 # Or in a template:
123 [% window.render_viewport(self.inner) %]
124
125 # Add a ViewPort to the UI:
126 $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
127
128=head1 DESCRIPTION
129
130A Window object is created and stored in the stash by
9964b409 131L<Reaction::UI::Controller::Root>, it is used to contain all the
7adfd53f 132elements (ViewPorts) that make up the UI. The Window is rendered in
9964b409 133the end action of the Root Controller to make up the page.
7adfd53f 134
f1cd5548 135To add L<ViewPorts|Reaction::UI::ViewPort> to the stack, use the
136L<Reaction::UI::Controller/push_viewport> method. For more detailed
137information, read the L<Reaction::UI::FocusStack> and
138L<Reaction::UI::ViewPort> documentation.
7adfd53f 139
f1cd5548 140=head1 ATTRIBUTES
7adfd53f 141
f1cd5548 142These are set for you by L<Reaction::UI::Controller::Root/begin> from
143your Root controller configuration.
7adfd53f 144
145=head2 ctx
146
147=over
148
9964b409 149=item Arguments: $ctx?
7adfd53f 150
151=back
152
f1cd5548 153The current L<Catalyst> context object.
7adfd53f 154
155=head2 view_name
156
157=over
158
f1cd5548 159=item Arguments: $viewname?
7adfd53f 160
161=back
162
9964b409 163Retrieve/set the name of the L<Catalyst::View> component used to render
7adfd53f 164this Window. If this has not been set, rendering the Window will fail.
165
166=head2 content_type
167
168=over
169
9964b409 170=item Arguments: $contenttype?
7adfd53f 171
172=back
173
174Retrieve the content_type for the page. If this has not been set,
175rendering the Window will fail.
176
177=head2 title
178
179=over
180
181=item Arguments: $title?
182
183=back
184
185 [% window.title %]
186
9964b409 187Retrieve/set the title of this page, if not set, it will default to
7adfd53f 188"Untitled window".
189
190=head2 view
191
192=over
193
194=item Arguments: none
195
196=back
197
198Retrieve the L<Catalyst::View> instance, this can be set, or will be
199instantiated using the L<view_name> class.
200
201=head2 focus_stack
202
203=over
204
205=item Arguments: none
206
207=back
208
209 $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
210
211Retrieve the L<stack|Reaction::UI::FocusStack> of
212L<ViewPorts|Reaction::UI::ViewPorts> that contains all the UI elements
213for this Window. Use L<Reaction::UI::FocusStack/push_viewport> on this
214to create more elements. An empty FocusStack is created by the
9964b409 215Controller::Root when the Window is created.
7adfd53f 216
f1cd5548 217=head1 METHODS
7adfd53f 218
219=head2 flush
220
221=over
222
223=item Arguments: none
224
225=back
226
227Synchronize the current events with all the L<Reaction::UI::ViewPort>
228objects in the UI, then render the root ViewPort. This is called for
9964b409 229you by L<Reaction::UI::Controller::Root/end>.
7adfd53f 230
231=head2 flush_events
232
233=over
234
235=item Arguments: none
236
237=back
238
239Resolves all the current events, first the query parameters then the
240body parameters, with all the L<Reaction::UI::ViewPort> objects in the
241UI. This calls L<Reaction::UI::FocusStack/apply_events>. This method
242is called by L<flush>.
243
244=head2 flush_view
245
246=over
247
248=item Arguments: none
249
250=back
251
252Renders the page into the L<Catalyst::Response> body, unless the
253response status is already set to 3xx, or the body has already been
f1cd5548 254filled. This is done via L<Reaction::UI::View/render_window>. This
255method is called by L<flush>.
7adfd53f 256
257=head1 AUTHORS
258
259See L<Reaction::Class> for authors.
260
261=head1 LICENSE
262
263See L<Reaction::Class> for the license.
264
265=cut