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