Fix NAME in POD
[catagits/Reaction.git] / lib / Reaction / UI / View.pm
CommitLineData
7adfd53f 1package Reaction::UI::View;
2
3use Reaction::Class;
4
5# declaring dependencies
7adfd53f 6use Reaction::UI::LayoutSet;
7use Reaction::UI::RenderingContext;
8a293e2e 8use aliased 'Reaction::UI::Skin';
9use aliased 'Path::Class::Dir';
7adfd53f 10
81393881 11use namespace::clean -except => [ qw(meta) ];
7adfd53f 12
7adfd53f 13
81393881 14has '_widget_cache' => (is => 'ro', default => sub { {} });
8a293e2e 15
81393881 16has '_layout_set_cache' => (is => 'ro', default => sub { {} });
7adfd53f 17
81393881 18has 'app' => (is => 'ro', required => 1);
7adfd53f 19
f94f51bf 20has 'skin_name' => (is => 'ro', required => 1, default => 'default');
7adfd53f 21
81393881 22has 'skin' => (
23 is => 'ro', lazy_build => 1,
24 handles => [ qw(create_layout_set search_path_for_type) ]
25);
7adfd53f 26
81393881 27has 'layout_set_class' => (is => 'ro', lazy_build => 1);
7adfd53f 28
81393881 29has 'rendering_context_class' => (is => 'ro', lazy_build => 1);
30sub _build_layout_set_class {
31 my ($self) = @_;
32 return $self->find_related_class('LayoutSet');
33};
34sub _build_rendering_context_class {
35 my ($self) = @_;
36 return $self->find_related_class('RenderingContext');
37};
38sub _build_skin {
39 my ($self) = @_;
40 Skin->new(
41 name => $self->skin_name, view => $self,
42 # path_to returns a File, not a Dir. Thanks, Catalyst.
43 skin_base_dir => Dir->new($self->app->path_to('share', 'skin')),
44 );
45};
46sub COMPONENT {
47 my ($class, $app, $args) = @_;
48 return $class->new(%{$args||{}}, app => $app);
49};
50sub render_window {
51 my ($self, $window) = @_;
52 my $root_vp = $window->focus_stack->vp_head;
53 my $rctx = $self->create_rendering_context;
54 my ($widget, $args) = $self->render_viewport_args($root_vp);
55 $widget->render(widget => $rctx, $args);
56};
57sub render_viewport_args {
58 my ($self, $vp) = @_;
59 my $layout_set = $self->layout_set_for($vp);
60 my $widget = $self->widget_for($vp, $layout_set);
61 return ($widget, { viewport => $vp });
62};
63sub widget_for {
64 my ($self, $vp, $layout_set) = @_;
65 return
66 $self->_widget_cache->{$layout_set->name}
67 ||= $layout_set->widget_class
68 ->new(
69 view => $self, layout_set => $layout_set
70 );
71};
72sub layout_set_for {
73 my ($self, $vp) = @_;
74 my $lset_name = eval { $vp->layout };
75 confess "Couldn't call layout method on \$vp arg ${vp}: $@" if $@;
76 $lset_name = $self->layout_set_name_from_viewport( blessed($vp) )
77 unless (length($lset_name));
78 my $cache = $self->_layout_set_cache;
79 return $cache->{$lset_name} ||= $self->create_layout_set($lset_name);
80};
349a57a4 81
81393881 82#XXX if it ever comes to it: this could be memoized. not bothering yet.
83sub layout_set_name_from_viewport {
84 my ($self, $class) = @_;
85 my ($last) = ($class =~ /.*(?:::ViewPort::)(.+?)$/);
86 #split when a non-uppercase letter meets an uppercase or when an
87 #uppercase letter is followed by another uppercase and then a non-uppercase
88 #FooBar = foo_bar; Foo_Bar = foo_bar; FOOBar = foo_bar; FooBAR = foo_bar
89 my @fragments = map {
90 join("_", split(/(?:(?<=[A-Z])(?=[A-Z][^_A-Z])|(?<=[^_A-Z])(?=[A-Z]))/, $_))
91 } split('::', $last);
92 return lc(join('/', @fragments));
93};
94sub layout_set_file_extension {
95 confess View." is abstract, you must subclass it";
96};
97sub find_related_class {
98 my ($self, $rel) = @_;
99 my $own_class = ref($self) || $self;
100 confess View." is abstract, you must subclass it" if $own_class eq View;
101 foreach my $super ($own_class->meta->class_precedence_list) {
102 next if $super eq View;
103 if ($super =~ /::View::/) {
104 (my $class = $super) =~ s/::View::/::${rel}::/;
105 if (eval { Class::MOP::load_class($class) }) {
106 return $class;
7adfd53f 107 }
108 }
81393881 109 }
110 confess "Unable to find related ${rel} class for ${own_class}";
111};
112sub create_rendering_context {
113 my ($self, @args) = @_;
114 return $self->rendering_context_class->new(
115 $self->rendering_context_args_for(@args),
116 @args,
117 );
118};
119sub rendering_context_args_for {
120 return ();
121};
122sub layout_set_args_for {
123 return ();
124};
7adfd53f 125
81393881 126__PACKAGE__->meta->make_immutable;
8a293e2e 127
7adfd53f 128
1291;