Revision history for Clay-UI
0.07 2026-10-19
- A widget that loses the focus hands it on instead of leaving
nothing focused: when the focused widget is removed from the
tree, disabled or told it cannot focus, the focus goes to the
nearest ancestor that stays and can take it, or to the widget
a focus_heir hook names (a closing popup names what was
focused before it). The blur and the heir's focus fire in one
batch, so listeners never see a moment without a focus.
- A HasFocusOrder owner decides how the focus enters and leaves
its subtree. get_entry_focus is asked when Tab or Shift+Tab
arrive from outside, so a container can be a Tab stop of its
own; focus_scope names the subtree its order covers, and
Interaction::focus_after steps on from the end of a subtree,
wrapping within the scope, so a modal dialog keeps the focus
inside itself.
- Text widgets have a bounding box like every other widget,
appear under the pointer and may compose Hoverable and
Pressable, so a label can react to the pointer without a box
around it. Clay__OpenTextElement returns the id Clay gave the
text.
- Text that does not wrap (CLAY_TEXT_WRAP_NEWLINES and
CLAY_TEXT_WRAP_NONE) no longer holds its parent wide: a long
URL or path is cut off at its container's width, but only
once nothing else in the container can shrink, so every
layout that fits today stays as it is. CLAY_TEXT_WRAP_NEWLINES
now breaks only at newlines, and CLAY_TEXT_WRAP_NONE keeps the
whole text on one line; the documentation of both says so.
- $ui->content_box answers the area inside a widget's padding
for the last frame, so a composite widget no longer combines
a bounding box with a fresh to_config run.
- on($name, $handler, owner => $object) binds a listener to an
owner it holds weakly: the handler is called with the owner
and the event, and stops running once the owner is gone. A
widget class can listen on its own parts without weakening a
copy of $self in every handler.
- HasParent answers every question about the tree: ancestors,
closest, subtree, and the exported is_widget predicate;
$ui->owns says whether a widget belongs to a UI.
- A Preparable scroll container is prepared in the same render
whenever it moved, by the wheel, a drag, momentum or
scroll_to; no OnScroll listener is needed for that any more.
- Clay_MinMemorySize and Clay_Initialize take explicit element
and word counts, so a Clay::UI sizes its context without a
throwaway seed context and never touches another UI's counts
or Clay's defaults. A tree that does not fit max_element_count
is reported with the number of widgets that would have fit,
exiting transitions included.
- The test suite now checks every exported enum constant against
clay.h and every wrapper guard against the guard table, so a
Clay bump cannot silently lose a constant or a guard. The
callback dispatcher is private to the binding; nothing in Perl
can call or replace it.
0.06 2026-10-11
- First release on CPAN. Earlier versions were only on GitHub.
- Requires Perl 5.26 and List::Util 1.45; Clay::UI never loaded
on 5.22 and 5.24, although the dist claimed to support them.
- Builds and passes its tests on Windows (Strawberry Perl) and
macOS. CI tests Perl 5.26 through 5.44 on Linux, macOS and
Windows, and make release refuses to upload a commit whose CI
run has not passed.
- Every module carries the dist version on CPAN, so a newer one
can be required.
- The documentation is also published as Markdown pages in
docs/ for reading on GitHub.
0.05 2026-10-06
- Every TEXT render command carries stringOffset, the character
offset of its line in the element's text, so a renderer can
style ranges of a wrapped text.
- max_measure_text_cache_word_count sizes Clay's text
measurement cache per UI, next to max_element_count; a frame
with more words dies naming the parameter instead of with
Clay's C message.
- remove_child takes widgets and compares them by identity;
remove_child_with_id removes by id, and has_child asks whether
a widget is a direct child. A string passed to remove_child
dies and names remove_child_with_id.
- scroll_to requests the preparation of a Preparable container,
so a windowed list learns about moves made from code, and
Container gains insert_children to put entering children
before the ones it keeps.
- Tree questions have one answer each: descendants, contains,
has_focus_within and focus scopes (focusables, default_next_
focus and default_previous_focus within a subtree). The public
tree_changed hook replaces overriding private methods to learn
about attachment.
- Grid->new(layout => { childGap => 3 }) no longer dies at the
first render: slices are stored with snake_case keys, whatever
spelling was given, and readers return snake_case keys.
- Every exported name and enum range has one source, so a
layout direction can no longer be installed but rejected by
the struct schemas. Clay_EaseOut raises Clay::XS::StructError
like every other struct argument.
- Fuzzing under ASan found crashes and hangs in Clay's
transition machinery on input the binding allowed: a frame
abandoned by an exception, an element declared again without
its transition key, exit clones past the element count, and
sizing groups that counted exiting copies until their sizes
overflowed. All fixed; Clay never sees two Clay_BeginLayout
calls without a Clay_EndLayout any more.
- exit and loop control (last, next) inside a callback no longer
unwind through Clay's frames and wedge the binding: callbacks
run on their own Perl stack and an exit is re-issued once Clay
returns.
- Clay_GetScrollContainerData may be called while a frame is
declared, as upstream's scrollbar pattern does. Query wrappers
no longer re-throw held errors while a frame is open; the
error surfaces from Clay_EndLayout.
- make dist works: the dist has a MANIFEST and ships the
pristine clay.h plus the patches, which make applies on the
installing machine.
0.04 2026-10-04
- Flow layout: the new layout direction CLAY_LEFT_TO_RIGHT_WRAP
wraps children into lines like text, with line_gap and
line_sizing to space and size the lines. Toolbars, tag clouds
and galleries no longer have to be split into rows by hand.
- Stack layout: CLAY_BACK_TO_FRONT lays children on top of each
other in one box, sized by the largest of them, for badges,
captions and backgrounds. Where Pressables overlap, the one
drawn on top receives the press.
- The Disableable role: a disabled widget is never hovered,
armed, pressed or focused. can_focus answers "wanted, accepted
by the class and not disabled"; setting can_focus(0) on the
focused widget blurs it at once, and 'disabled' is a derived
state.
- Grids can share their columns and width (share_columns_with),
hold rows that span every column, reorder rows in place, and
take styled cells of any class through the GridCell marker
role. Grid rows are now sizing_grow wide, so grow cells share
the space left in a wide grid; fit grids look as before.
- Composite widgets can ask where things landed and rebuild
once per frame: bounding_box, scroll_state and scroll_to
answer from the last frame, Preparable widgets run
prepare_layout right before the layout pass, and
laid_out_revision tells renderers which revision they drew,
so changes made during render need no second frame.
- A widget can lay out internal children of its own, such as a
scrollbar floating over a scroll container, which its user's
children list and the Container mutators never see.
- Sizing groups share their largest minimum instead of their
largest size, so grid columns compress and wrap when their
grid is too narrow.
- Transitions move the revision, so renderers following the
documented loop no longer freeze on the first animated frame,
and they run only while Perl handlers are installed: a
transition key no longer delays every change by a frame.
- Several upstream Clay bugs are fixed in a patch instead of
being worked around: a completing exit transition no longer
skips the next one, an exit starts from the current state,
image and custom elements get no background rectangle, every
command of a floating element carries its zIndex,
CLAY_TEXT_WRAP_NONE never breaks, lineHeight boxes stack from
the element's top, a culled clip container still emits its
scissor commands, a floating element may attach to an element
declared later in the frame, and calling
Clay_UpdateScrollContainers twice between frames no longer
forgets every scroll position.
- A BORDER command is emitted for any positive width again; the
colour's alpha decides how a border looks, not whether the
renderer hears about it.
- Clay::UI errors name the caller's line instead of a line in
the library.
- The documentation is rewritten: Clay::Manual introduces the
concepts in order, Clay::Cookbook answers concrete tasks,
Clay::XS::Structs covers every struct key, and each function,
method, parameter, event and constant has its own heading.
Eighteen headless examples show the features, including a
PNG card and a paginated PDF invoice, and the layout model is
illustrated with figures that Clay itself laid out.
0.03 2026-10-02
- A full review found ways to crash the interpreter from plain
Perl: text Clay still held was freed, a second context handle
double-freed the context, most functions ran without a live
context, and dying with an element open crashed
Clay_EndLayout. All fixed; every function checks its context,
its arguments and the open/close balance before touching Clay.
- A dying callback now fails the call instead of being
downgraded to a warning, and callbacks cannot re-enter Clay.
The error is held and re-thrown once Clay has returned.
- Clay::XS tracks the frame state, so a frame left open by an
exception is finished before the next one begins, and the
text arena keeps exactly what Clay's exiting elements still
show.
- check_struct validates a Clay struct strictly, and Clay::UI
uses it where an attribute is set, so negative padding or an
oversized child gap dies at the caller with the snake_case
path, not at render time. Struct errors are
Clay::XS::StructError objects that stringify as before.
- Clay::UI::Revision counts every change a frame would show,
including scroll positions, so a renderer can skip frames in
which nothing moved instead of redrawing on a timer.
- max_element_count sizes each UI's context; a larger tree dies
naming the parameter instead of with an unrelated Clay error
at 8192 elements.
- Pointer and focus state live in one interaction tracker,
$ui->interaction: hover, armed, pressed and focus, with the
widget readers and the hovered / pressed / focused states
derived from it. get_hovered is replaced by
$ui->interaction->under_pointer, and the focus methods live
on the tracker. Removing a hovered or focused widget fires
OnHoverStopped and OnBlur during the removal, not on the next
render.
- Press semantics are fixed: press and release fire in the
first frame, a release needs a press on the same widget, and
hover events no longer bubble. Events are claimed right before
delivery, so an event made stale by an earlier listener is
dropped and start/stop and focus/blur stay paired.
- fire_event reports whether a handler took the event (HANDLED
or CONTINUE), and the event remembers who handled it, so a
mouse wheel report can still scroll the box under the pointer
when nothing consumed it.
- A removed widget can be attached again, so a dialog that
detaches itself on close can be opened a second time. Child
mutators moved to the Container role.
- Widget style and layout attributes are mutable: a box can be
rethemed, a label's text changed and a container's layout
switched without rebuilding the widget. Readers return copies,
so no value is shared with the caller, and to_config output is
read-only.
- Box, Grid and Text are roles, so an application composes them
into its own widget classes; the Button and ScrollPanel
classes are gone, and HasClip merged into HasScroll. Grids are
mutable: rows can be appended, inserted, replaced and removed
at any time, and the children and rows constructor parameters
are gone.
- A clip slice from a widget that does not compose HasScroll
still clips but no longer scrolls.
- The vendored Clay header follows upstream main, which fixes
four bugs in exit transitions.
0.02 2026-05-18
- The distribution is Clay-UI and the binding is Clay::XS; the
modules are grouped into Core, Style, Layout, Interaction,
Events and Enum.
- Focus: the Focusable role, OnFocus and OnBlur, and
HasFocusOrder to override the default Tab order.
- The HasStates role: every widget composing an interaction role
reports its hovered, pressed and focused states for themes.
- Not released; only on GitHub.
0.01 2026-05-16
- First version, never released: Clay::XS binds the header-only
Clay v0.14 C layout library under its exact C names, and
Clay::UI builds widget trees from Object::Pad roles, lays them
out and turns pointer input into events. Sizing groups, added
to Clay by a patch, align widgets across the tree and drive
the Grid widget. Examples render the layout to SVG and PNG.
Keyboard Shortcuts
Global
s
Focus search bar
?
Bring up this help dialog
GitHub
gp
Go to pull requests
gi
Go to GitHub issues (only if GitHub is preferred repository)