Clay-UI
view release on metacpan or search on metacpan
tools/make-images view on Meta::CPAN
$grid->append_row([ $cell->('Carol'), $cell->('carol@example.com'), $amount->('88.00') ]);
$page->add_child(panel('Clay::UI::Grid: every column as wide as its widest cell', $grid));
return ($page, []);
}
# The same text in a 200 wide parent under each wrap mode; the panels
# are stacked so that the overflow of the lower two stays visible.
sub build_text_wrapping ($state) {
my $page = page(layout_direction => CLAY_TOP_TO_BOTTOM, child_gap => 12);
my $sample = "Clay wraps this text to the width\nof its parent element.";
for my $mode ([ 'CLAY_TEXT_WRAP_WORDS (the default)', CLAY_TEXT_WRAP_WORDS ], [ 'CLAY_TEXT_WRAP_NEWLINES', CLAY_TEXT_WRAP_NEWLINES ], [ 'CLAY_TEXT_WRAP_NONE', CLAY_TEXT_WRAP_NONE ]) {
my ($title, $constant) = @$mode;
my $column = container(layout => { sizing => { width => sizing_fixed(200) }, padding => padding_all(6) });
$column->add_child(text($sample, wrap_mode => $constant));
$page->add_child(panel($title, $column));
}
return ($page, []);
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
my $tag = TAG_PREFIX . $Clay::UI::VERSION;
my @names = (
(map { "$_->{name}.png" } @figures),
(map { "$_->{name}.$_->{extension}" } @example_outputs),
);
my $outdated = update_pod_pictures(pod_files('lib'), $tag, $options{check}, @names);
my $font = label_font();
for my $figure (@figures) {
my $image = render_figure($figure, $font);
$outdated += store_image($image, File::Spec->catfile($options{output}, "$figure->{name}.png"), $options{check});
}
my $scratch = tempdir(CLEANUP => 1);
for my $output (@example_outputs) {
my $content = run_example($output->{script}, File::Spec->catfile($scratch, "$output->{name}.$output->{extension}"));
my $file = File::Spec->catfile($options{output}, "$output->{name}.$output->{extension}");
$outdated += store_if_changed($file, $content, $options{check});
}
exit($options{check} && $outdated ? 1 : 0);
# ---------------------------------------------------------------------------
# Pod checks
# ---------------------------------------------------------------------------
sub pod_files ($directory) {
my @files;
find({ no_chdir => 1, wanted => sub { push @files, $_ if /\.(?:pm|pod)\z/ } }, $directory);
return [ sort @files ];
}
# Dies unless the pod files together show exactly the files this script
# renders: a PNG through an <img src="IMAGE_URL_BASE/TAG/images/NAME.png">
# tag preceded by a "=for text Figure: images/NAME.png" paragraph for
# readers without HTML, any other file through a link to
# FILE_URL_BASE/TAG/images/NAME.EXT. Then points every such URL to $tag.
# Returns the number of pod files that were (or, with $check_only, would
# be) changed.
sub update_pod_pictures ($pod_files, $tag, $check_only, @names) {
my $image_url = qr{<img src="\Q@{[IMAGE_URL_BASE]}\E/[^"/]+/images/([^"/]+\.png)"};
my $file_url = qr{\Q@{[FILE_URL_BASE]}\E/[^\s/>]+/images/([^\s/>]+)};
my (%shown, %updated_pods);
for my $pod_file (@$pod_files) {
my $pod = read_file($pod_file);
my @bad_tags = grep { !/^$image_url/ } $pod =~ m{(<img\b[^>]*>)}g;
die map {"$pod_file: $_ does not start with <img src=\"@{[IMAGE_URL_BASE]}/TAG/images/NAME.png\"\n"} @bad_tags if @bad_tags;
for my $name ($pod =~ m{$image_url}g) {
die "$pod_file: no '=for text Figure: images/$name' paragraph for the <img> that shows it\n"
unless $pod =~ m{^=for text Figure: images/\Q$name\E\b}m;
$shown{$name} = 1;
}
$shown{$_} = 1 for $pod =~ m{$file_url}g;
(my $updated = $pod) =~ s{(<img src="\Q@{[IMAGE_URL_BASE]}\E/)[^"/]+/}{$1$tag/}g;
$updated =~ s{(\Q@{[FILE_URL_BASE]}\E/)[^\s/>]+/}{$1$tag/}g;
$updated_pods{$pod_file} = $updated if $updated ne $pod;
}
my %rendered = map { $_ => 1 } @names;
my @problems = (
(map {"the pod shows images/$_, which this script does not render\n"} grep { !$rendered{$_} } sort keys %shown),
(map {"no pod shows images/$_\n"} grep { !$shown{$_} } @names),
);
die @problems if @problems;
store_file($_, $updated_pods{$_}, $check_only) for sort keys %updated_pods;
return scalar keys %updated_pods;
}
# ---------------------------------------------------------------------------
# Layout and rendering
# ---------------------------------------------------------------------------
sub label_font () {
die "Imager was built without FreeType support; it is needed for the text\n"
unless $Imager::formats{ft2};
open(my $match, '-|', 'fc-match', '--format=%{file}', 'DejaVu Sans:style=Book')
or die "cannot run fc-match: $!\n";
my $file = <$match>;
close($match);
die "fc-match found no font\n" unless defined $file && -f $file;
return Imager::Font->new(file => $file, aa => 1)
// die Imager->errstr, "\n";
}
sub measure_with ($font) {
return sub ($string, $config, $userdata) {
my $size = $config->{fontSize};
my $bbox = $font->bounding_box(string => drawable($string), size => $size);
return { width => $bbox->advance_width, height => $bbox->font_height };
};
}
# A text command may still hold newlines (CLAY_TEXT_WRAP_NONE keeps the
# whole text on one line); the font has no glyph for them, so they are
# drawn, and measured, as spaces.
sub drawable ($string) {
(my $copy = $string) =~ tr/\n/ /;
return $copy;
}
( run in 1.025 second using v1.01-cache-2.11-cpan-145b269f2f3 )