Clay::XS and Clay::UI
Perl bindings for Clay v0.14, a fast layout engine written in C. You describe a tree of boxes and text with rules such as "grow to fill the space" or "children from top to bottom"; Clay computes the position and size of everything and returns a list of render commands ("draw a rectangle here", "draw this text there"). Your code draws them - into a PNG, a PDF page, an SVG file, a window or a terminal.
The distribution has two layers:
Clay::XS- the low-level binding. Every Clay function under its C name (Clay_BeginLayout,Clay_GetElementData, ...); structs are Perl hashes with Clay's camelCase keys.Clay::UI- a widget layer built with Object::Pad. You build a tree of widget objects once, change it when your data changes, and call$ui->render. It adds pointer events (hover, press, release, scroll), keyboard focus, tables with auto-sized columns, and a revision counter that tells you when a frame must be redrawn.
Clay is vendored; there is no system library to install.
Install
perl Makefile.PL
make
make test
make install
Requirements: Perl 5.26+, a C99 compiler (GCC or Clang), the patch
program, ExtUtils::MakeMaker 7.12+, Object::Pad 0.800+ and
Object::PadX::Enum. Tests need Test2::V0 and JSON::PP. The examples
that write images or PDFs need Imager or PDF::Builder (see the table
below).
To run scripts against the built but not installed module:
perl -Ilib -Iblib/lib -Iblib/arch examples/01-minimal.pl
A first layout with Clay::UI
use v5.22;
use warnings;
use feature 'signatures';
no warnings 'experimental::signatures';
use Object::Pad 0.800;
use Clay::XS qw(:all);
use Clay::UI;
use Clay::UI::Box;
use Clay::UI::Text;
# Clay::UI ships roles; a widget class is one line that composes them.
class My::Box :strict(params) :does(Clay::UI::Box) {}
class My::Text :strict(params) :does(Clay::UI::Text) {}
my $root = My::Box->new(
layout => {
sizing => { width => sizing_grow(), height => sizing_grow() },
padding => padding_all(16),
child_alignment => { x => CLAY_ALIGN_X_CENTER, y => CLAY_ALIGN_Y_CENTER },
},
background_color => [240, 240, 240, 255],
);
$root->add_child(My::Text->new(text => 'Hello, Clay', font_size => 24, text_color => [0, 0, 0, 255]));
my $ui = Clay::UI->new(width => 800, height => 600, root => $root);
my $commands = $ui->render;
for my $command (@$commands) {
my $box = $command->{boundingBox};
printf "%-8s at %g,%g size %gx%g\n", ref $ui->widget_for($command->{userData}), @$box{qw(x y width height)};
}
The same with Clay::XS
use v5.22;
use warnings;
use feature 'signatures';
no warnings 'experimental::signatures';
use Clay::XS qw(:all);
my $ctx = Clay_Initialize(
Clay_MinMemorySize(),
{ width => 800, height => 600 },
sub ($error, $userdata) { die "Clay error: $error->{errorText}\n" },
);
Clay_SetMeasureTextFunction(sub ($text, $config, $userdata) {
return { width => length($text) * $config->{fontSize}, height => $config->{fontSize} };
});
Clay_BeginLayout();
Clay__OpenElementWithId(Clay_GetElementId('root'));
Clay__ConfigureOpenElement({
layout => {
sizing => { width => sizing_grow(), height => sizing_grow() },
padding => padding_all(16),
childAlignment => { x => CLAY_ALIGN_X_CENTER, y => CLAY_ALIGN_Y_CENTER },
},
backgroundColor => [240, 240, 240, 255],
});
Clay__OpenTextElement('Hello, Clay', { fontSize => 24, textColor => [0, 0, 0, 255] });
Clay__CloseElement();
my $commands = Clay_EndLayout();
printf "%d render commands\n", scalar @$commands; # 2: the rectangle and the text
Documentation
After installation, read the documents with perldoc; in the source
tree, with perldoc lib/Clay/Manual.pod. On GitHub, every document
is also a Markdown page in
docs/,
starting with
Clay::Manual.
| Document | What it contains |
|---|---|
| Clay::Manual | The user guide: concepts, the layout model, text, renderers, events, focus, a feature index and a glossary. Start here. |
| Clay::Cookbook | Recipes for common tasks: center an element, make a table, show a tooltip, render to PDF, paginate. |
| Clay::XS | Reference for every low-level function and constant, render commands, callbacks and errors. |
| Clay::XS::Structs | Reference for every key of an element declaration and every other struct. |
| Clay::UI | Reference for the widget layer. Each widget role (Clay::UI::Box, Clay::UI::Text, Clay::UI::Grid, Clay::UI::Role::...) and event class has its own page. |
Every function, method, attribute and struct key has a heading or an
=item of its own, spelled as in code, so
grep -rnE '^=(head.|item) (C<)?scroll_to' lib/ finds it.
The Manual and the Cookbook illustrate the layout model with figures
from images/, each laid out by Clay itself and drawn with
Imager by tools/make-images (make images regenerates them, make images-check reports stale ones). perldoc names the figure files
where the HTML shows them.
Examples
All examples run headless. Those that write a PNG or PDF (06, 15, 16)
take the output path as their first argument; their outputs are kept in
images/
(06,
15,
16).
The SVG examples write to
the path given or to standard output. The Features:
line in each file's header lists the identifiers it uses, so
grep -l 'Features:.*floating' examples/*.pl finds the examples for a
feature.
| Script | Shows | Needs |
|---|---|---|
| 01-minimal.pl | the smallest Clay::XS program; render commands as JSON | - |
| 02-sidebar-demo.pl | Clay's README layout with Clay::XS; pointer queries | - |
| 03-svg-render.pl | an SVG renderer | - |
| 04-ui-sidebar.pl | 02 rebuilt with Clay::UI widgets | - |
| 05-ui-grid.pl | Clay::UI::Grid tables, rendered to SVG | - |
| 06-png-render.pl | a PNG renderer with real fonts | Imager, DejaVu Sans Mono |
| 07-ui-flow.pl | flow layout (CLAY_LEFT_TO_RIGHT_WRAP) | - |
| 08-ui-stack.pl | stack layout (CLAY_BACK_TO_FRONT) | - |
| 09-xs-floating-scroll.pl | Clay::XS floating elements, scrolling, pointer queries | - |
| 10-xs-transitions.pl | Clay::XS transitions and easing | - |
| 11-xs-images-custom.pl | images, custom elements, aspect ratio, overlay colour, struct checks | - |
| 12-ui-interaction.pl | events, bubbling, focus, disabled widgets, states, redraw on change | - |
| 13-ui-scroll-floating.pl | Clay::UI scroll containers and tooltips | - |
| 14-ui-custom-widgets.pl | writing widget classes, internal children, sizing groups, errors | - |
| 15-og-card.pl | a 1200x630 social media preview card as PNG | Imager, DejaVu Sans |
| 16-invoice-pdf.pl | a multi-page invoice PDF from a data template | PDF::Builder |
| 17-xs-contexts-debug.pl | several contexts, capacity, culling, debug view, external scrolling, ids | - |
| 18-ui-tree-editing.pl | click-to-focus, states, editing children and grids, resizing | - |
Limitations
- Clay calls one set of transition handlers per context for every
element; Clay::UI users install them with
Clay_SetTransitionHandlersright afterClay::UI->new(seeClay::Manual, TRANSITIONS). - A context belongs to the Perl interpreter (thread) that created it.
- Neither layer reads the keyboard or draws; both are left to your program.
License
zlib/libpng, the same license as Clay. See src/clay/LICENSE.md for the
upstream notice.