Commit | Line | Data |
7adfd53f |
1 | package Reaction::UI::Window; |
2 | |
3 | use Reaction::Class; |
4 | use Reaction::UI::FocusStack; |
5 | |
81393881 |
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); |
8b3b4820 |
11 | has content_type => (isa => 'Str', is => 'rw', lazy_fail => 1); |
81393881 |
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; |
52f292b2 |
49 | $self->focus_stack->apply_events($param_hash) |
81393881 |
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 | }; |
7adfd53f |
63 | |
81393881 |
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); |
7adfd53f |
69 | }; |
70 | |
81393881 |
71 | __PACKAGE__->meta->make_immutable; |
72 | |
73 | |
7adfd53f |
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 | |
9964b409 |
89 | # More commonly, as Reaction::UI::Controller::Root creates one for you: |
7adfd53f |
90 | my $window = $ctx->stash->{window}; |
91 | |
89939ff9 |
92 | # Resolve current events and render the view of the UI |
7adfd53f |
93 | # elements of this Window: |
9964b409 |
94 | # This is called by the end action of Reaction::UI::Controller::Root |
7adfd53f |
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 |
9964b409 |
115 | L<Reaction::UI::Controller::Root>, it is used to contain all the |
7adfd53f |
116 | elements (ViewPorts) that make up the UI. The Window is rendered in |
9964b409 |
117 | the end action of the Root Controller to make up the page. |
7adfd53f |
118 | |
f1cd5548 |
119 | To add L<ViewPorts|Reaction::UI::ViewPort> to the stack, use the |
120 | L<Reaction::UI::Controller/push_viewport> method. For more detailed |
121 | information, read the L<Reaction::UI::FocusStack> and |
122 | L<Reaction::UI::ViewPort> documentation. |
7adfd53f |
123 | |
f1cd5548 |
124 | =head1 ATTRIBUTES |
7adfd53f |
125 | |
f1cd5548 |
126 | These are set for you by L<Reaction::UI::Controller::Root/begin> from |
127 | your Root controller configuration. |
7adfd53f |
128 | |
129 | =head2 ctx |
130 | |
131 | =over |
132 | |
9964b409 |
133 | =item Arguments: $ctx? |
7adfd53f |
134 | |
135 | =back |
136 | |
f1cd5548 |
137 | The current L<Catalyst> context object. |
7adfd53f |
138 | |
139 | =head2 view_name |
140 | |
141 | =over |
142 | |
f1cd5548 |
143 | =item Arguments: $viewname? |
7adfd53f |
144 | |
145 | =back |
146 | |
9964b409 |
147 | Retrieve/set the name of the L<Catalyst::View> component used to render |
7adfd53f |
148 | this Window. If this has not been set, rendering the Window will fail. |
149 | |
150 | =head2 content_type |
151 | |
152 | =over |
153 | |
9964b409 |
154 | =item Arguments: $contenttype? |
7adfd53f |
155 | |
156 | =back |
157 | |
158 | Retrieve the content_type for the page. If this has not been set, |
159 | rendering the Window will fail. |
160 | |
161 | =head2 title |
162 | |
163 | =over |
164 | |
165 | =item Arguments: $title? |
166 | |
167 | =back |
168 | |
169 | [% window.title %] |
170 | |
9964b409 |
171 | Retrieve/set the title of this page, if not set, it will default to |
7adfd53f |
172 | "Untitled window". |
173 | |
174 | =head2 view |
175 | |
176 | =over |
177 | |
178 | =item Arguments: none |
179 | |
180 | =back |
181 | |
182 | Retrieve the L<Catalyst::View> instance, this can be set, or will be |
183 | instantiated using the L<view_name> class. |
184 | |
185 | =head2 focus_stack |
186 | |
187 | =over |
188 | |
189 | =item Arguments: none |
190 | |
191 | =back |
192 | |
193 | $window->focus_stack->push_viewport('Reaction::UI::ViewPort'); |
194 | |
195 | Retrieve the L<stack|Reaction::UI::FocusStack> of |
196 | L<ViewPorts|Reaction::UI::ViewPorts> that contains all the UI elements |
197 | for this Window. Use L<Reaction::UI::FocusStack/push_viewport> on this |
198 | to create more elements. An empty FocusStack is created by the |
9964b409 |
199 | Controller::Root when the Window is created. |
7adfd53f |
200 | |
f1cd5548 |
201 | =head1 METHODS |
7adfd53f |
202 | |
203 | =head2 flush |
204 | |
205 | =over |
206 | |
207 | =item Arguments: none |
208 | |
209 | =back |
210 | |
211 | Synchronize the current events with all the L<Reaction::UI::ViewPort> |
212 | objects in the UI, then render the root ViewPort. This is called for |
9964b409 |
213 | you by L<Reaction::UI::Controller::Root/end>. |
7adfd53f |
214 | |
215 | =head2 flush_events |
216 | |
217 | =over |
218 | |
219 | =item Arguments: none |
220 | |
221 | =back |
222 | |
223 | Resolves all the current events, first the query parameters then the |
224 | body parameters, with all the L<Reaction::UI::ViewPort> objects in the |
225 | UI. This calls L<Reaction::UI::FocusStack/apply_events>. This method |
226 | is called by L<flush>. |
227 | |
228 | =head2 flush_view |
229 | |
230 | =over |
231 | |
232 | =item Arguments: none |
233 | |
234 | =back |
235 | |
236 | Renders the page into the L<Catalyst::Response> body, unless the |
237 | response status is already set to 3xx, or the body has already been |
f1cd5548 |
238 | filled. This is done via L<Reaction::UI::View/render_window>. This |
239 | method is called by L<flush>. |
7adfd53f |
240 | |
241 | =head1 AUTHORS |
242 | |
243 | See L<Reaction::Class> for authors. |
244 | |
245 | =head1 LICENSE |
246 | |
247 | See L<Reaction::Class> for the license. |
248 | |
249 | =cut |