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