1 package Reaction::UI::Widget;
4 use aliased 'Reaction::UI::ViewPort';
5 use aliased 'Reaction::UI::View';
6 use aliased 'Reaction::UI::LayoutSet';
8 sub DEBUG_FRAGMENTS () { $ENV{REACTION_UI_WIDGET_DEBUG_FRAGMENTS} }
12 has 'view' => (isa => View, is => 'ro', required => 1);
13 has 'layout_set' => (isa => LayoutSet, is => 'ro', required => 1);
14 has 'fragment_names' => (is => 'ro', lazy_build => 1);
15 has 'basic_layout_args' => (is => 'ro', lazy_build => 1);
17 implements '_build_fragment_names' => as {
20 map { /^_fragment_(.*)/; $1; }
21 grep { /^_fragment_/ }
23 $self->meta->compute_all_applicable_methods
27 implements 'render' => as {
28 my ($self, $fragment_name, $rctx, $passed_args) = @_;
29 confess "\$passed_args not hashref" unless ref($passed_args) eq 'HASH';
30 if (DEBUG_FRAGMENTS) {
31 my $vp = $passed_args->{viewport};
32 $self->view->app->log->debug(
33 "Rendering fragment ${fragment_name} for ".ref($self)
34 ." for VP ${vp} at ".$vp->location
37 my $args = { self => $self, %$passed_args };
38 my $new_args = { %$args };
39 my $render_tree = $self->_render_dispatch_order(
40 $fragment_name, $args, $new_args
42 $rctx->dispatch($render_tree, $new_args);
45 implements '_method_for_fragment_name' => as {
46 my ($self, $fragment_name) = @_;
47 return $self->can("_fragment_${fragment_name}");
50 implements '_render_dispatch_order' => as {
51 my ($self, $fragment_name, $args, $new_args) = @_;
53 my @render_stack = (my $render_deep = (my $render_curr = []));
54 my @layout_order = $self->layout_set->widget_order_for($fragment_name);
56 if (my $f_meth = $self->_method_for_fragment_name($fragment_name)) {
61 while ($package eq $layout_order[0][0]
62 || $layout_order[0][0]->isa($package)) {
64 my @l = @{shift(@layout_order)};
65 push(@$render_curr, [ -layout, $l[1], $fragment_name, $new_curr ]);
66 push(@render_stack, $new_curr);
67 push(@wclass_stack, $l[0]);
68 $render_deep = $render_curr = $new_curr;
69 last unless @layout_order;
73 while ($package ne $wclass_stack[-1]
74 && $package->isa($wclass_stack[-1])) {
76 $render_curr = pop(@render_stack);
79 push(@{$render_curr}, [ -render, @_ ]);
81 $self->$f_meth($do_render, $args, $new_args);
83 # if we had no fragment method or if we still have layouts left
85 while (my $l = shift(@layout_order)) {
87 -layout => $l->[1], $fragment_name, ($render_deep = [])
92 return $render_stack[0];
95 implements '_build_basic_layout_args' => as {
98 foreach my $name (@{$self->fragment_names},
99 @{$self->layout_set->layout_names}) {
100 $args->{$name} ||= sub { $self->render($name, @_); };
105 implements '_fragment_viewport' => as {
106 my ($self, $do_render, $args, $new_args) = @_;
107 my $vp = $args->{'_'};
108 my ($widget, $merge_args) = $self->view->render_viewport_args($vp);
109 delete @{$new_args}{keys %$new_args}; # fresh start
110 @{$new_args}{keys %$merge_args} = values %$merge_args;
111 $do_render->(Widget, $widget, 'widget');
114 implements '_fragment_widget' => as {
115 my ($self, $do_render, $args, $new_args) = @_;
116 my $merge = $self->basic_layout_args;
117 #warn "Merge: ".join(', ', keys %$merge)." into: ".join(', ', keys %$new_args);
118 delete @{$merge}{keys %$new_args}; # nuke 'self' and 'viewport'
119 @{$new_args}{keys %$merge} = values %$merge;
134 See L<Reaction::Class> for authors.
138 See L<Reaction::Class> for the license.