no reason for member_type to build lazily, it should be required
[catagits/Reaction.git] / lib / Reaction / UI / LayoutSet.pm
CommitLineData
7adfd53f 1package Reaction::UI::LayoutSet;
2
3use Reaction::Class;
4use File::Spec;
5
6class LayoutSet which {
7
f2fef590 8 has 'layouts' => (is => 'ro', default => sub { {} });
7adfd53f 9
10 has 'name' => (is => 'ro', required => 1);
11
8a293e2e 12 has 'source_file' => (is => 'ro', required => 1);
e22de101 13
aa8c0c90 14 has 'widget_class' => (
15 is => 'rw', lazy_fail => 1, predicate => 'has_widget_class'
16 );
17
1c41e332 18 has 'widget_type' => (is => 'rw', lazy_build => 1);
19
aa8c0c90 20 has 'super' => (is => 'rw', predicate => 'has_super');
f2fef590 21
7adfd53f 22 implements 'BUILD' => as {
23 my ($self, $args) = @_;
24 my @path = @{$args->{search_path}||[]};
b269d2bf 25 confess "No skin object provided" unless $args->{skin};
3624f977 26 confess "No top skin object provided" unless $args->{top_skin};
8a293e2e 27 $self->_load_file($self->source_file, $args);
aa8c0c90 28 unless ($self->has_widget_class) {
f6b79841 29 $self->widget_class($args->{skin}->widget_class_for($self));
aa8c0c90 30 }
f2fef590 31 };
32
33 implements 'widget_order_for' => as {
34 my ($self, $name) = @_;
aa8c0c90 35 return (
36 ($self->has_layout($name)
37 ? ([ $self->widget_class, $self ]) #;
38 : ()),
39 ($self->has_super
40 ? ($self->super->widget_order_for($name))
41 : ()),
42 );
7adfd53f 43 };
44
aa8c0c90 45 implements 'layout_names' => as {
46 my ($self) = @_;
47 my %seen;
48 return [
49 grep { !$seen{$_}++ }
50 keys %{shift->layouts},
51 ($self->has_super
52 ? (@{$self->super->layout_names})
53 : ())
54 ];
55 };
f2fef590 56
57 implements 'has_layout' => as { exists $_[0]->layouts->{$_[1]} };
58
7adfd53f 59 implements '_load_file' => as {
aa8c0c90 60 my ($self, $file, $build_args) = @_;
7adfd53f 61 my $data = $file->slurp;
f2fef590 62 my $layouts = $self->layouts;
63 # cheesy match for "=for layout name ... =something"
7adfd53f 64 # final split group also handles last in file, (?==) is lookahead
f2fef590 65 # assertion for '=' so "=for layout name1 ... =for layout name2"
66 # doesn't have the match pos go past the latter = and lose name2
aa8c0c90 67 while ($data =~ m/=(.*?)\n(.*?)(?:\n(?==)|$)/sg) {
68 my ($data, $text) = ($1, $2);
aa8c0c90 69 if ($data =~ /^for layout (\S+)/) {
70 my $fname = $1;
cf272446 71 $text =~ s/^(?:\s*\r?\n)+//; #remove leading empty lines
72 $text =~ s/[\s\r\n]+$//; #remove trailing whitespace
73 $layouts->{$fname} = $text;
aa8c0c90 74 } elsif ($data =~ /^extends (\S+)/) {
75 my $super_name = $1;
68404faa 76 my $skin;
77 if ($super_name eq 'NEXT') {
78 confess "No next skin and layout extends NEXT"
79 unless $build_args->{next_skin};
80 $skin = $build_args->{next_skin};
81 $super_name = $self->name;
82 } else {
3624f977 83 $skin = $build_args->{top_skin};
68404faa 84 }
85 $self->super($skin->create_layout_set($super_name));
1c41e332 86 } elsif ($data =~ /^widget (\S+)/) {
87 my $widget_type = $1;
88 $self->widget_type($1);
aa8c0c90 89 } elsif ($data =~ /^cut/) {
90 # no-op
91 } else {
e675ccb0 92 confess "Unparseable directive ${data} in ${file}";
aa8c0c90 93 }
7adfd53f 94 }
7adfd53f 95 };
96
1c41e332 97 implements '_build_widget_type' => as {
7adfd53f 98 my ($self) = @_;
6ab43711 99 my $widget = join('', map { ucfirst($_) } split('_', $self->name));
100 $widget = join('::', map { ucfirst($_) } split('/', $widget));
101
e22de101 102 #print STDERR "--- ", $self->name, " maps to widget $widget \n";
6ab43711 103
104 return $widget;
7adfd53f 105 };
7b78a39d 106
7adfd53f 107};
108
1091;