| Reaction documentation | Contained in the Reaction distribution. |
Reaction::UI::Widget - The base widget.
This is the base class for all widgets. It provides common functionality and fragments. It is also concerned with the rendering of the fragments.
This is the root fragment for every widget.
This fragment is used to render another viewport from inside a fragment. It
assumes the viewport is stored in the _ argument.
Log additional debugging output for fragment processing.
Log additional debugging output for layout processing.
The widget's view object. Is required, readonly and must be a Reaction::UI::View.
The widget's layout set. Is required, readonly and must be a Reaction::UI::LayoutSet.
List of names of known fragments for the current widget. Lazily computed from all
methods that are named in the pattern _fragment_$name.
A lazily built hash reference containing the rendered fragments defined in both the widget and the layout set, keyed by the fragments' names.
$widget->render('fragment_name', $reaction_ctx, \%passed_args);
This method is concerned with rendering a fragment.
See Reaction::Class for authors.
See Reaction::Class for the license.
| Reaction documentation | Contained in the Reaction distribution. |
package Reaction::UI::Widget; use Reaction::Class; use aliased 'Reaction::UI::ViewPort'; use aliased 'Reaction::UI::View'; use aliased 'Reaction::UI::LayoutSet'; use namespace::clean -except => [ qw(meta) ]; sub DEBUG_FRAGMENTS () { $ENV{REACTION_UI_WIDGET_DEBUG_FRAGMENTS} } sub DEBUG_LAYOUTS () { $ENV{REACTION_UI_WIDGET_DEBUG_LAYOUTS} } has 'view' => (isa => View, is => 'ro', required => 1); has 'layout_set' => (isa => LayoutSet, is => 'ro', required => 1); has 'fragment_names' => (is => 'ro', lazy_build => 1); has 'basic_layout_args' => (is => 'ro', lazy_build => 1); sub _build_fragment_names { my ($self) = shift; return [ map { /^_fragment_(.*)/; $1; } grep { /^_fragment_/ } map { $_->{name} } $self->meta->get_all_methods ]; }; sub render { my ($self, $fragment_name, $rctx, $passed_args) = @_; confess "\$passed_args not hashref" unless ref($passed_args) eq 'HASH'; if (DEBUG_FRAGMENTS) { my $vp = $passed_args->{viewport}; $self->view->app->log->debug( "Rendering fragment ${fragment_name} for ".ref($self) ." for VP ${vp} at ".$vp->location ); } my $args = { self => $self, %$passed_args }; my $new_args = { %$args }; my $render_tree = $self->_render_dispatch_order( $fragment_name, $args, $new_args ); $rctx->dispatch($render_tree, $new_args); }; sub _method_for_fragment_name { my ($self, $fragment_name) = @_; return $self->can("_fragment_${fragment_name}"); }; sub _render_dispatch_order { my ($self, $fragment_name, $args, $new_args) = @_; my @render_stack = (my $render_deep = (my $render_curr = [])); my @layout_order = $self->layout_set->widget_order_for($fragment_name); if (my $f_meth = $self->_method_for_fragment_name($fragment_name)) { my @wclass_stack; my $do_render = sub { my $package = shift; if (@layout_order) { while ($package eq $layout_order[0][0] || $layout_order[0][0]->isa($package)) { my $new_curr = []; my @l = @{shift(@layout_order)}; if (DEBUG_LAYOUTS) { $self->view->app->log->debug( "Layout ${fragment_name} in ${\$l[1]->name} from ${\$l[1]->source_file}" ); } push(@$render_curr, [ -layout, $l[1], $fragment_name, $new_curr ]); push(@render_stack, $new_curr); push(@wclass_stack, $l[0]); $render_deep = $render_curr = $new_curr; last unless @layout_order; } } if (@wclass_stack) { while ($package ne $wclass_stack[-1] && $package->isa($wclass_stack[-1])) { pop(@wclass_stack); $render_curr = pop(@render_stack); } } push(@{$render_curr}, [ '-render', @_ ]); }; $self->$f_meth($do_render, $args, $new_args); } # if we had no fragment method or if we still have layouts left if (@layout_order) { while (my $l = shift(@layout_order)) { if (DEBUG_LAYOUTS) { $self->view->app->log->debug( "Layout ${fragment_name} in ${\$l->[1]->name} from ${\$l->[1]->source_file}" ); } push(@$render_deep, [ -layout => $l->[1], $fragment_name, ($render_deep = []) ]); } } return $render_stack[0]; }; sub _build_basic_layout_args { my ($self) = @_; my $args; foreach my $name (@{$self->fragment_names}, @{$self->layout_set->layout_names}) { $args->{$name} ||= sub { $self->render($name, @_); }; } return $args; }; sub _fragment_viewport { my ($self, $do_render, $args, $new_args) = @_; my $vp = $args->{'_'}; my ($widget, $merge_args) = $self->view->render_viewport_args($vp); $merge_args->{outer} = { %$new_args }; delete @{$new_args}{keys %$new_args}; # fresh start @{$new_args}{keys %$merge_args} = values %$merge_args; $do_render->(Widget, $widget, 'widget'); }; sub _fragment_widget { my ($self, $do_render, $args, $new_args) = @_; my $merge = $self->basic_layout_args; #warn "Merge: ".join(', ', keys %$merge)." into: ".join(', ', keys %$new_args); delete @{$merge}{keys %$new_args}; # nuke 'self' and 'viewport' @{$new_args}{keys %$merge} = values %$merge; }; __PACKAGE__->meta->make_immutable; 1;