r22767@hercule (orig r1158): mateu | 2009-07-28 14:16:29 +0100
[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;
31 if ( $res->status =~ /^3/ || length($res->body) ) {
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";
5873cdbf 53 my $param_hash = { %{$ctx->req->$meth} }; # yeah, FocusStack deletes it
4edebf11 54 my @param_keys = keys %$param_hash;
55 if (@param_keys) {
56 for (@param_keys) {
57 utf8::decode($param_hash->{$_})
58 unless (utf8::is_utf8($param_hash->{$_}));
59 }
60 $self->focus_stack->apply_events($param_hash);
61 }
81393881 62 }
63};
64sub flush_view {
65 my ($self) = @_;
66 my $res = $self->ctx->res;
4edebf11 67 my $res_body = $self->view->render_window($self);
68 utf8::encode($res_body) if utf8::is_utf8($res_body);
69 $res->body($res_body);
81393881 70 $res->content_type($self->content_type);
71};
7adfd53f 72
81393881 73# required by old Renderer::XHTML
74sub render_viewport {
75 my ($self, $vp) = @_;
76 return unless $vp;
77 return $self->view->render_viewport($self, $vp);
7adfd53f 78};
79
81393881 80__PACKAGE__->meta->make_immutable;
81
82
7adfd53f 831;
84
85=head1 NAME
86
87Reaction::UI::Window - Container for rendering the UI elements in
88
89=head1 SYNOPSIS
90
91 my $window = Reaction::UI::Window->new(
92 ctx => $ctx,
93 view_name => $view_name,
94 content_type => $content_type,
95 title => $window_title,
96 );
97
9964b409 98 # More commonly, as Reaction::UI::Controller::Root creates one for you:
7adfd53f 99 my $window = $ctx->stash->{window};
100
89939ff9 101 # Resolve current events and render the view of the UI
7adfd53f 102 # elements of this Window:
9964b409 103 # This is called by the end action of Reaction::UI::Controller::Root
7adfd53f 104 $window->flush();
105
106 # Resolve current events:
107 $window->flush_events();
108
109 # Render the top ViewPort in the FocusStack of this Window:
110 $window->flush_view();
111
112 # Render a particular ViewPort:
113 $window->render_viewport($viewport);
114
115 # Or in a template:
116 [% window.render_viewport(self.inner) %]
117
118 # Add a ViewPort to the UI:
119 $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
120
121=head1 DESCRIPTION
122
123A Window object is created and stored in the stash by
9964b409 124L<Reaction::UI::Controller::Root>, it is used to contain all the
7adfd53f 125elements (ViewPorts) that make up the UI. The Window is rendered in
9964b409 126the end action of the Root Controller to make up the page.
7adfd53f 127
f1cd5548 128To add L<ViewPorts|Reaction::UI::ViewPort> to the stack, use the
129L<Reaction::UI::Controller/push_viewport> method. For more detailed
130information, read the L<Reaction::UI::FocusStack> and
131L<Reaction::UI::ViewPort> documentation.
7adfd53f 132
f1cd5548 133=head1 ATTRIBUTES
7adfd53f 134
f1cd5548 135These are set for you by L<Reaction::UI::Controller::Root/begin> from
136your Root controller configuration.
7adfd53f 137
138=head2 ctx
139
140=over
141
9964b409 142=item Arguments: $ctx?
7adfd53f 143
144=back
145
f1cd5548 146The current L<Catalyst> context object.
7adfd53f 147
148=head2 view_name
149
150=over
151
f1cd5548 152=item Arguments: $viewname?
7adfd53f 153
154=back
155
9964b409 156Retrieve/set the name of the L<Catalyst::View> component used to render
7adfd53f 157this Window. If this has not been set, rendering the Window will fail.
158
159=head2 content_type
160
161=over
162
9964b409 163=item Arguments: $contenttype?
7adfd53f 164
165=back
166
167Retrieve the content_type for the page. If this has not been set,
168rendering the Window will fail.
169
170=head2 title
171
172=over
173
174=item Arguments: $title?
175
176=back
177
178 [% window.title %]
179
9964b409 180Retrieve/set the title of this page, if not set, it will default to
7adfd53f 181"Untitled window".
182
183=head2 view
184
185=over
186
187=item Arguments: none
188
189=back
190
191Retrieve the L<Catalyst::View> instance, this can be set, or will be
192instantiated using the L<view_name> class.
193
194=head2 focus_stack
195
196=over
197
198=item Arguments: none
199
200=back
201
202 $window->focus_stack->push_viewport('Reaction::UI::ViewPort');
203
204Retrieve the L<stack|Reaction::UI::FocusStack> of
205L<ViewPorts|Reaction::UI::ViewPorts> that contains all the UI elements
206for this Window. Use L<Reaction::UI::FocusStack/push_viewport> on this
207to create more elements. An empty FocusStack is created by the
9964b409 208Controller::Root when the Window is created.
7adfd53f 209
f1cd5548 210=head1 METHODS
7adfd53f 211
212=head2 flush
213
214=over
215
216=item Arguments: none
217
218=back
219
220Synchronize the current events with all the L<Reaction::UI::ViewPort>
221objects in the UI, then render the root ViewPort. This is called for
9964b409 222you by L<Reaction::UI::Controller::Root/end>.
7adfd53f 223
224=head2 flush_events
225
226=over
227
228=item Arguments: none
229
230=back
231
232Resolves all the current events, first the query parameters then the
233body parameters, with all the L<Reaction::UI::ViewPort> objects in the
234UI. This calls L<Reaction::UI::FocusStack/apply_events>. This method
235is called by L<flush>.
236
237=head2 flush_view
238
239=over
240
241=item Arguments: none
242
243=back
244
245Renders the page into the L<Catalyst::Response> body, unless the
246response status is already set to 3xx, or the body has already been
f1cd5548 247filled. This is done via L<Reaction::UI::View/render_window>. This
248method is called by L<flush>.
7adfd53f 249
250=head1 AUTHORS
251
252See L<Reaction::Class> for authors.
253
254=head1 LICENSE
255
256See L<Reaction::Class> for the license.
257
258=cut