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