X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FReaction%2FUI%2FViewPort.pm;h=7b2898ea4b8af51d9fd3a61df332e701c3c16fa3;hb=9bb62506e33250989de0bbd80bdd182d9425e5c6;hp=0e9cbb2f080c52e9000ae31c09493ab861daa570;hpb=f6ec638f6489a23441e584428d4bd7ccdffaef46;p=catagits%2FReaction.git diff --git a/lib/Reaction/UI/ViewPort.pm b/lib/Reaction/UI/ViewPort.pm index 0e9cbb2..7b2898e 100644 --- a/lib/Reaction/UI/ViewPort.pm +++ b/lib/Reaction/UI/ViewPort.pm @@ -3,136 +3,144 @@ package Reaction::UI::ViewPort; use Reaction::Class; use Scalar::Util qw/blessed/; +use namespace::clean -except => [ qw(meta) ]; + + sub DEBUG_EVENTS () { $ENV{REACTION_UI_VIEWPORT_DEBUG_EVENTS} } -class ViewPort which { - - has location => (isa => 'Str', is => 'rw', required => 1); - has layout => (isa => 'Str', is => 'rw', lazy_build => 1); - has layout_args => (isa => 'HashRef', is => 'ro', default => sub { {} }); - has outer => (isa => 'Reaction::UI::ViewPort', is => 'rw', weak_ref => 1); - has inner => (isa => 'Reaction::UI::ViewPort', is => 'rw'); - has focus_stack => ( - isa => 'Reaction::UI::FocusStack', is => 'rw', weak_ref => 1 - ); - has _tangent_stacks => ( - isa => 'HashRef', is => 'ro', default => sub { {} } - ); - has ctx => (isa => 'Catalyst', is => 'ro', required => 1); - - implements _build_layout => as { - ''; - }; - - implements create_tangent => as { - my ($self, $name) = @_; - my $t_map = $self->_tangent_stacks; - if (exists $t_map->{$name}) { - confess "Can't create tangent with already existing name ${name}"; - } - my $loc = join('.', $self->location, $name); - my $tangent = Reaction::UI::FocusStack->new(loc_prefix => $loc); - $t_map->{$name} = $tangent; +has location => (isa => 'Str', is => 'rw', required => 1); +has layout => (isa => 'Str', is => 'rw', lazy_build => 1); +has layout_args => (isa => 'HashRef', is => 'ro', default => sub { {} }); +has outer => (isa => 'Reaction::UI::ViewPort', is => 'rw', weak_ref => 1); +has inner => (isa => 'Reaction::UI::ViewPort', is => 'rw'); +has focus_stack => ( + isa => 'Reaction::UI::FocusStack', is => 'rw', weak_ref => 1 +); +has _tangent_stacks => ( + isa => 'HashRef', is => 'ro', default => sub { {} } +); +has ctx => (isa => 'Catalyst', is => 'ro', weak_ref => 1); #, required => 1); + +sub _build_layout { + ''; +} + +sub create_tangent { + my ($self, $name) = @_; + my $t_map = $self->_tangent_stacks; + if (exists $t_map->{$name}) { + confess "Can't create tangent with already existing name ${name}"; + } + my $loc = join('.', $self->location, $name); + my $tangent = Reaction::UI::FocusStack->new(loc_prefix => $loc); + $t_map->{$name} = $tangent; + return $tangent; +} + +sub focus_tangent { + my ($self, $name) = @_; + if (my $tangent = $self->_tangent_stacks->{$name}) { return $tangent; - }; - - implements focus_tangent => as { - my ($self, $name) = @_; - if (my $tangent = $self->_tangent_stacks->{$name}) { - return $tangent; - } else { - return; - } - }; - - implements focus_tangents => as { - return keys %{shift->_tangent_stacks}; - }; - - implements child_event_sinks => as { - my $self = shift; - return values %{$self->_tangent_stacks}; - }; - - implements apply_events => as { - my ($self, $ctx, $events) = @_; - return unless keys %$events; - $self->apply_child_events($ctx, $events); - $self->apply_our_events($ctx, $events); - }; - - implements apply_child_events => as { - my ($self, $ctx, $events) = @_; - return unless keys %$events; - foreach my $child ($self->child_event_sinks) { - confess blessed($child) ."($child) is not a valid object" - unless blessed($child) && $child->can('apply_events'); - $child->apply_events($ctx, $events); + } else { + return; + } +} + +sub focus_tangents { + return keys %{shift->_tangent_stacks}; +} + +sub child_event_sinks { + my $self = shift; + return values %{$self->_tangent_stacks}; +} + +sub apply_events { + my ($self, $events) = @_; + return unless keys %$events; + $self->apply_child_events($events); + $self->apply_our_events($events); +} + +sub apply_child_events { + my ($self, $events) = @_; + return unless keys %$events; + foreach my $child ($self->child_event_sinks) { + confess blessed($child) ."($child) is not a valid object" + unless blessed($child) && $child->can('apply_events'); + my $loc = $child->location; + my %child_events = map { $_ => delete $events->{$_} } + grep { /^${loc}[-:]/ } keys %$events; + $child->apply_events(\%child_events); + } +} + +sub apply_our_events { + my ($self, $events) = @_; + my $loc = $self->location; + my %our_events; + foreach my $key (keys %$events) { + if ($key =~ m/^${loc}:(.*)$/) { + $our_events{$1} = delete $events->{$key}; } - }; - - implements apply_our_events => as { - my ($self, $ctx, $events) = @_; - my @keys = keys %$events; - return unless @keys; - my $loc = $self->location; - my %our_events; - foreach my $key (keys %$events) { - if ($key =~ m/^${loc}:(.*)$/) { - $our_events{$1} = $events->{$key}; - } + } + $self->handle_events(\%our_events) if keys %our_events; +} + +sub handle_events { + my ($self, $events) = @_; + my $exists = exists $events->{exists}; + if ($exists) { + my %force = $self->force_events; + my @need = grep { !exists $events->{$_} } keys %force; + @{$events}{@need} = @force{@need}; + } + foreach my $event ($self->accept_events) { + if (exists $events->{$event}) { + $self->_dump_event($event, $events->{$event}) if DEBUG_EVENTS; + $self->$event($events->{$event}); } - if (keys %our_events) { - #warn "$self: events ".join(', ', %our_events)."\n"; - $self->handle_events(\%our_events); - } - }; - - implements handle_events => as { - my ($self, $events) = @_; - foreach my $event ($self->accept_events) { - if (exists $events->{$event}) { - if (DEBUG_EVENTS) { - my $name = join(' at ', $self, $self->location); - $self->ctx->log->debug( - "Applying Event: $event on $name with value: " - .$events->{$event} - ); - } - $self->$event($events->{$event}); - } - } - }; + } +} - implements accept_events => as { () }; +sub _dump_event { + my ( $self, $name, $value ) = @_; + my $vp_name = join(' at ', $self, $self->location); + print STDERR + "Applying Event: $name on $vp_name with value: " + . (defined $value ? $value : '') . "\n"; +} - implements event_id_for => as { - my ($self, $name) = @_; - return join(':', $self->location, $name); - }; +sub accept_events { () } - implements sort_by_spec => as { - my ($self, $spec, $items) = @_; - return $items if not defined $spec; +sub force_events { () } - my @order; - if (ref $spec eq 'ARRAY') { - @order = @$spec; - } - elsif (not ref $spec) { - return $items unless length $spec; - @order = split /\s+/, $spec; - } +sub event_id_for { + my ($self, $name) = @_; + return join(':', $self->location, $name); +} - my %order_map = map {$_ => 0} @$items; - for my $order_num (0..$#order) { - $order_map{ $order[$order_num] } = ($#order - $order_num) + 1; - } +sub sort_by_spec { + my ($self, $spec, $items) = @_; + return [sort @$items] unless $spec; + + my @order; + if (ref $spec eq 'ARRAY') { + @order = @$spec; + } elsif (not ref $spec) { + @order = split /\s+/, $spec; + } + + my %order_map = map {$_ => 0} @$items; + for my $order_num (0..$#order) { + $order_map{ $order[$order_num] } = ($#order - $order_num) + 1; + } + + return [sort {$order_map{$b} <=> $order_map{$a}} @$items]; +} - return [sort {$order_map{$b} <=> $order_map{$a}} @$items]; - }; +__PACKAGE__->meta->make_immutable; -}; 1; @@ -145,7 +153,7 @@ Reaction::UI::ViewPort - Page layout building block # Create a new ViewPort: # $stack isa Reaction::UI::FocusStack object - my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xthml'); + my $vp = $stack->push_viewport('Reaction::UI::ViewPort', layout => 'xhtml'); # Fetch ViewPort higher up the stack (further out) my $outer = $vp->outer(); @@ -245,6 +253,16 @@ The layout attribute can either be specifically passed when calling C, or it will be determined using the last part of the ViewPorts classname. +=item layout_args + +This read-only hashref attribute will pass all it's keys as variables to the +layout at render time. They should be accessible from both the layout templates +and the widget, if applicable, through the C<%_> hash. + + $controller->push_viewport(VPName, layout => 'foo', layout_args => { bar => 'bar'}); + $_{bar} #in widget + [% bar %] in template + =item column_order This is generally used by more specialised ViewPorts such as the