Merge branch utf8_support branch (r1109:1112) into trunk.
[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, weak_ref => 1);
10 has view_name => (isa => 'Str', is => 'ro', lazy_fail => 1);
11 has content_type => (isa => 'Str', is => 'rw', 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   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   }
35   $self->flush_events;
36   $self->flush_view;
37 };
38 sub 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";
53     my $param_hash = { %{$ctx->req->$meth} }; # yeah, FocusStack deletes it
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     }
62   }
63 };
64 sub flush_view {
65   my ($self) = @_;
66   my $res = $self->ctx->res;
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);
70   $res->content_type($self->content_type);
71 };
72
73 # required by old Renderer::XHTML
74 sub render_viewport {
75   my ($self, $vp) = @_;
76   return unless $vp;
77   return $self->view->render_viewport($self, $vp);
78 };
79
80 __PACKAGE__->meta->make_immutable;
81
82
83 1;
84
85 =head1 NAME
86
87 Reaction::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
98   # More commonly, as Reaction::UI::Controller::Root creates one for you:
99   my $window = $ctx->stash->{window};
100
101   # Resolve current events and render the view of the UI
102   #  elements of this Window:
103   # This is called by the end action of Reaction::UI::Controller::Root
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
123 A Window object is created and stored in the stash by
124 L<Reaction::UI::Controller::Root>, it is used to contain all the
125 elements (ViewPorts) that make up the UI. The Window is rendered in
126 the end action of the Root Controller to make up the page.
127
128 To add L<ViewPorts|Reaction::UI::ViewPort> to the stack, use the
129 L<Reaction::UI::Controller/push_viewport> method. For more detailed
130 information, read the L<Reaction::UI::FocusStack> and
131 L<Reaction::UI::ViewPort> documentation.
132
133 =head1 ATTRIBUTES
134
135 These are set for you by L<Reaction::UI::Controller::Root/begin> from
136 your Root controller configuration.
137
138 =head2 ctx
139
140 =over
141
142 =item Arguments: $ctx?
143
144 =back
145
146 The current L<Catalyst> context object.
147
148 =head2 view_name
149
150 =over
151
152 =item Arguments: $viewname?
153
154 =back
155
156 Retrieve/set the name of the L<Catalyst::View> component used to render
157 this Window. If this has not been set, rendering the Window will fail.
158
159 =head2 content_type
160
161 =over
162
163 =item Arguments: $contenttype?
164
165 =back
166
167 Retrieve the content_type for the page. If this has not been set,
168 rendering the Window will fail.
169
170 =head2 title
171
172 =over
173
174 =item Arguments: $title?
175
176 =back
177
178   [% window.title %]
179
180 Retrieve/set the title of this page, if not set, it will default to
181 "Untitled window".
182
183 =head2 view
184
185 =over
186
187 =item Arguments: none
188
189 =back
190
191 Retrieve the L<Catalyst::View> instance, this can be set, or will be
192 instantiated 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
204 Retrieve the L<stack|Reaction::UI::FocusStack> of
205 L<ViewPorts|Reaction::UI::ViewPorts> that contains all the UI elements
206 for this Window. Use L<Reaction::UI::FocusStack/push_viewport> on this
207 to create more elements. An empty FocusStack is created by the
208 Controller::Root when the Window is created.
209
210 =head1 METHODS
211
212 =head2 flush
213
214 =over
215
216 =item Arguments: none
217
218 =back
219
220 Synchronize the current events with all the L<Reaction::UI::ViewPort>
221 objects in the UI, then render the root ViewPort. This is called for
222 you by L<Reaction::UI::Controller::Root/end>.
223
224 =head2 flush_events
225
226 =over
227
228 =item Arguments: none
229
230 =back
231
232 Resolves all the current events, first the query parameters then the
233 body parameters, with all the L<Reaction::UI::ViewPort> objects in the
234 UI. This calls L<Reaction::UI::FocusStack/apply_events>. This method
235 is called by L<flush>.
236
237 =head2 flush_view
238
239 =over
240
241 =item Arguments: none
242
243 =back
244
245 Renders the page into the L<Catalyst::Response> body, unless the
246 response status is already set to 3xx, or the body has already been
247 filled. This is done via L<Reaction::UI::View/render_window>. This
248 method is called by L<flush>.
249
250 =head1 AUTHORS
251
252 See L<Reaction::Class> for authors.
253
254 =head1 LICENSE
255
256 See L<Reaction::Class> for the license.
257
258 =cut