$self->blessed sense make no
[catagits/Reaction.git] / lib / Reaction / UI / Widget.pm
CommitLineData
7adfd53f 1package Reaction::UI::Widget;
2
3use Reaction::Class;
4use aliased 'Reaction::UI::ViewPort';
5use aliased 'Reaction::UI::View';
d8c7a86e 6use aliased 'Reaction::UI::LayoutSet';
7adfd53f 7
5b263604 8sub DEBUG_FRAGMENTS () { $ENV{REACTION_UI_WIDGET_DEBUG_FRAGMENTS} }
c439e187 9sub DEBUG_LAYOUTS () { $ENV{REACTION_UI_WIDGET_DEBUG_LAYOUTS} }
5b263604 10
7adfd53f 11class Widget which {
12
7adfd53f 13 has 'view' => (isa => View, is => 'ro', required => 1);
d8c7a86e 14 has 'layout_set' => (isa => LayoutSet, is => 'ro', required => 1);
f2fef590 15 has 'fragment_names' => (is => 'ro', lazy_build => 1);
16 has 'basic_layout_args' => (is => 'ro', lazy_build => 1);
17
18 implements '_build_fragment_names' => as {
19 my ($self) = shift;
20 return [
21 map { /^_fragment_(.*)/; $1; }
22 grep { /^_fragment_/ }
23 map { $_->{name} }
24 $self->meta->compute_all_applicable_methods
25 ];
26 };
7adfd53f 27
28 implements 'render' => as {
f2fef590 29 my ($self, $fragment_name, $rctx, $passed_args) = @_;
30 confess "\$passed_args not hashref" unless ref($passed_args) eq 'HASH';
5b263604 31 if (DEBUG_FRAGMENTS) {
32 my $vp = $passed_args->{viewport};
33 $self->view->app->log->debug(
34 "Rendering fragment ${fragment_name} for ".ref($self)
35 ." for VP ${vp} at ".$vp->location
36 );
37 }
097e8442 38 my $args = { self => $self, %$passed_args };
f2fef590 39 my $new_args = { %$args };
40 my $render_tree = $self->_render_dispatch_order(
41 $fragment_name, $args, $new_args
42 );
43 $rctx->dispatch($render_tree, $new_args);
44 };
45
1c41e332 46 implements '_method_for_fragment_name' => as {
47 my ($self, $fragment_name) = @_;
48 return $self->can("_fragment_${fragment_name}");
49 };
50
f2fef590 51 implements '_render_dispatch_order' => as {
52 my ($self, $fragment_name, $args, $new_args) = @_;
53
54 my @render_stack = (my $render_deep = (my $render_curr = []));
55 my @layout_order = $self->layout_set->widget_order_for($fragment_name);
56
1c41e332 57 if (my $f_meth = $self->_method_for_fragment_name($fragment_name)) {
f2fef590 58 my @wclass_stack;
59 my $do_render = sub {
60 my $package = shift;
61 if (@layout_order) {
62 while ($package eq $layout_order[0][0]
63 || $layout_order[0][0]->isa($package)) {
64 my $new_curr = [];
65 my @l = @{shift(@layout_order)};
c439e187 66 if (DEBUG_LAYOUTS) {
67 $self->view->app->log->debug(
68 "Layout ${fragment_name} in ${\$l[1]->name} from ${\$l[1]->source_file}"
69 );
70 }
f2fef590 71 push(@$render_curr, [ -layout, $l[1], $fragment_name, $new_curr ]);
72 push(@render_stack, $new_curr);
73 push(@wclass_stack, $l[0]);
74 $render_deep = $render_curr = $new_curr;
75 last unless @layout_order;
76 }
77 }
78 if (@wclass_stack) {
79 while ($package ne $wclass_stack[-1]
80 && $package->isa($wclass_stack[-1])) {
81 pop(@wclass_stack);
82 $render_curr = pop(@render_stack);
83 }
84 }
85 push(@{$render_curr}, [ -render, @_ ]);
86 };
87 $self->$f_meth($do_render, $args, $new_args);
88 }
89 # if we had no fragment method or if we still have layouts left
90 if (@layout_order) {
91 while (my $l = shift(@layout_order)) {
c439e187 92 if (DEBUG_LAYOUTS) {
93 $self->view->app->log->debug(
94 "Layout ${fragment_name} in ${\$l->[1]->name} from ${\$l->[1]->source_file}"
95 );
96 }
f2fef590 97 push(@$render_deep, [
98 -layout => $l->[1], $fragment_name, ($render_deep = [])
99 ]);
100 }
101 }
102
103 return $render_stack[0];
104 };
105
106 implements '_build_basic_layout_args' => as {
107 my ($self) = @_;
108 my $args;
109 foreach my $name (@{$self->fragment_names},
110 @{$self->layout_set->layout_names}) {
111 $args->{$name} ||= sub { $self->render($name, @_); };
112 }
113 return $args;
7adfd53f 114 };
115
f2fef590 116 implements '_fragment_viewport' => as {
117 my ($self, $do_render, $args, $new_args) = @_;
7adfd53f 118 my $vp = $args->{'_'};
f2fef590 119 my ($widget, $merge_args) = $self->view->render_viewport_args($vp);
74e1591d 120 $merge_args->{outer} = { %$new_args };
aa8c0c90 121 delete @{$new_args}{keys %$new_args}; # fresh start
f2fef590 122 @{$new_args}{keys %$merge_args} = values %$merge_args;
123 $do_render->(Widget, $widget, 'widget');
124 };
125
126 implements '_fragment_widget' => as {
127 my ($self, $do_render, $args, $new_args) = @_;
128 my $merge = $self->basic_layout_args;
aa8c0c90 129#warn "Merge: ".join(', ', keys %$merge)." into: ".join(', ', keys %$new_args);
f2fef590 130 delete @{$merge}{keys %$new_args}; # nuke 'self' and 'viewport'
131 @{$new_args}{keys %$merge} = values %$merge;
7adfd53f 132 };
133
134};
135
1361;
137
138=head1 NAME
139
140Reaction::UI::Widget
141
142=head1 DESCRIPTION
143
144=head1 AUTHORS
145
146See L<Reaction::Class> for authors.
147
148=head1 LICENSE
149
150See L<Reaction::Class> for the license.
151
152=cut