1 package Reaction::UI::FocusStack;
5 use namespace::clean -except => [ qw(meta) ];
9 isa => 'Reaction::UI::ViewPort', is => 'rw',
10 clearer => 'clear_vp_head',
13 isa => 'Reaction::UI::ViewPort', is => 'rw',
14 clearer => 'clear_vp_tail',
17 isa => 'Int', is => 'rw', required => 1, default => sub { 0 }
19 has loc_prefix => (isa => 'Str', is => 'rw', predicate => 'has_loc_prefix');
21 my ($self, $class, %create) = @_;
22 my $tail = $self->vp_tail;
23 my $loc = $self->vp_count;
24 if ($self->has_loc_prefix) {
25 $loc = join('-', $self->loc_prefix, $loc);
31 (defined $tail ? ( outer => $tail ) : ()), # XXX possibly a bug in immutable?
33 if ($tail) { # if we already have a tail (non-empty vp stack)
34 $tail->inner($vp); # set the current tail's inner vp to the new vp
35 } else { # else we're currently an empty stack
36 $self->vp_head($vp); # so set the head to the new vp
38 $self->vp_count($self->vp_count + 1);
45 my $head = $self->vp_head;
46 confess "Can't pop from empty focus stack" unless defined($head);
47 my $vp = $self->vp_tail;
52 $self->vp_tail($vp->outer);
54 $self->vp_count($self->vp_count - 1);
58 sub pop_viewports_to {
60 1 while ($self->pop_viewport ne $vp);
66 my $all_events = shift;
67 my $vp = $self->vp_tail;
69 while (defined $vp && keys %$all_events) {
70 my $loc = $vp->location;
71 my %vp_events = map { $_ => delete $all_events->{$_} }
72 grep { /^${loc}[-:]/ } keys %$all_events;
73 $vp->apply_events(\%vp_events);
79 __PACKAGE__->meta->make_immutable;
86 Reaction::UI::FocusStack - A linked list of ViewPort-based objects
90 my $stack = Reaction::UI::FocusStack->new();
92 # Or more commonly, in a Reaction::UI::RootController based
93 # Catalyst Controller:
94 my $stack = $ctx->focus_stack;
96 # Add a new basic viewport inside the last viewport on the stack:
97 my $vp = $stack->push_viewport('Reaction::UI::ViewPort' =>
101 # Fetch the innermost viewport from the stack:
102 my $vp = $stack->pop_viewport();
104 # Remove all viewports inside a given viewport:
105 $stack->pop_viewports_to($vp);
107 # Create a named stack as a tangent to an existing viewport:
108 my $newstack = $vp->create_tangent('somename');
110 # Resolve current events using your stack:
111 # This is called by Reaction::UI::RootController in the end action.
112 $stack->apply_events($ctx, $param_hash);
116 A FocusStack represents a list of related L<ViewPort|Reaction::UI::ViewPort>
117 objects. The L<Reaction::UI::RootController> creates an empty stack for you in
118 it's begin action, which represents the main thread/container of the page.
119 Typically you add new ViewPorts to this stack as the main parts of your page.
120 To add multiple parallel page subparts, create a tangent from the outer
121 viewport, and add more viewports as normal.
129 =item Arguments: none
133 Create a new empty FocusStack. This is done for you in
134 L<Reaction::UI::RootController>.
140 =item Arguments: $class, %options
144 Creates a new L<Reaction::UI::ViewPort> based object and adds it to the stack.
146 The following attributes of the new ViewPort are set:
152 Is set to the preceding ViewPort in the stack.
156 Is set to the FocusStack object that created the ViewPort.
160 Is set to the location of the ViewPort in the stack.
168 =item Arguments: none
172 Removes the last/innermost ViewPort from the stack and returns it.
174 =head2 pop_viewports_to
178 =item Arguments: $viewport
182 Pops all ViewPorts off the stack until the given ViewPort object
183 remains as the last item. If passed a $viewport not on the stack, this
184 will empty the stack completely (and then die complainingly).
186 TODO: Should pop_viewports_to check $vp->focus_stack eq $self first?
192 =item Arguments: none
196 Retrieve the first ViewPort in this stack. Useful for calling
197 L<Reaction::UI::Window/render_viewport> on a
198 L<Reaction::UI::ViewPort/focus_tangent>.
204 =item Arguments: none
208 Retrieve the first ViewPort in this stack. Useful for calling
209 L<Reaction::UI::Window/render_viewport> on a
210 L<Reaction::UI::ViewPort/focus_tangent>.
216 =item Arguments: none
220 Retrieve the last ViewPort in this stack. Useful for calling
221 L<Reaction::UI::Window/render_viewport> on a
222 L<Reaction::UI::ViewPort/focus_tangent>.
228 =item Arguments: none
238 =item Arguments: $ctx, $params_hashref
242 Instruct each of the ViewPorts in the stack to apply the given events
243 to each of it's tangent stacks, and then to itself. These are applied
244 starting with the last/innermost ViewPort first.
248 See L<Reaction::Class> for authors.
252 See L<Reaction::Class> for the license.