Compare commits

...

9 Commits

Author SHA1 Message Date
Harrison Lambeth
5bb70d9469
Merge 38bb241ced into 3961720ef0 2025-02-11 06:15:02 +00:00
Harrison Lambeth
38bb241ced small fixes 2025-02-10 23:14:44 -07:00
Harrison Lambeth
f4933c6669 small refactor 2025-02-10 23:14:44 -07:00
Harrison Lambeth
61ba36753b Fix Clay_OnHover 2025-02-10 23:14:44 -07:00
Harrison Lambeth
4f4605eff9 Add proper support for function arguments 2025-02-10 23:14:44 -07:00
Harrison Lambeth
7c65f31f46 Some fixes after rebasing 2025-02-10 23:14:44 -07:00
Harrison Lambeth
01025e9157 initial pass 2025-02-10 23:14:44 -07:00
Nic Barker
3961720ef0 [Core & Documentation] Cleanup public / private API and internal document public API via comments
Some checks are pending
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Waiting to run
2025-02-11 17:11:03 +13:00
Nic Barker
dd1f018444 [Documentation] Add inline documentation comments for subfields of Clay_ElementDeclaration 2025-02-11 14:14:55 +13:00
23 changed files with 1733 additions and 163 deletions

5
.gitignore vendored
View File

@ -5,3 +5,8 @@ cmake-build-release/
node_modules/
*.dSYM
.vs/
bindings/odin/clay-odin/tmp/
generator/__pycache__/
generator/generators/__pycache__/

Binary file not shown.

Binary file not shown.

Binary file not shown.

477
clay.h
View File

@ -21,11 +21,6 @@
#include <arm_neon.h>
#endif
#ifdef __JETBRAINS_IDE__
// Help jetbrains IDEs like CLion and Rider with intellisense & debugging
#define CLAY_IMPLEMENTATION
#endif
// -----------------------------------------
// HEADER DECLARATIONS ---------------------
// -----------------------------------------
@ -49,10 +44,6 @@
// Public Macro API ------------------------
#define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper
#define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type)
#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped
#define CLAY__MAX(x, y) (((x) > (y)) ? (x) : (y))
#define CLAY__MIN(x, y) (((x) < (y)) ? (x) : (y))
@ -98,19 +89,20 @@ static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
/* This macro looks scary on the surface, but is actually quite simple.
It turns a macro call like this:
CLAY(
CLAY_RECTANGLE(),
CLAY_ID()
) {
CLAY({
.id = CLAY_ID("Container"),
.backgroundColor = { 255, 200, 200, 255 }
}) {
...children declared here
}
Into calls like this:
Clay_OpenElement();
CLAY_RECTANGLE();
CLAY_ID();
Clay_ElementPostConfiguration();
Clay_ConfigureOpenElement((Clay_ElementDeclaration) {
.id = CLAY_ID("Container"),
.backgroundColor = { 255, 200, 200, 255 }
});
...children declared here
Clay_CloseElement();
@ -125,6 +117,15 @@ static uint8_t CLAY__ELEMENT_DEFINITION_LATCH;
++CLAY__ELEMENT_DEFINITION_LATCH, Clay__CloseElement() \
)
// These macros exist to allow the CLAY() macro to be called both with an inline struct definition, such as
// CLAY({ .id = something... });
// As well as by passing a predefined declaration struct
// Clay_ElementDeclaration declarationStruct = ...
// CLAY(declarationStruct);
#define CLAY__WRAPPER_TYPE(type) Clay__##type##Wrapper
#define CLAY__WRAPPER_STRUCT(type) typedef struct { type wrapped; } CLAY__WRAPPER_TYPE(type)
#define CLAY__CONFIG_WRAPPER(type, ...) (CLAY__INIT(CLAY__WRAPPER_TYPE(type)) { __VA_ARGS__ }).wrapped
#define CLAY_TEXT(text, textConfig) Clay__OpenTextElement(text, textConfig)
#ifdef __cplusplus
@ -158,22 +159,27 @@ extern "C" {
#endif
// Utility Structs -------------------------
// Note: Clay_String is not guaranteed to be null terminated. It may be if created from a literal C string,
// but it is also used to represent slices.
typedef struct {
int32_t length;
// The underlying character memory. Note: this will not be copied and will not extend the lifetime of the underlying memory.
const char *chars;
} Clay_String;
// Clay_StringSlice is used to represent non owning string slices, and includes
// a baseChars field which points to the string this slice is derived from.
typedef struct {
int32_t length;
const char *chars;
// The source string / char* that this slice was derived from
const char *baseChars;
const char *baseChars; // The source string / char* that this slice was derived from
} Clay_StringSlice;
typedef struct Clay_Context Clay_Context;
// Clay_Arena is a memory arena structure that is used by clay to manage its internal allocations.
// Rather than creating it by hand, it's easier to use Clay_CreateArenaWithCapacityAndMemory()
typedef struct {
uintptr_t nextAllocation;
size_t capacity;
@ -188,6 +194,7 @@ typedef struct {
float x, y;
} Clay_Vector2;
// Internally clay conventionally represents colors as 0-255, but interpretation is up to the renderer.
typedef struct {
float r, g, b, a;
} Clay_Color;
@ -196,14 +203,18 @@ typedef struct {
float x, y, width, height;
} Clay_BoundingBox;
// baseId + offset = id
// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.
// Represents a hashed string ID used for identifying and finding specific clay UI elements, required
// by functions such as Clay_PointerOver() and Clay_GetElementData().
typedef struct {
uint32_t id;
uint32_t offset;
uint32_t baseId;
Clay_String stringId;
uint32_t id; // The resulting hash generated from the other fields.
uint32_t offset; // A numerical offset applied after computing the hash from stringId.
uint32_t baseId; // A base hash value to start from, for example the parent element ID is used when calculating CLAY_ID_LOCAL().
Clay_String stringId; // The string id to hash.
} Clay_ElementId;
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
typedef struct {
float topLeft;
float topRight;
@ -211,66 +222,78 @@ typedef struct {
float bottomRight;
} Clay_CornerRadius;
typedef CLAY_PACKED_ENUM {
CLAY__ELEMENT_CONFIG_TYPE_NONE,
CLAY__ELEMENT_CONFIG_TYPE_BORDER,
CLAY__ELEMENT_CONFIG_TYPE_FLOATING,
CLAY__ELEMENT_CONFIG_TYPE_SCROLL,
CLAY__ELEMENT_CONFIG_TYPE_IMAGE,
CLAY__ELEMENT_CONFIG_TYPE_TEXT,
CLAY__ELEMENT_CONFIG_TYPE_CUSTOM,
CLAY__ELEMENT_CONFIG_TYPE_SHARED,
} Clay__ElementConfigType;
// Element Configs ---------------------------
// Layout
// Controls the direction in which child elements will be automatically laid out.
typedef CLAY_PACKED_ENUM {
// (Default) Lays out child elements from left to right with increasing x.
CLAY_LEFT_TO_RIGHT,
// Lays out child elements from top to bottom with increasing y.
CLAY_TOP_TO_BOTTOM,
} Clay_LayoutDirection;
// Controls the alignment along the x axis (horizontal) of child elements.
typedef CLAY_PACKED_ENUM {
// (Default) Aligns child elements to the left hand side of this element, offset by padding.width.left
CLAY_ALIGN_X_LEFT,
// Aligns child elements to the right hand side of this element, offset by padding.width.right
CLAY_ALIGN_X_RIGHT,
// Aligns child elements horizontally to the center of this element
CLAY_ALIGN_X_CENTER,
} Clay_LayoutAlignmentX;
// Controls the alignment along the y axis (vertical) of child elements.
typedef CLAY_PACKED_ENUM {
// (Default) Aligns child elements to the top of this element, offset by padding.width.top
CLAY_ALIGN_Y_TOP,
// Aligns child elements to the bottom of this element, offset by padding.width.bottom
CLAY_ALIGN_Y_BOTTOM,
// Aligns child elements vertiically to the center of this element
CLAY_ALIGN_Y_CENTER,
} Clay_LayoutAlignmentY;
// Controls how the element takes up space inside its parent container.
typedef CLAY_PACKED_ENUM {
// (default) Wraps tightly to the size of the element's contents.
CLAY__SIZING_TYPE_FIT,
// Expands along this axis to fill available space in the parent element, sharing it with other GROW elements.
CLAY__SIZING_TYPE_GROW,
// Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.
CLAY__SIZING_TYPE_PERCENT,
// Clamps the axis size to an exact size in pixels.
CLAY__SIZING_TYPE_FIXED,
} Clay__SizingType;
// Controls how child elements are aligned on each axis.
typedef struct {
Clay_LayoutAlignmentX x;
Clay_LayoutAlignmentY y;
Clay_LayoutAlignmentX x; // Controls alignment of children along the x axis.
Clay_LayoutAlignmentY y; // Controls alignment of children along the y axis.
} Clay_ChildAlignment;
// Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to,
// overriding sizing types such as FIT or GROW.
typedef struct {
float min;
float max;
float min; // The smallest final size of the element on this axis will be this value in pixels.
float max; // The largest final size of the element on this axis will be this value in pixels.
} Clay_SizingMinMax;
// Controls the sizing of this element along one axis inside its parent container.
typedef struct {
union {
Clay_SizingMinMax minMax;
float percent;
Clay_SizingMinMax minMax; // Controls the minimum and maximum size in pixels that this element is allowed to grow or shrink to, overriding sizing types such as FIT or GROW.
float percent; // Expects 0-1 range. Clamps the axis size to a percent of the parent container's axis size minus padding and child gaps.
} size;
Clay__SizingType type;
Clay__SizingType type; // Controls how the element takes up space inside its parent container.
} Clay_SizingAxis;
// Controls the sizing of this element along one axis inside its parent container.
typedef struct {
Clay_SizingAxis width;
Clay_SizingAxis height;
Clay_SizingAxis width; // Controls the width sizing of the element, along the x axis.
Clay_SizingAxis height; // Controls the height sizing of the element, along the y axis.
} Clay_Sizing;
// Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children
// will be placed.
typedef struct {
uint16_t left;
uint16_t right;
@ -280,46 +303,70 @@ typedef struct {
CLAY__WRAPPER_STRUCT(Clay_Padding);
// Controls various settings that affect the size and position of an element, as well as the sizes and positions
// of any child elements.
typedef struct {
Clay_Sizing sizing;
Clay_Padding padding;
uint16_t childGap;
Clay_ChildAlignment childAlignment;
Clay_LayoutDirection layoutDirection;
Clay_Sizing sizing; // Controls the sizing of this element inside it's parent container, including FIT, GROW, PERCENT and FIXED sizing.
Clay_Padding padding; // Controls "padding" in pixels, which is a gap between the bounding box of this element and where its children will be placed.
uint16_t childGap; // Controls the gap in pixels between child elements along the layout axis (horizontal gap for LEFT_TO_RIGHT, vertical gap for TOP_TO_BOTTOM).
Clay_ChildAlignment childAlignment; // Controls how child elements are aligned on each axis.
Clay_LayoutDirection layoutDirection; // Controls the direction in which child elements will be automatically laid out.
} Clay_LayoutConfig;
CLAY__WRAPPER_STRUCT(Clay_LayoutConfig);
extern Clay_LayoutConfig CLAY_LAYOUT_DEFAULT;
// Text
// Controls how text "wraps", that is how it is broken into multiple lines when there is insufficient horizontal space.
typedef CLAY_PACKED_ENUM {
// (default) breaks on whitespace characters.
CLAY_TEXT_WRAP_WORDS,
// Don't break on space characters, only on newlines.
CLAY_TEXT_WRAP_NEWLINES,
// Disable text wrapping entirely.
CLAY_TEXT_WRAP_NONE,
} Clay_TextElementConfigWrapMode;
// Controls various functionality related to text elements.
typedef struct {
// The RGBA color of the font to render, conventionally specified as 0-255.
Clay_Color textColor;
// An integer transparently passed to Clay_MeasureText to identify the font to use.
// The debug view will pass fontId = 0 for its internal text.
uint16_t fontId;
// Controls the size of the font. Handled by the function provided to Clay_MeasureText.
uint16_t fontSize;
// Controls extra horizontal spacing between characters. Handled by the function provided to Clay_MeasureText.
uint16_t letterSpacing;
// Controls additional vertical space between wrapped lines of text.
uint16_t lineHeight;
// Controls how text "wraps", that is how it is broken into multiple lines when there is insufficient horizontal space.
// CLAY_TEXT_WRAP_WORDS (default) breaks on whitespace characters.
// CLAY_TEXT_WRAP_NEWLINES doesn't break on space characters, only on newlines.
// CLAY_TEXT_WRAP_NONE disables wrapping entirely.
Clay_TextElementConfigWrapMode wrapMode;
// When set to true, clay will hash the entire text contents of this string as an identifier for its internal
// text measurement cache, rather than just the pointer and length. This will incur significant performance cost for
// long bodies of text.
bool hashStringContents;
} Clay_TextElementConfig;
CLAY__WRAPPER_STRUCT(Clay_TextElementConfig);
// Image
// Image --------------------------------
// Controls various settings related to image elements.
typedef struct {
void* imageData;
Clay_Dimensions sourceDimensions;
void* imageData; // A transparent pointer used to pass image data through to the renderer.
Clay_Dimensions sourceDimensions; // The original dimensions of the source image, used to control aspect ratio.
} Clay_ImageElementConfig;
CLAY__WRAPPER_STRUCT(Clay_ImageElementConfig);
// Floating
// Floating -----------------------------
// Controls where a floating element is offset relative to its parent element.
// Note: see https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce for a visual explanation.
typedef CLAY_PACKED_ENUM {
CLAY_ATTACH_POINT_LEFT_TOP,
CLAY_ATTACH_POINT_LEFT_CENTER,
@ -332,190 +379,322 @@ typedef CLAY_PACKED_ENUM {
CLAY_ATTACH_POINT_RIGHT_BOTTOM,
} Clay_FloatingAttachPointType;
// Controls where a floating element is offset relative to its parent element.
typedef struct {
Clay_FloatingAttachPointType element;
Clay_FloatingAttachPointType parent;
Clay_FloatingAttachPointType element; // Controls the origin point on a floating element that attaches to its parent.
Clay_FloatingAttachPointType parent; // Controls the origin point on the parent element that the floating element attaches to.
} Clay_FloatingAttachPoints;
// Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.
typedef CLAY_PACKED_ENUM {
// (default) "Capture" the pointer event and don't allow events like hover and click to pass through to elements underneath.
CLAY_POINTER_CAPTURE_MODE_CAPTURE,
// CLAY_POINTER_CAPTURE_MODE_PARENT, TODO pass pointer through to attached parent
// Transparently pass through pointer events like hover and click to elements underneath the floating element.
CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH,
} Clay_PointerCaptureMode;
// Controls which element a floating element is "attached" to (i.e. relative offset from).
typedef CLAY_PACKED_ENUM {
// (default) Disables floating for this element.
CLAY_ATTACH_TO_NONE,
// Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.
CLAY_ATTACH_TO_PARENT,
// Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.
CLAY_ATTACH_TO_ELEMENT_WITH_ID,
// Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to "absolute positioning".
CLAY_ATTACH_TO_ROOT,
} Clay_FloatingAttachToElement;
// Controls various settings related to "floating" elements, which are elements that "float" above other elements, potentially overlapping their boundaries,
// and not affecting the layout of sibling or parent elements.
typedef struct {
// Offsets this floating element by the provided x,y coordinates from its attachPoints.
Clay_Vector2 offset;
// Expands the boundaries of the outer floating element without affecting its children.
Clay_Dimensions expand;
// When used in conjunction with .attachTo = CLAY_ATTACH_TO_ELEMENT_WITH_ID, attaches this floating element to the element in the hierarchy with the provided ID.
// Hint: attach the ID to the other element with .id = CLAY_ID("yourId"), and specify the id the same way, with .parentId = CLAY_ID("yourId").id
uint32_t parentId;
// Controls the z index of this floating element and all its children. Floating elements are sorted in ascending z order before output.
// zIndex is also passed to the renderer for all elements contained within this floating element.
int16_t zIndex;
// Controls how mouse pointer events like hover and click are captured or passed through to elements underneath / behind a floating element.
// Enum is of the form CLAY_ATTACH_POINT_foo_bar. See Clay_FloatingAttachPoints for more details.
// Note: see <img src="https://github.com/user-attachments/assets/b8c6dfaa-c1b1-41a4-be55-013473e4a6ce />
// and <img src="https://github.com/user-attachments/assets/ebe75e0d-1904-46b0-982d-418f929d1516 /> for a visual explanation.
Clay_FloatingAttachPoints attachPoints;
// Controls how mouse pointer events like hover and click are captured or passed through to elements underneath a floating element.
// CLAY_POINTER_CAPTURE_MODE_CAPTURE (default) - "Capture" the pointer event and don't allow events like hover and click to pass through to elements underneath.
// CLAY_POINTER_CAPTURE_MODE_PASSTHROUGH - Transparently pass through pointer events like hover and click to elements underneath the floating element.
Clay_PointerCaptureMode pointerCaptureMode;
// Controls which element a floating element is "attached" to (i.e. relative offset from).
// CLAY_ATTACH_TO_NONE (default) - Disables floating for this element.
// CLAY_ATTACH_TO_PARENT - Attaches this floating element to its parent, positioned based on the .attachPoints and .offset fields.
// CLAY_ATTACH_TO_ELEMENT_WITH_ID - Attaches this floating element to an element with a specific ID, specified with the .parentId field. positioned based on the .attachPoints and .offset fields.
// CLAY_ATTACH_TO_ROOT - Attaches this floating element to the root of the layout, which combined with the .offset field provides functionality similar to "absolute positioning".
Clay_FloatingAttachToElement attachTo;
} Clay_FloatingElementConfig;
CLAY__WRAPPER_STRUCT(Clay_FloatingElementConfig);
// Custom
// Custom -----------------------------
// Controls various settings related to custom elements.
typedef struct {
// A transparent pointer through which you can pass custom data to the renderer.
// Generates CUSTOM render commands.
void* customData;
} Clay_CustomElementConfig;
CLAY__WRAPPER_STRUCT(Clay_CustomElementConfig);
// Scroll
// Scroll -----------------------------
// Controls the axis on which an element switches to "scrolling", which clips the contents and allows scrolling in that direction.
typedef struct {
bool horizontal;
bool vertical;
bool horizontal; // Clip overflowing elements on the X axis and allow scrolling left and right.
bool vertical; // Clip overflowing elements on the YU axis and allow scrolling up and down.
} Clay_ScrollElementConfig;
CLAY__WRAPPER_STRUCT(Clay_ScrollElementConfig);
// Shared
typedef struct {
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
void* userData;
} Clay_SharedElementConfig;
// Border -----------------------------
CLAY__WRAPPER_STRUCT(Clay_SharedElementConfig);
// Border
// Controls the widths of individual element borders.
typedef struct {
uint16_t left;
uint16_t right;
uint16_t top;
uint16_t bottom;
// Creates borders between each child element, depending on the .layoutDirection.
// e.g. for LEFT_TO_RIGHT, borders will be vertical lines, and for TOP_TO_BOTTOM borders will be horizontal lines.
// .betweenChildren borders will result in individual RECTANGLE render commands being generated.
uint16_t betweenChildren;
} Clay_BorderWidth;
// Controls settings related to element borders.
typedef struct {
Clay_Color color;
Clay_BorderWidth width;
Clay_Color color; // Controls the color of all borders with width > 0. Conventionally represented as 0-255, but interpretation is up to the renderer.
Clay_BorderWidth width; // Controls the widths of individual borders. At least one of these should be > 0 for a BORDER render command to be generated.
} Clay_BorderElementConfig;
CLAY__WRAPPER_STRUCT(Clay_BorderElementConfig);
// Render Command Data -----------------------------
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
typedef struct {
// A string slice containing the text to be rendered.
// Note: this is not guaranteed to be null terminated.
Clay_StringSlice stringContents;
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color textColor;
// An integer representing the font to use to render this text, transparently passed through from the text declaration.
uint16_t fontId;
uint16_t fontSize;
// Specifies the extra whitespace gap in pixels between each character.
uint16_t letterSpacing;
// The height of the bounding box for this line of text.
uint16_t lineHeight;
} Clay_TextRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
typedef struct {
// The solid background color to fill this rectangle with. Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
Clay_CornerRadius cornerRadius;
} Clay_RectangleRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE
typedef struct {
// The tint color for this image. Note that the default value is 0,0,0,0 and should likely be interpreted
// as "untinted".
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of this image.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
Clay_CornerRadius cornerRadius;
// The original dimensions of the source image, used to control aspect ratio.
Clay_Dimensions sourceDimensions;
// A pointer transparently passed through from the original element definition, typically used to represent image data.
void* imageData;
} Clay_ImageRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM
typedef struct {
// Passed through from .backgroundColor in the original element declaration.
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of this custom element.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
Clay_CornerRadius cornerRadius;
// A pointer transparently passed through from the original element definition.
void* customData;
} Clay_CustomRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_START || commandType == CLAY_RENDER_COMMAND_TYPE_SCISSOR_END
typedef struct {
bool horizontal;
bool vertical;
} Clay_ScrollRenderData;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER
typedef struct {
// Controls a shared color for all this element's borders.
// Conventionally represented as 0-255 for each channel, but interpretation is up to the renderer.
Clay_Color color;
// Specifies the "radius", or corner rounding of this border element.
// The rounding is determined by drawing a circle inset into the element corner by (radius, radius) pixels.
Clay_CornerRadius cornerRadius;
// Controls individual border side widths.
Clay_BorderWidth width;
} Clay_BorderRenderData;
// A struct union containing data specific to this command's .commandType
typedef union {
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE
Clay_RectangleRenderData rectangle;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_TEXT
Clay_TextRenderData text;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_IMAGE
Clay_ImageRenderData image;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_CUSTOM
Clay_CustomRenderData custom;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_BORDER
Clay_BorderRenderData border;
// Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_SCROLL
Clay_ScrollRenderData scroll;
} Clay_RenderData;
// Miscellaneous Structs & Enums ---------------------------------
// Data representing the current internal state of a scrolling element.
typedef struct {
// Note: This is a pointer to the real internal scroll position, mutating it may cause a change in final layout.
// Intended for use with external functionality that modifies scroll position, such as scroll bars or auto scrolling.
Clay_Vector2 *scrollPosition;
// The bounding box of the scroll element.
Clay_Dimensions scrollContainerDimensions;
// The outer dimensions of the inner scroll container content, including the padding of the parent scroll container.
Clay_Dimensions contentDimensions;
// The config that was originally passed to the scroll element.
Clay_ScrollElementConfig config;
// Indicates whether an actual scroll container matched the provided ID or if the default struct was returned.
bool found;
} Clay_ScrollContainerData;
typedef struct
{
// Bounding box and other data for a specific UI element.
typedef struct {
// The rectangle that encloses this UI element, with the position relative to the root of the layout.
Clay_BoundingBox boundingBox;
// Indicates whether an actual Element matched the provided ID or if the default struct was returned.
bool found;
} Clay_ElementData;
// Used by renderers to determine specific handling for each render command.
typedef CLAY_PACKED_ENUM {
// This command type should be skipped.
CLAY_RENDER_COMMAND_TYPE_NONE,
// The renderer should draw a solid color rectangle.
CLAY_RENDER_COMMAND_TYPE_RECTANGLE,
// The renderer should draw a colored border inset into the bounding box.
CLAY_RENDER_COMMAND_TYPE_BORDER,
// The renderer should draw text.
CLAY_RENDER_COMMAND_TYPE_TEXT,
// The renderer should draw an image.
CLAY_RENDER_COMMAND_TYPE_IMAGE,
// The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
CLAY_RENDER_COMMAND_TYPE_SCISSOR_START,
// The renderer should finish any previously active clipping, and begin rendering elements in full again.
CLAY_RENDER_COMMAND_TYPE_SCISSOR_END,
// The renderer should provide a custom implementation for handling this render command based on its .customData
CLAY_RENDER_COMMAND_TYPE_CUSTOM,
} Clay_RenderCommandType;
typedef struct {
// A rectangular box that fully encloses this UI element, with the position relative to the root of the layout.
Clay_BoundingBox boundingBox;
// A struct union containing data specific to this command's commandType.
Clay_RenderData renderData;
// A pointer passed through from the element declaration
// A pointer transparently passed through from the original element declaration.
void *userData;
// The id of this element, transparently passed through from the original element declaration.
uint32_t id;
// The z order required for drawing this command correctly.
// Note: the render command array is already sorted in ascending order, and will produce correct results if drawn in naive order.
// This field is intended for use in batching renderers for improved performance.
int16_t zIndex;
// Specifies how to handle rendering of this command.
// CLAY_RENDER_COMMAND_TYPE_RECTANGLE - The renderer should draw a solid color rectangle.
// CLAY_RENDER_COMMAND_TYPE_BORDER - The renderer should draw a colored border inset into the bounding box.
// CLAY_RENDER_COMMAND_TYPE_TEXT - The renderer should draw text.
// CLAY_RENDER_COMMAND_TYPE_IMAGE - The renderer should draw an image.
// CLAY_RENDER_COMMAND_TYPE_SCISSOR_START - The renderer should begin clipping all future draw commands, only rendering content that falls within the provided boundingBox.
// CLAY_RENDER_COMMAND_TYPE_SCISSOR_END - The renderer should finish any previously active clipping, and begin rendering elements in full again.
// CLAY_RENDER_COMMAND_TYPE_CUSTOM - The renderer should provide a custom implementation for handling this render command based on its .customData
Clay_RenderCommandType commandType;
} Clay_RenderCommand;
// A sized array of render commands.
typedef struct {
// The underlying max capacity of the array, not necessarily all initialized.
int32_t capacity;
// The number of initialized elements in this array. Used for loops and iteration.
int32_t length;
// A pointer to the first element in the internal array.
Clay_RenderCommand* internalArray;
} Clay_RenderCommandArray;
// Represents the current state of interaction with clay this frame.
typedef CLAY_PACKED_ENUM {
// A left mouse click, or touch occurred this frame.
CLAY_POINTER_DATA_PRESSED_THIS_FRAME,
// The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.
CLAY_POINTER_DATA_PRESSED,
// The left mouse button click or touch was released this frame.
CLAY_POINTER_DATA_RELEASED_THIS_FRAME,
// The left mouse button click or touch is not currently down / was released at some point in the past.
CLAY_POINTER_DATA_RELEASED,
} Clay_PointerDataInteractionState;
// Information on the current state of pointer interactions this frame.
typedef struct {
// The position of the mouse / touch / pointer relative to the root of the layout.
Clay_Vector2 position;
// Represents the current state of interaction with clay this frame.
// CLAY_POINTER_DATA_PRESSED_THIS_FRAME - A left mouse click, or touch occurred this frame.
// CLAY_POINTER_DATA_PRESSED - The left mouse button click or touch happened at some point in the past, and is still currently held down this frame.
// CLAY_POINTER_DATA_RELEASED_THIS_FRAME - The left mouse button click or touch was released this frame.
// CLAY_POINTER_DATA_RELEASED - The left mouse button click or touch is not currently down / was released at some point in the past.
Clay_PointerDataInteractionState state;
} Clay_PointerData;
typedef struct {
// Primarily created via the CLAY_ID(), CLAY_IDI(), CLAY_ID_LOCAL() and CLAY_IDI_LOCAL() macros.
// Represents a hashed string ID used for identifying and finding specific clay UI elements, required by functions such as Clay_PointerOver() and Clay_GetElementData().
Clay_ElementId id;
// Controls various settings that affect the size and position of an element, as well as the sizes and positions of any child elements.
Clay_LayoutConfig layout;
// Controls the background color of the resulting element.
// By convention specified as 0-255, but interpretation is up to the renderer.
// If no other config is specified, .backgroundColor will generate a RECTANGLE render command, otherwise it will be passed as a property to IMAGE or CUSTOM render commands.
Clay_Color backgroundColor;
// Controls the "radius", or corner rounding of elements, including rectangles, borders and images.
Clay_CornerRadius cornerRadius;
// Controls settings related to image elements.
Clay_ImageElementConfig image;
// Controls whether and how an element "floats", which means it layers over the top of other elements in z order, and doesn't affect the position and size of siblings or parent elements.
// Note: in order to activate floating, .floating.attachTo must be set to something other than the default value.
Clay_FloatingElementConfig floating;
// Used to create CUSTOM render commands, usually to render element types not supported by Clay.
Clay_CustomElementConfig custom;
// Controls whether an element should clip its contents and allow scrolling rather than expanding to contain them.
Clay_ScrollElementConfig scroll;
// Controls settings related to element borders, and will generate BORDER render commands.
Clay_BorderElementConfig border;
// A pointer that will be transparently passed through to resulting render commands.
void *userData;
@ -523,68 +702,153 @@ typedef struct {
CLAY__WRAPPER_STRUCT(Clay_ElementDeclaration);
// Represents the type of error clay encountered while computing layout.
typedef CLAY_PACKED_ENUM {
// A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
// Clay attempted to allocate its internal data structures but ran out of space.
// The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().
CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED,
// Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().
CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED,
// Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().
CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
// Two elements were declared with exactly the same ID within one layout.
CLAY_ERROR_TYPE_DUPLICATE_ID,
// A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.
CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND,
// An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.
CLAY_ERROR_TYPE_PERCENTAGE_OVER_1,
// Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!
CLAY_ERROR_TYPE_INTERNAL_ERROR,
} Clay_ErrorType;
// Data to identify the error that clay has encountered.
typedef struct {
// Represents the type of error clay encountered while computing layout.
// CLAY_ERROR_TYPE_TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED - A text measurement function wasn't provided using Clay_SetMeasureTextFunction(), or the provided function was null.
// CLAY_ERROR_TYPE_ARENA_CAPACITY_EXCEEDED - Clay attempted to allocate its internal data structures but ran out of space. The arena passed to Clay_Initialize was created with a capacity smaller than that required by Clay_MinMemorySize().
// CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxElementCount().
// CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED - Clay ran out of capacity in its internal array for storing elements. This limit can be increased with Clay_SetMaxMeasureTextCacheWordCount().
// CLAY_ERROR_TYPE_DUPLICATE_ID - Two elements were declared with exactly the same ID within one layout.
// CLAY_ERROR_TYPE_FLOATING_CONTAINER_PARENT_NOT_FOUND - A floating element was declared using CLAY_ATTACH_TO_ELEMENT_ID and either an invalid .parentId was provided or no element with the provided .parentId was found.
// CLAY_ERROR_TYPE_PERCENTAGE_OVER_1 - An element was declared that using CLAY_SIZING_PERCENT but the percentage value was over 1. Percentage values are expected to be in the 0-1 range.
// CLAY_ERROR_TYPE_INTERNAL_ERROR - Clay encountered an internal error. It would be wonderful if you could report this so we can fix it!
Clay_ErrorType errorType;
// A string containing human-readable error text that explains the error in more detail.
Clay_String errorText;
// A transparent pointer passed through from when the error handler was first provided.
void *userData;
} Clay_ErrorData;
// A wrapper struct around Clay's error handler function.
typedef struct {
// A user provided function to call when Clay encounters an error during layout.
void (*errorHandlerFunction)(Clay_ErrorData errorText);
// A pointer that will be transparently passed through to the error handler when it is called.
void *userData;
} Clay_ErrorHandler;
// Function Forward Declarations ---------------------------------
// Public API functions ---
// Public API functions ------------------------------------------
// Returns the size, in bytes, of the minimum amount of memory Clay requires to operate at its current settings.
uint32_t Clay_MinMemorySize(void);
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset);
// Creates an arena for clay to use for its internal allocations, given a certain capacity in bytes and a pointer to an allocation of at least that size.
// Intended to be used with Clay_MinMemorySize in the following way:
// uint32_t minMemoryRequired = Clay_MinMemorySize();
// Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(minMemoryRequired, malloc(minMemoryRequired));
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *memory);
// Sets the state of the "pointer" (i.e. the mouse or touch) in Clay's internal data. Used for detecting and responding to mouse events in the debug view,
// as well as for Clay_Hovered() and scroll element handling.
void Clay_SetPointerState(Clay_Vector2 position, bool pointerDown);
// Initialize Clay's internal arena and setup required data before layout can begin. Only needs to be called once.
// - arena can be created using Clay_CreateArenaWithCapacityAndMemory()
// - layoutDimensions are the initial bounding dimensions of the layout (i.e. the screen width and height for a full screen layout)
// - errorHandler is used by Clay to inform you if something has gone wrong in configuration or layout.
Clay_Context* Clay_Initialize(Clay_Arena arena, Clay_Dimensions layoutDimensions, Clay_ErrorHandler errorHandler);
// Returns the Context that clay is currently using. Used when using multiple instances of clay simultaneously.
Clay_Context* Clay_GetCurrentContext(void);
// Sets the context that clay will use to compute the layout.
// Used to restore a context saved from Clay_GetCurrentContext when using multiple instances of clay simultaneously.
void Clay_SetCurrentContext(Clay_Context* context);
// Updates the state of Clay's internal scroll data, updating scroll content positions if scrollDelta is non zero, and progressing momentum scrolling.
// - enableDragScrolling when set to true will enable mobile device like "touch drag" scroll of scroll containers, including momentum scrolling after the touch has ended.
// - scrollDelta is the amount to scroll this frame on each axis in pixels.
// - deltaTime is the time in seconds since the last "frame" (scroll update)
void Clay_UpdateScrollContainers(bool enableDragScrolling, Clay_Vector2 scrollDelta, float deltaTime);
// Updates the layout dimensions in response to the window or outer container being resized.
void Clay_SetLayoutDimensions(Clay_Dimensions dimensions);
// Called before starting any layout declarations.
void Clay_BeginLayout(void);
// Called when all layout declarations are finished.
// Computes the layout and generates and returns the array of render commands to draw.
Clay_RenderCommandArray Clay_EndLayout(void);
// Calculates a hash ID from the given idString.
// Generally only used for dynamic strings when CLAY_ID("stringLiteral") can't be used.
Clay_ElementId Clay_GetElementId(Clay_String idString);
// Calculates a hash ID from the given idString and index.
// - index is used to avoid constructing dynamic ID strings in loops.
// Generally only used for dynamic strings when CLAY_IDI("stringLiteral", index) can't be used.
Clay_ElementId Clay_GetElementIdWithIndex(Clay_String idString, uint32_t index);
// Returns layout data such as the final calculated bounding box for an element with a given ID.
// The returned Clay_ElementData contains a `found` bool that will be true if an element with the provided ID was found.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
Clay_ElementData Clay_GetElementData(Clay_ElementId id);
// Returns true if the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
// Works during element declaration, e.g. CLAY({ .backgroundColor = Clay_Hovered() ? BLUE : RED });
bool Clay_Hovered(void);
// Bind a callback that will be called when the pointer position provided by Clay_SetPointerState is within the current element's bounding box.
// - onHoverFunction is a function pointer to a user defined function.
// - userData is a pointer that will be transparently passed through when the onHoverFunction is called.
void Clay_OnHover(void (*onHoverFunction)(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData), intptr_t userData);
// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
bool Clay_PointerOver(Clay_ElementId elementId);
// Returns data representing the state of the scrolling element with the provided ID.
// The returned Clay_ScrollContainerData contains a `found` bool that will be true if a scroll element was found with the provided ID.
// An imperative function that returns true if the pointer position provided by Clay_SetPointerState is within the element with the provided ID's bounding box.
// This ID can be calculated either with CLAY_ID() for string literal IDs, or Clay_GetElementId for dynamic strings.
Clay_ScrollContainerData Clay_GetScrollContainerData(Clay_ElementId id);
Clay_TextElementConfig * Clay__StoreTextElementConfig(Clay_TextElementConfig config);
// Binds a callback function that Clay will call to determine the dimensions of a given string slice.
// - measureTextFunction is a user provided function that adheres to the interface Clay_Dimensions (Clay_StringSlice text, Clay_TextElementConfig *config, void *userData);
// - userData is a pointer that will be transparently passed through when the measureTextFunction is called.
void Clay_SetMeasureTextFunction(Clay_Dimensions (*measureTextFunction)(Clay_StringSlice text, Clay_TextElementConfig *config, void *userData), void *userData);
// Experimental - Used in cases where Clay needs to integrate with a system that manages its own scrolling containers externally.
// Please reach out if you plan to use this function, as it may be subject to change.
void Clay_SetQueryScrollOffsetFunction(Clay_Vector2 (*queryScrollOffsetFunction)(uint32_t elementId, void *userData), void *userData);
// A bounds-checked "get" function for the Clay_RenderCommandArray returned from Clay_EndLayout().
Clay_RenderCommand * Clay_RenderCommandArray_Get(Clay_RenderCommandArray* array, int32_t index);
// Enables and disables Clay's internal debug tools.
// This state is retained and does not need to be set each frame.
void Clay_SetDebugModeEnabled(bool enabled);
// Returns true if Clay's internal debug tools are currently enabled.
bool Clay_IsDebugModeEnabled(void);
// Enables and disables visibility culling. By default, Clay will not generate render commands for elements whose bounding box is entirely outside the screen.
void Clay_SetCullingEnabled(bool enabled);
// Returns the maximum number of UI elements supported by Clay's current configuration.
int32_t Clay_GetMaxElementCount(void);
// Modifies the maximum number of UI elements supported by Clay's current configuration.
// This may require reallocating additional memory, and re-calling Clay_Initialize();
void Clay_SetMaxElementCount(int32_t maxElementCount);
// Returns the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
int32_t Clay_GetMaxMeasureTextCacheWordCount(void);
// Modifies the maximum number of measured "words" (whitespace seperated runs of characters) that Clay can store in its internal text measurement cache.
// This may require reallocating additional memory, and re-calling Clay_Initialize();
void Clay_SetMaxMeasureTextCacheWordCount(int32_t maxMeasureTextCacheWordCount);
// Resets Clay's internal text measurement cache, useful if memory to represent strings is being re-used.
// Similar behaviour can be achieved on an individual text element level by using Clay_TextElementConfig.hashStringContents
void Clay_ResetMeasureTextCache(void);
// Internal API functions required by macros
// Internal API functions required by macros ----------------------
void Clay__OpenElement(void);
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration config);
void Clay__CloseElement(void);
Clay_LayoutConfig * Clay__StoreLayoutConfig(Clay_LayoutConfig config);
Clay_ElementId Clay__AttachId(Clay_ElementId id);
Clay_ElementId Clay__HashString(Clay_String key, uint32_t offset, uint32_t seed);
void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig);
Clay_TextElementConfig *Clay__StoreTextElementConfig(Clay_TextElementConfig config);
uint32_t Clay__GetParentElementId(void);
extern Clay_Color Clay__debugViewHighlightColor;
@ -616,6 +880,7 @@ Clay_Color Clay__Color_DEFAULT = CLAY__DEFAULT_STRUCT;
Clay_CornerRadius Clay__CornerRadius_DEFAULT = CLAY__DEFAULT_STRUCT;
Clay_BorderWidth Clay__BorderWidth_DEFAULT = CLAY__DEFAULT_STRUCT;
// The below functions define array bounds checking and convenience functions for a provided type.
#define CLAY__ARRAY_DEFINE_FUNCTIONS(typeName, arrayName) \
\
typedef struct \
@ -627,7 +892,8 @@ typedef struct \
typeName typeName##_DEFAULT = CLAY__DEFAULT_STRUCT; \
\
arrayName arrayName##_Allocate_Arena(int32_t capacity, Clay_Arena *arena) { \
return CLAY__INIT(arrayName){.capacity = capacity, .length = 0, .internalArray = (typeName *)Clay__Array_Allocate_Arena(capacity, sizeof(typeName), arena)}; \
return CLAY__INIT(arrayName){.capacity = capacity, .length = 0, \
.internalArray = (typeName *)Clay__Array_Allocate_Arena(capacity, sizeof(typeName), arena)}; \
} \
\
typeName *arrayName##_Get(arrayName *array, int32_t index) { \
@ -708,6 +974,14 @@ typedef struct {
Clay__Warning *internalArray;
} Clay__WarningArray;
typedef struct {
Clay_Color backgroundColor;
Clay_CornerRadius cornerRadius;
void* userData;
} Clay_SharedElementConfig;
CLAY__WRAPPER_STRUCT(Clay_SharedElementConfig);
Clay__WarningArray Clay__WarningArray_Allocate_Arena(int32_t capacity, Clay_Arena *arena);
Clay__Warning *Clay__WarningArray_Add(Clay__WarningArray *array, Clay__Warning item);
void* Clay__Array_Allocate_Arena(int32_t capacity, uint32_t itemSize, Clay_Arena *arena);
@ -729,6 +1003,17 @@ CLAY__ARRAY_DEFINE(Clay_String, Clay__StringArray)
CLAY__ARRAY_DEFINE(Clay_SharedElementConfig, Clay__SharedElementConfigArray)
CLAY__ARRAY_DEFINE_FUNCTIONS(Clay_RenderCommand, Clay_RenderCommandArray)
typedef CLAY_PACKED_ENUM {
CLAY__ELEMENT_CONFIG_TYPE_NONE,
CLAY__ELEMENT_CONFIG_TYPE_BORDER,
CLAY__ELEMENT_CONFIG_TYPE_FLOATING,
CLAY__ELEMENT_CONFIG_TYPE_SCROLL,
CLAY__ELEMENT_CONFIG_TYPE_IMAGE,
CLAY__ELEMENT_CONFIG_TYPE_TEXT,
CLAY__ELEMENT_CONFIG_TYPE_CUSTOM,
CLAY__ELEMENT_CONFIG_TYPE_SHARED,
} Clay__ElementConfigType;
typedef union {
Clay_TextElementConfig *textElementConfig;
Clay_ImageElementConfig *imageElementConfig;
@ -1511,6 +1796,19 @@ void Clay__OpenTextElement(Clay_String text, Clay_TextElementConfig *textConfig)
parentElement->childrenOrTextContent.children.length++;
}
Clay_ElementId Clay__AttachId(Clay_ElementId elementId) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->booleanWarnings.maxElementsExceeded) {
return Clay_ElementId_DEFAULT;
}
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
uint32_t idAlias = openLayoutElement->id;
openLayoutElement->id = elementId.id;
Clay__AddHashMapItem(elementId, openLayoutElement, idAlias);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
return elementId;
}
void Clay__ConfigureOpenElement(const Clay_ElementDeclaration declaration) {
Clay_Context* context = Clay_GetCurrentContext();
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
@ -2549,19 +2847,6 @@ void Clay__CalculateFinalLayout(void) {
}
}
Clay_ElementId Clay__AttachId(Clay_ElementId elementId) {
Clay_Context* context = Clay_GetCurrentContext();
if (context->booleanWarnings.maxElementsExceeded) {
return Clay_ElementId_DEFAULT;
}
Clay_LayoutElement *openLayoutElement = Clay__GetOpenLayoutElement();
uint32_t idAlias = openLayoutElement->id;
openLayoutElement->id = elementId.id;
Clay__AddHashMapItem(elementId, openLayoutElement, idAlias);
Clay__StringArray_Add(&context->layoutElementIdStrings, elementId.stringId);
return elementId;
}
#pragma region DebugTools
Clay_Color CLAY__DEBUGVIEW_COLOR_1 = {58, 56, 52, 255};
Clay_Color CLAY__DEBUGVIEW_COLOR_2 = {62, 60, 58, 255};
@ -3259,10 +3544,10 @@ uint32_t Clay_MinMemorySize(void) {
}
CLAY_WASM_EXPORT("Clay_CreateArenaWithCapacityAndMemory")
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *offset) {
Clay_Arena Clay_CreateArenaWithCapacityAndMemory(uint32_t capacity, void *memory) {
Clay_Arena arena = {
.capacity = capacity,
.memory = (char *)offset
.memory = (char *)memory
};
return arena;
}

0
generator/__init__.py Normal file
View File

83
generator/cli.py Normal file
View File

@ -0,0 +1,83 @@
import argparse
import logging
import json
from pathlib import Path
from generators.base_generator import BaseGenerator
from generators.odin_generator import OdinGenerator
from parser import parse_headers
logger = logging.getLogger(__name__)
GeneratorMap = dict[str, type[BaseGenerator]]
GENERATORS = {
'odin': OdinGenerator,
}
def main() -> None:
arg_parser = argparse.ArgumentParser(description='Generate clay bindings')
# Directories
arg_parser.add_argument('input_files', nargs='+', type=str, help='Input header files')
arg_parser.add_argument('--output-dir', type=str, help='Output directory', required=True)
arg_parser.add_argument('--tmp-dir', type=str, help='Temporary directory')
# Generators
arg_parser.add_argument('--generator', type=str, choices=list(GENERATORS.keys()), help='Generators to run', required=True)
# Logging
arg_parser.add_argument('--verbose', action='store_true', help='Verbose logging')
args = arg_parser.parse_args()
log_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
log_handler = logging.StreamHandler()
log_handler.setFormatter(log_formatter)
if args.verbose:
logging.basicConfig(level=logging.DEBUG, handlers=[log_handler])
else:
logging.basicConfig(level=logging.INFO, handlers=[log_handler])
output_dir = Path(args.output_dir)
output_dir.mkdir(parents=True, exist_ok=True)
if args.tmp_dir:
tmp_dir = Path(args.tmp_dir)
else:
tmp_dir = output_dir / 'tmp'
tmp_dir.mkdir(parents=True, exist_ok=True)
fake_libc_include_path = Path(__file__).parent / 'fake_libc_include'
input_files = list(fake_libc_include_path.glob('*.h')) + [Path(f) for f in args.input_files]
logger.info(f'Input files: {input_files}')
logger.info(f'Output directory: {output_dir}')
logger.info(f'Temporary directory: {tmp_dir}')
logger.info(f'Generator: {args.generator}')
logger.info('Parsing headers')
extracted_symbols = parse_headers(input_files, tmp_dir)
with open(tmp_dir / 'extracted_symbols.json', 'w') as f:
f.write(json.dumps({
'structs': extracted_symbols.structs,
'enums': extracted_symbols.enums,
'functions': extracted_symbols.functions,
}, indent=2))
logger.info('Generating bindings')
generator = GENERATORS[args.generator](extracted_symbols)
generator.generate()
logger.debug(f'Generated bindings:')
# for file_name, content in generator.get_outputs().items():
# logger.debug(f'{file_name}:')
# logger.debug(content)
# logger.debug('\n')
tmp_outputs_dir = tmp_dir / 'generated'
tmp_outputs_dir.mkdir(parents=True, exist_ok=True)
generator.write_outputs(tmp_outputs_dir)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,262 @@
#ifndef _FAKE_DEFINES_H
#define _FAKE_DEFINES_H
#define NULL 0
#define BUFSIZ 1024
#define FOPEN_MAX 20
#define FILENAME_MAX 1024
#ifndef SEEK_SET
#define SEEK_SET 0 /* set file offset to offset */
#endif
#ifndef SEEK_CUR
#define SEEK_CUR 1 /* set file offset to current plus offset */
#endif
#ifndef SEEK_END
#define SEEK_END 2 /* set file offset to EOF plus offset */
#endif
#define __LITTLE_ENDIAN 1234
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#define __BIG_ENDIAN 4321
#define BIG_ENDIAN __BIG_ENDIAN
#define __BYTE_ORDER __LITTLE_ENDIAN
#define BYTE_ORDER __BYTE_ORDER
#define EXIT_FAILURE 1
#define EXIT_SUCCESS 0
#define SCHAR_MIN -128
#define SCHAR_MAX 127
#define CHAR_MIN -128
#define CHAR_MAX 127
#define UCHAR_MAX 255
#define SHRT_MIN -32768
#define SHRT_MAX 32767
#define USHRT_MAX 65535
#define INT_MIN -2147483648
#define INT_MAX 2147483647
#define UINT_MAX 4294967295U
#define LONG_MIN -9223372036854775808L
#define LONG_MAX 9223372036854775807L
#define ULONG_MAX 18446744073709551615UL
#define RAND_MAX 32767
/* C99 inttypes.h defines */
#define PRId8 "d"
#define PRIi8 "i"
#define PRIo8 "o"
#define PRIu8 "u"
#define PRIx8 "x"
#define PRIX8 "X"
#define PRId16 "d"
#define PRIi16 "i"
#define PRIo16 "o"
#define PRIu16 "u"
#define PRIx16 "x"
#define PRIX16 "X"
#define PRId32 "d"
#define PRIi32 "i"
#define PRIo32 "o"
#define PRIu32 "u"
#define PRIx32 "x"
#define PRIX32 "X"
#define PRId64 "d"
#define PRIi64 "i"
#define PRIo64 "o"
#define PRIu64 "u"
#define PRIx64 "x"
#define PRIX64 "X"
#define PRIdLEAST8 "d"
#define PRIiLEAST8 "i"
#define PRIoLEAST8 "o"
#define PRIuLEAST8 "u"
#define PRIxLEAST8 "x"
#define PRIXLEAST8 "X"
#define PRIdLEAST16 "d"
#define PRIiLEAST16 "i"
#define PRIoLEAST16 "o"
#define PRIuLEAST16 "u"
#define PRIxLEAST16 "x"
#define PRIXLEAST16 "X"
#define PRIdLEAST32 "d"
#define PRIiLEAST32 "i"
#define PRIoLEAST32 "o"
#define PRIuLEAST32 "u"
#define PRIxLEAST32 "x"
#define PRIXLEAST32 "X"
#define PRIdLEAST64 "d"
#define PRIiLEAST64 "i"
#define PRIoLEAST64 "o"
#define PRIuLEAST64 "u"
#define PRIxLEAST64 "x"
#define PRIXLEAST64 "X"
#define PRIdFAST8 "d"
#define PRIiFAST8 "i"
#define PRIoFAST8 "o"
#define PRIuFAST8 "u"
#define PRIxFAST8 "x"
#define PRIXFAST8 "X"
#define PRIdFAST16 "d"
#define PRIiFAST16 "i"
#define PRIoFAST16 "o"
#define PRIuFAST16 "u"
#define PRIxFAST16 "x"
#define PRIXFAST16 "X"
#define PRIdFAST32 "d"
#define PRIiFAST32 "i"
#define PRIoFAST32 "o"
#define PRIuFAST32 "u"
#define PRIxFAST32 "x"
#define PRIXFAST32 "X"
#define PRIdFAST64 "d"
#define PRIiFAST64 "i"
#define PRIoFAST64 "o"
#define PRIuFAST64 "u"
#define PRIxFAST64 "x"
#define PRIXFAST64 "X"
#define PRIdPTR "d"
#define PRIiPTR "i"
#define PRIoPTR "o"
#define PRIuPTR "u"
#define PRIxPTR "x"
#define PRIXPTR "X"
#define PRIdMAX "d"
#define PRIiMAX "i"
#define PRIoMAX "o"
#define PRIuMAX "u"
#define PRIxMAX "x"
#define PRIXMAX "X"
#define SCNd8 "d"
#define SCNi8 "i"
#define SCNo8 "o"
#define SCNu8 "u"
#define SCNx8 "x"
#define SCNd16 "d"
#define SCNi16 "i"
#define SCNo16 "o"
#define SCNu16 "u"
#define SCNx16 "x"
#define SCNd32 "d"
#define SCNi32 "i"
#define SCNo32 "o"
#define SCNu32 "u"
#define SCNx32 "x"
#define SCNd64 "d"
#define SCNi64 "i"
#define SCNo64 "o"
#define SCNu64 "u"
#define SCNx64 "x"
#define SCNdLEAST8 "d"
#define SCNiLEAST8 "i"
#define SCNoLEAST8 "o"
#define SCNuLEAST8 "u"
#define SCNxLEAST8 "x"
#define SCNdLEAST16 "d"
#define SCNiLEAST16 "i"
#define SCNoLEAST16 "o"
#define SCNuLEAST16 "u"
#define SCNxLEAST16 "x"
#define SCNdLEAST32 "d"
#define SCNiLEAST32 "i"
#define SCNoLEAST32 "o"
#define SCNuLEAST32 "u"
#define SCNxLEAST32 "x"
#define SCNdLEAST64 "d"
#define SCNiLEAST64 "i"
#define SCNoLEAST64 "o"
#define SCNuLEAST64 "u"
#define SCNxLEAST64 "x"
#define SCNdFAST8 "d"
#define SCNiFAST8 "i"
#define SCNoFAST8 "o"
#define SCNuFAST8 "u"
#define SCNxFAST8 "x"
#define SCNdFAST16 "d"
#define SCNiFAST16 "i"
#define SCNoFAST16 "o"
#define SCNuFAST16 "u"
#define SCNxFAST16 "x"
#define SCNdFAST32 "d"
#define SCNiFAST32 "i"
#define SCNoFAST32 "o"
#define SCNuFAST32 "u"
#define SCNxFAST32 "x"
#define SCNdFAST64 "d"
#define SCNiFAST64 "i"
#define SCNoFAST64 "o"
#define SCNuFAST64 "u"
#define SCNxFAST64 "x"
#define SCNdPTR "d"
#define SCNiPTR "i"
#define SCNoPTR "o"
#define SCNuPTR "u"
#define SCNxPTR "x"
#define SCNdMAX "d"
#define SCNiMAX "i"
#define SCNoMAX "o"
#define SCNuMAX "u"
#define SCNxMAX "x"
/* C99 stdbool.h defines */
#define __bool_true_false_are_defined 1
#define false 0
#define true 1
/* va_arg macros and type*/
#define va_start(_ap, _type) __builtin_va_start((_ap))
#define va_arg(_ap, _type) __builtin_va_arg((_ap))
#define va_end(_list)
/* Vectors */
#define __m128 int
#define __m128_u int
#define __m128d int
#define __m128d_u int
#define __m128i int
#define __m128i_u int
#define __m256 int
#define __m256_u int
#define __m256d int
#define __m256d_u int
#define __m256i int
#define __m256i_u int
#define __m512 int
#define __m512_u int
#define __m512d int
#define __m512d_u int
#define __m512i int
#define __m512i_u int
/* C11 stdnoreturn.h defines */
#define __noreturn_is_defined 1
#define noreturn _Noreturn
/* C11 threads.h defines */
#define thread_local _Thread_local
/* C11 assert.h defines */
#define static_assert _Static_assert
/* C11 stdatomic.h defines */
#define ATOMIC_BOOL_LOCK_FREE 0
#define ATOMIC_CHAR_LOCK_FREE 0
#define ATOMIC_CHAR16_T_LOCK_FREE 0
#define ATOMIC_CHAR32_T_LOCK_FREE 0
#define ATOMIC_WCHAR_T_LOCK_FREE 0
#define ATOMIC_SHORT_LOCK_FREE 0
#define ATOMIC_INT_LOCK_FREE 0
#define ATOMIC_LONG_LOCK_FREE 0
#define ATOMIC_LLONG_LOCK_FREE 0
#define ATOMIC_POINTER_LOCK_FREE 0
#define ATOMIC_VAR_INIT(value) (value)
#define ATOMIC_FLAG_INIT { 0 }
#define kill_dependency(y) (y)
/* C11 stdalign.h defines */
#define alignas _Alignas
#define alignof _Alignof
#define __alignas_is_defined 1
#define __alignof_is_defined 1
#endif

View File

@ -0,0 +1,222 @@
#ifndef _FAKE_TYPEDEFS_H
#define _FAKE_TYPEDEFS_H
typedef int size_t;
typedef int __builtin_va_list;
typedef int __gnuc_va_list;
typedef int va_list;
typedef int __int8_t;
typedef int __uint8_t;
typedef int __int16_t;
typedef int __uint16_t;
typedef int __int_least16_t;
typedef int __uint_least16_t;
typedef int __int32_t;
typedef int __uint32_t;
typedef int __int64_t;
typedef int __uint64_t;
typedef int __int_least32_t;
typedef int __uint_least32_t;
typedef int __s8;
typedef int __u8;
typedef int __s16;
typedef int __u16;
typedef int __s32;
typedef int __u32;
typedef int __s64;
typedef int __u64;
typedef int _LOCK_T;
typedef int _LOCK_RECURSIVE_T;
typedef int _off_t;
typedef int __dev_t;
typedef int __uid_t;
typedef int __gid_t;
typedef int _off64_t;
typedef int _fpos_t;
typedef int _ssize_t;
typedef int wint_t;
typedef int _mbstate_t;
typedef int _flock_t;
typedef int _iconv_t;
typedef int __ULong;
typedef int __FILE;
typedef int ptrdiff_t;
typedef int wchar_t;
typedef int char16_t;
typedef int char32_t;
typedef int __off_t;
typedef int __pid_t;
typedef int __loff_t;
typedef int u_char;
typedef int u_short;
typedef int u_int;
typedef int u_long;
typedef int ushort;
typedef int uint;
typedef int clock_t;
typedef int time_t;
typedef int daddr_t;
typedef int caddr_t;
typedef int ino_t;
typedef int off_t;
typedef int dev_t;
typedef int uid_t;
typedef int gid_t;
typedef int pid_t;
typedef int key_t;
typedef int ssize_t;
typedef int mode_t;
typedef int nlink_t;
typedef int fd_mask;
typedef int _types_fd_set;
typedef int clockid_t;
typedef int timer_t;
typedef int useconds_t;
typedef int suseconds_t;
typedef int FILE;
typedef int fpos_t;
typedef int cookie_read_function_t;
typedef int cookie_write_function_t;
typedef int cookie_seek_function_t;
typedef int cookie_close_function_t;
typedef int cookie_io_functions_t;
typedef int div_t;
typedef int ldiv_t;
typedef int lldiv_t;
typedef int sigset_t;
typedef int __sigset_t;
typedef int _sig_func_ptr;
typedef int sig_atomic_t;
typedef int __tzrule_type;
typedef int __tzinfo_type;
typedef int mbstate_t;
typedef int sem_t;
typedef int pthread_t;
typedef int pthread_attr_t;
typedef int pthread_mutex_t;
typedef int pthread_mutexattr_t;
typedef int pthread_cond_t;
typedef int pthread_condattr_t;
typedef int pthread_key_t;
typedef int pthread_once_t;
typedef int pthread_rwlock_t;
typedef int pthread_rwlockattr_t;
typedef int pthread_spinlock_t;
typedef int pthread_barrier_t;
typedef int pthread_barrierattr_t;
typedef int jmp_buf;
typedef int rlim_t;
typedef int sa_family_t;
typedef int sigjmp_buf;
typedef int stack_t;
typedef int siginfo_t;
typedef int z_stream;
/* C99 exact-width integer types */
typedef int int8_t;
typedef int uint8_t;
typedef int int16_t;
typedef int uint16_t;
typedef int int32_t;
typedef int uint32_t;
typedef int int64_t;
typedef int uint64_t;
/* C99 minimum-width integer types */
typedef int int_least8_t;
typedef int uint_least8_t;
typedef int int_least16_t;
typedef int uint_least16_t;
typedef int int_least32_t;
typedef int uint_least32_t;
typedef int int_least64_t;
typedef int uint_least64_t;
/* C99 fastest minimum-width integer types */
typedef int int_fast8_t;
typedef int uint_fast8_t;
typedef int int_fast16_t;
typedef int uint_fast16_t;
typedef int int_fast32_t;
typedef int uint_fast32_t;
typedef int int_fast64_t;
typedef int uint_fast64_t;
/* C99 integer types capable of holding object pointers */
typedef int intptr_t;
typedef int uintptr_t;
/* C99 greatest-width integer types */
typedef int intmax_t;
typedef int uintmax_t;
/* C99 stdbool.h bool type. _Bool is built-in in C99 */
typedef _Bool bool;
/* Mir typedefs */
typedef void* MirEGLNativeWindowType;
typedef void* MirEGLNativeDisplayType;
typedef struct MirConnection MirConnection;
typedef struct MirSurface MirSurface;
typedef struct MirSurfaceSpec MirSurfaceSpec;
typedef struct MirScreencast MirScreencast;
typedef struct MirPromptSession MirPromptSession;
typedef struct MirBufferStream MirBufferStream;
typedef struct MirPersistentId MirPersistentId;
typedef struct MirBlob MirBlob;
typedef struct MirDisplayConfig MirDisplayConfig;
/* xcb typedefs */
typedef struct xcb_connection_t xcb_connection_t;
typedef uint32_t xcb_window_t;
typedef uint32_t xcb_visualid_t;
/* C11 stdatomic.h types */
typedef _Atomic(_Bool) atomic_bool;
typedef _Atomic(char) atomic_char;
typedef _Atomic(signed char) atomic_schar;
typedef _Atomic(unsigned char) atomic_uchar;
typedef _Atomic(short) atomic_short;
typedef _Atomic(unsigned short) atomic_ushort;
typedef _Atomic(int) atomic_int;
typedef _Atomic(unsigned int) atomic_uint;
typedef _Atomic(long) atomic_long;
typedef _Atomic(unsigned long) atomic_ulong;
typedef _Atomic(long long) atomic_llong;
typedef _Atomic(unsigned long long) atomic_ullong;
typedef _Atomic(uint_least16_t) atomic_char16_t;
typedef _Atomic(uint_least32_t) atomic_char32_t;
typedef _Atomic(wchar_t) atomic_wchar_t;
typedef _Atomic(int_least8_t) atomic_int_least8_t;
typedef _Atomic(uint_least8_t) atomic_uint_least8_t;
typedef _Atomic(int_least16_t) atomic_int_least16_t;
typedef _Atomic(uint_least16_t) atomic_uint_least16_t;
typedef _Atomic(int_least32_t) atomic_int_least32_t;
typedef _Atomic(uint_least32_t) atomic_uint_least32_t;
typedef _Atomic(int_least64_t) atomic_int_least64_t;
typedef _Atomic(uint_least64_t) atomic_uint_least64_t;
typedef _Atomic(int_fast8_t) atomic_int_fast8_t;
typedef _Atomic(uint_fast8_t) atomic_uint_fast8_t;
typedef _Atomic(int_fast16_t) atomic_int_fast16_t;
typedef _Atomic(uint_fast16_t) atomic_uint_fast16_t;
typedef _Atomic(int_fast32_t) atomic_int_fast32_t;
typedef _Atomic(uint_fast32_t) atomic_uint_fast32_t;
typedef _Atomic(int_fast64_t) atomic_int_fast64_t;
typedef _Atomic(uint_fast64_t) atomic_uint_fast64_t;
typedef _Atomic(intptr_t) atomic_intptr_t;
typedef _Atomic(uintptr_t) atomic_uintptr_t;
typedef _Atomic(size_t) atomic_size_t;
typedef _Atomic(ptrdiff_t) atomic_ptrdiff_t;
typedef _Atomic(intmax_t) atomic_intmax_t;
typedef _Atomic(uintmax_t) atomic_uintmax_t;
typedef struct atomic_flag { atomic_bool _Value; } atomic_flag;
typedef enum memory_order {
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
} memory_order;
#endif

View File

@ -0,0 +1,2 @@
#include "_fake_defines.h"
#include "_fake_typedefs.h"

View File

@ -0,0 +1,2 @@
#include "_fake_defines.h"
#include "_fake_typedefs.h"

View File

@ -0,0 +1,2 @@
#include "_fake_defines.h"
#include "_fake_typedefs.h"

View File

@ -0,0 +1,2 @@
#include "_fake_defines.h"
#include "_fake_typedefs.h"

View File

@ -0,0 +1,5 @@
#!/usr/bin/bash
REPO_ROOT=$(realpath $(dirname $(dirname $0)))
# Generate odin bindings
python $REPO_ROOT/generator/cli.py $REPO_ROOT/clay.h --output-dir $REPO_ROOT/bindings/odin/clay-odin --generator odin --verbose

View File

View File

@ -0,0 +1,47 @@
from parser import ExtractedSymbols, ExtractedEnum, ExtractedStruct, ExtractedFunction
from typing import Any, Callable, DefaultDict, Literal, NotRequired, Optional, TypedDict
from pathlib import Path
from dataclasses import dataclass
SymbolType = Literal['enum', 'struct', 'function']
class BaseGenerator:
def __init__(self, extracted_symbols: ExtractedSymbols):
self.extracted_symbols = extracted_symbols
self.output_content: dict[str, list[str]] = dict()
def generate(self) -> None:
pass
def has_symbol(self, symbol: str) -> bool:
return (
symbol in self.extracted_symbols.enums or
symbol in self.extracted_symbols.structs or
symbol in self.extracted_symbols.functions
)
def get_symbol_type(self, symbol: str) -> SymbolType:
if symbol in self.extracted_symbols.enums:
return 'enum'
elif symbol in self.extracted_symbols.structs:
return 'struct'
elif symbol in self.extracted_symbols.functions:
return 'function'
raise ValueError(f'Unknown symbol: {symbol}')
def _write(self, file_name: str, content: str) -> None:
if file_name not in self.output_content:
self.output_content[file_name] = []
self.output_content[file_name].append(content)
def write_outputs(self, output_dir: Path) -> None:
for file_name, content in self.output_content.items():
(output_dir / file_name).parent.mkdir(parents=True, exist_ok=True)
with open(output_dir / file_name, 'w') as f:
f.write("\n".join(content))
def get_outputs(self) -> dict[str, str]:
return {file_name: "\n".join(content) for file_name, content in self.output_content.items()}

View File

@ -0,0 +1,164 @@
package clay
import "core:c"
import "core:strings"
when ODIN_OS == .Windows {
foreign import Clay "windows/clay.lib"
} else when ODIN_OS == .Linux {
foreign import Clay "linux/clay.a"
} else when ODIN_OS == .Darwin {
when ODIN_ARCH == .arm64 {
foreign import Clay "macos-arm64/clay.a"
} else {
foreign import Clay "macos/clay.a"
}
} else when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 {
foreign import Clay "wasm/clay.o"
}
when ODIN_OS == .Windows {
EnumBackingType :: u32
} else {
EnumBackingType :: u8
}
{{enums}}
Context :: struct {
}
ClayArray :: struct($type: typeid) {
capacity: i32,
length: i32,
internalArray: [^]type,
}
SizingConstraints :: struct #raw_union {
sizeMinMax: SizingConstraintsMinMax,
sizePercent: c.float,
}
TypedConfig :: struct {
type: ElementConfigType,
config: rawptr,
id: ElementId,
}
{{structs}}
@(link_prefix = "Clay_", default_calling_convention = "c")
foreign Clay {
{{public_functions}}
}
@(link_prefix = "Clay_", default_calling_convention = "c", private)
foreign Clay {
{{private_functions}}
}
@(require_results, deferred_none = _CloseElement)
UI :: proc(configs: ..TypedConfig) -> bool {
_OpenElement()
for config in configs {
#partial switch (config.type) {
case ElementConfigType.Id:
_AttachId(config.id)
case ElementConfigType.Layout:
_AttachLayoutConfig(cast(^LayoutConfig)config.config)
case:
_AttachElementConfig(config.config, config.type)
}
}
_ElementPostConfiguration()
return true
}
Layout :: proc(config: LayoutConfig) -> TypedConfig {
return {type = ElementConfigType.Layout, config = _StoreLayoutConfig(config) }
}
PaddingAll :: proc (padding: u16) -> Padding {
return { padding, padding, padding, padding }
}
Rectangle :: proc(config: RectangleElementConfig) -> TypedConfig {
return {type = ElementConfigType.Rectangle, config = _StoreRectangleElementConfig(config)}
}
Text :: proc(text: string, config: ^TextElementConfig) {
_OpenTextElement(MakeString(text), config)
}
TextConfig :: proc(config: TextElementConfig) -> ^TextElementConfig {
return _StoreTextElementConfig(config)
}
Image :: proc(config: ImageElementConfig) -> TypedConfig {
return {type = ElementConfigType.Image, config = _StoreImageElementConfig(config)}
}
Floating :: proc(config: FloatingElementConfig) -> TypedConfig {
return {type = ElementConfigType.Floating, config = _StoreFloatingElementConfig(config)}
}
Custom :: proc(config: CustomElementConfig) -> TypedConfig {
return {type = ElementConfigType.Custom, config = _StoreCustomElementConfig(config)}
}
Scroll :: proc(config: ScrollElementConfig) -> TypedConfig {
return {type = ElementConfigType.Scroll, config = _StoreScrollElementConfig(config)}
}
Border :: proc(config: BorderElementConfig) -> TypedConfig {
return {type = ElementConfigType.Border, config = _StoreBorderElementConfig(config)}
}
BorderOutside :: proc(outsideBorders: BorderData) -> TypedConfig {
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders}) }
}
BorderOutsideRadius :: proc(outsideBorders: BorderData, radius: f32) -> TypedConfig {
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
(BorderElementConfig){left = outsideBorders, right = outsideBorders, top = outsideBorders, bottom = outsideBorders, cornerRadius = {radius, radius, radius, radius}},
) }
}
BorderAll :: proc(allBorders: BorderData) -> TypedConfig {
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig((BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, betweenChildren = allBorders}) }
}
BorderAllRadius :: proc(allBorders: BorderData, radius: f32) -> TypedConfig {
return { type = ElementConfigType.Border, config = _StoreBorderElementConfig(
(BorderElementConfig){left = allBorders, right = allBorders, top = allBorders, bottom = allBorders, cornerRadius = {radius, radius, radius, radius}},
) }
}
CornerRadiusAll :: proc(radius: f32) -> CornerRadius {
return CornerRadius{radius, radius, radius, radius}
}
SizingFit :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
return SizingAxis{type = SizingType.FIT, constraints = {sizeMinMax = sizeMinMax}}
}
SizingGrow :: proc(sizeMinMax: SizingConstraintsMinMax) -> SizingAxis {
return SizingAxis{type = SizingType.GROW, constraints = {sizeMinMax = sizeMinMax}}
}
SizingFixed :: proc(size: c.float) -> SizingAxis {
return SizingAxis{type = SizingType.FIXED, constraints = {sizeMinMax = {size, size}}}
}
SizingPercent :: proc(sizePercent: c.float) -> SizingAxis {
return SizingAxis{type = SizingType.PERCENT, constraints = {sizePercent = sizePercent}}
}
MakeString :: proc(label: string) -> String {
return String{chars = raw_data(label), length = cast(c.int)len(label)}
}
ID :: proc(label: string, index: u32 = 0) -> TypedConfig {
return { type = ElementConfigType.Id, id = _HashString(MakeString(label), index, 0) }
}

View File

@ -0,0 +1,315 @@
from pathlib import Path
import logging
from parser import ExtractedSymbolType
from generators.base_generator import BaseGenerator
logger = logging.getLogger(__name__)
def get_common_prefix(keys: list[str]) -> str:
# find a prefix that's shared between all keys
prefix = ""
for i in range(min(map(len, keys))):
if len(set(key[i] for key in keys)) > 1:
break
prefix += keys[0][i]
return prefix
def snake_case_to_pascal_case(snake_case: str) -> str:
return ''.join(word.lower().capitalize() for word in snake_case.split('_'))
SYMBOL_NAME_OVERRIDES = {
'Clay_TextElementConfigWrapMode': 'TextWrapMode',
'Clay_Border': 'BorderData',
'Clay_SizingMinMax': 'SizingConstraintsMinMax',
}
SYMBOL_COMPLETE_OVERRIDES = {
'Clay_RenderCommandArray': 'ClayArray(RenderCommand)',
'Clay_Context': 'Context',
'Clay_ElementConfig': None,
# 'Clay_SetQueryScrollOffsetFunction': None,
}
# These enums should have output binding members that are PascalCase instead of UPPER_SNAKE_CASE.
ENUM_MEMBER_PASCAL = {
'Clay_RenderCommandType',
'Clay_TextElementConfigWrapMode',
'Clay__ElementConfigType',
}
ENUM_MEMBER_OVERRIDES = {
'Clay__ElementConfigType': {
'CLAY__ELEMENT_CONFIG_TYPE_BORDER_CONTAINER': 'Border',
'CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER': 'Floating',
'CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER': 'Scroll',
}
}
ENUM_ADDITIONAL_MEMBERS = {
'Clay__ElementConfigType': {
'Id': 65,
'Layout': 66,
}
}
TYPE_MAPPING = {
'*char': '[^]c.char',
'const *char': '[^]c.char',
'*void': 'rawptr',
'bool': 'bool',
'float': 'c.float',
'uint16_t': 'u16',
'uint32_t': 'u32',
'int32_t': 'c.int32_t',
'uintptr_t': 'rawptr',
'intptr_t': 'rawptr',
'void': 'void',
}
STRUCT_TYPE_OVERRIDES = {
'Clay_Arena': {
'nextAllocation': 'uintptr',
'capacity': 'uintptr',
},
'Clay_SizingAxis': {
'size': 'SizingConstraints',
},
"Clay_RenderCommand": {
"zIndex": 'i32',
},
}
STRUCT_MEMBER_OVERRIDES = {
'Clay_ErrorHandler': {
'errorHandlerFunction': 'handler',
},
'Clay_SizingAxis': {
'size': 'constraints',
},
}
STRUCT_OVERRIDE_AS_FIXED_ARRAY = {
'Clay_Color',
'Clay_Vector2',
}
FUNCTION_PARAM_OVERRIDES = {
'Clay_SetCurrentContext': {
'context': 'ctx',
},
}
FUNCTION_TYPE_OVERRIDES = {
'Clay_CreateArenaWithCapacityAndMemory': {
'offset': '[^]u8',
},
'Clay_SetMeasureTextFunction': {
'userData': 'uintptr',
},
'Clay_RenderCommandArray_Get': {
'index': 'i32',
},
"Clay__AttachElementConfig": {
"config": 'rawptr',
},
}
class OdinGenerator(BaseGenerator):
def generate(self) -> None:
self.generate_structs()
self.generate_enums()
self.generate_functions()
odin_template_path = Path(__file__).parent / 'odin' / 'clay.template.odin'
with open(odin_template_path, 'r') as f:
template = f.read()
self.output_content['clay.odin'] = (
template
.replace('{{structs}}', '\n'.join(self.output_content['struct']))
.replace('{{enums}}', '\n'.join(self.output_content['enum']))
.replace('{{public_functions}}', '\n'.join(self.output_content['public_function']))
.replace('{{private_functions}}', '\n'.join(self.output_content['private_function']))
.splitlines()
)
del self.output_content['struct']
del self.output_content['enum']
del self.output_content['private_function']
del self.output_content['public_function']
def get_symbol_name(self, symbol: str) -> str:
if symbol in SYMBOL_NAME_OVERRIDES:
return SYMBOL_NAME_OVERRIDES[symbol]
symbol_type = self.get_symbol_type(symbol)
base_name = symbol.removeprefix('Clay_')
if symbol_type == 'enum':
return base_name.removeprefix('_') # Clay_ and Clay__ are exported as public types.
elif symbol_type == 'struct':
return base_name
elif symbol_type == 'function':
return base_name
raise ValueError(f'Unknown symbol: {symbol}')
def format_type(self, type: ExtractedSymbolType) -> str:
if isinstance(type, str):
return type
parameter_strs = []
for param_name, param_type in type['params']:
parameter_strs.append(f"{param_name}: {self.format_type(param_type or 'unknown')}")
return_type_str = ''
if type['return_type'] is not None and type['return_type'] != 'void':
return_type_str = ' -> ' + self.format_type(type['return_type'])
return f"proc \"c\" ({', '.join(parameter_strs)}){return_type_str}"
def resolve_binding_type(self, symbol: str, member: str | None, member_type: ExtractedSymbolType | None, type_overrides: dict[str, dict[str, str]]) -> str | None:
if isinstance(member_type, str):
if member_type in SYMBOL_COMPLETE_OVERRIDES:
return SYMBOL_COMPLETE_OVERRIDES[member_type]
if symbol in type_overrides and member in type_overrides[symbol]:
return type_overrides[symbol][member]
if member_type in TYPE_MAPPING:
return TYPE_MAPPING[member_type]
if member_type and self.has_symbol(member_type):
return self.get_symbol_name(member_type)
if member_type and member_type.startswith('*'):
result = self.resolve_binding_type(symbol, member, member_type[1:], type_overrides)
if result:
return f"^{result}"
return None
if member_type is None:
return None
resolved_parameters = []
for param_name, param_type in member_type['params']:
resolved_param = self.resolve_binding_type(symbol, param_name, param_type, type_overrides)
if resolved_param is None:
return None
resolved_parameters.append((param_name, resolved_param))
resolved_return_type = self.resolve_binding_type(symbol, None, member_type['return_type'], type_overrides)
if resolved_return_type is None:
return None
return self.format_type({
"params": resolved_parameters,
"return_type": resolved_return_type,
})
def generate_structs(self) -> None:
for struct, struct_data in sorted(self.extracted_symbols.structs.items(), key=lambda x: x[0]):
members = struct_data['attrs']
if not struct.startswith('Clay_'):
continue
if struct in SYMBOL_COMPLETE_OVERRIDES:
continue
binding_name = self.get_symbol_name(struct)
if binding_name.startswith('_'):
continue
if struct in STRUCT_OVERRIDE_AS_FIXED_ARRAY:
array_size = len(members)
first_elem = list(members.values())[0]
array_type = None
if 'type' in first_elem:
array_type = first_elem['type']
if array_type in TYPE_MAPPING:
array_binding_type = TYPE_MAPPING[array_type]
elif array_type and self.has_symbol(self.format_type(array_type)):
array_binding_type = self.get_symbol_name(self.format_type(array_type))
else:
self._write('struct', f"// {struct} ({array_type}) - has no mapping")
continue
self._write('struct', f"// {struct} (overridden as fixed array)")
self._write('struct', f"{binding_name} :: [{array_size}]{array_binding_type}")
self._write('struct', "")
continue
raw_union = ' #raw_union' if struct_data.get('is_union', False) else ''
self._write('struct', f"// {struct}")
self._write('struct', f"{binding_name} :: struct{raw_union} {{")
for member, member_info in members.items():
if struct in STRUCT_TYPE_OVERRIDES and member in STRUCT_TYPE_OVERRIDES[struct]:
member_type = 'unknown'
elif not 'type' in member_info:
self._write('struct', f" // {member} (unknown type)")
continue
else:
member_type = member_info['type']
binding_member_name = member
if struct in STRUCT_MEMBER_OVERRIDES and member in STRUCT_MEMBER_OVERRIDES[struct]:
binding_member_name = STRUCT_MEMBER_OVERRIDES[struct][member]
member_binding_type = self.resolve_binding_type(struct, member, member_type, STRUCT_TYPE_OVERRIDES)
if member_binding_type is None:
self._write('struct', f" // {binding_member_name} ({member_type}) - has no mapping")
continue
self._write('struct', f" {binding_member_name}: {member_binding_type}, // {member} ({member_type})")
self._write('struct', "}")
self._write('struct', '')
def generate_enums(self) -> None:
for enum, members in sorted(self.extracted_symbols.enums.items(), key=lambda x: x[0]):
if not enum.startswith('Clay_'):
continue
if enum in SYMBOL_COMPLETE_OVERRIDES:
continue
binding_name = self.get_symbol_name(enum)
common_member_prefix = get_common_prefix(list(members.keys()))
self._write('enum', f"// {enum}")
self._write('enum', f"{binding_name} :: enum EnumBackingType {{")
for member in members:
if enum in ENUM_MEMBER_OVERRIDES and member in ENUM_MEMBER_OVERRIDES[enum]:
binding_member_name = ENUM_MEMBER_OVERRIDES[enum][member]
else:
binding_member_name = member.removeprefix(common_member_prefix)
if enum in ENUM_MEMBER_PASCAL:
binding_member_name = snake_case_to_pascal_case(binding_member_name)
if members[member] is not None:
self._write('enum', f" {binding_member_name} = {members[member]}, // {member}")
else:
self._write('enum', f" {binding_member_name}, // {member}")
if enum in ENUM_ADDITIONAL_MEMBERS:
self._write('enum', ' // Odin specific enum types')
for member, value in ENUM_ADDITIONAL_MEMBERS[enum].items():
self._write('enum', f" {member} = {value},")
self._write('enum', "}")
self._write('enum', '')
def generate_functions(self) -> None:
for function, function_info in sorted(self.extracted_symbols.functions.items(), key=lambda x: x[0]):
if not function.startswith('Clay_'):
continue
if function in SYMBOL_COMPLETE_OVERRIDES:
continue
is_private = function.startswith('Clay__')
write_to = 'private_function' if is_private else 'public_function'
binding_name = self.get_symbol_name(function)
return_type = function_info['return_type']
binding_return_type = self.resolve_binding_type(function, None, return_type, {})
if binding_return_type is None:
self._write(write_to, f" // {function} ({return_type}) - has no mapping")
continue
skip = False
binding_params = []
for param_name, param_type in function_info['params']:
binding_param_name = param_name
if function in FUNCTION_PARAM_OVERRIDES and param_name in FUNCTION_PARAM_OVERRIDES[function]:
binding_param_name = FUNCTION_PARAM_OVERRIDES[function][param_name]
binding_param_type = self.resolve_binding_type(function, param_name, param_type, FUNCTION_TYPE_OVERRIDES)
if binding_param_type is None:
skip = True
binding_params.append(f"{binding_param_name}: {binding_param_type}")
if skip:
self._write(write_to, f" // {function} - has no mapping")
continue
binding_params_str = ', '.join(binding_params)
return_str = f" -> {binding_return_type}" if binding_return_type != 'void' else ''
self._write(write_to, f" {binding_name} :: proc({binding_params_str}){return_str} --- // {function}")

173
generator/parser.py Normal file
View File

@ -0,0 +1,173 @@
from dataclasses import dataclass
from typing import Optional, TypedDict, NotRequired, Union
from pycparser import c_ast, parse_file, preprocess_file
from pathlib import Path
import os
import json
import shutil
import logging
logger = logging.getLogger(__name__)
ExtractedSymbolType = Union[str, "ExtractedFunction"]
class ExtractedStructAttributeUnion(TypedDict):
type: Optional[ExtractedSymbolType]
class ExtractedStructAttribute(TypedDict):
type: NotRequired[ExtractedSymbolType]
union: NotRequired[dict[str, Optional[ExtractedSymbolType]]]
class ExtractedStruct(TypedDict):
attrs: dict[str, ExtractedStructAttribute]
is_union: NotRequired[bool]
ExtractedEnum = dict[str, Optional[str]]
ExtractedFunctionParam = tuple[str, Optional[ExtractedSymbolType]]
class ExtractedFunction(TypedDict):
return_type: Optional["ExtractedSymbolType"]
params: list[ExtractedFunctionParam]
@dataclass
class ExtractedSymbols:
structs: dict[str, ExtractedStruct]
enums: dict[str, ExtractedEnum]
functions: dict[str, ExtractedFunction]
def get_type_names(node: c_ast.Node, prefix: str="") -> Optional[ExtractedSymbolType]:
if isinstance(node, c_ast.TypeDecl) and hasattr(node, 'quals') and node.quals:
prefix = " ".join(node.quals) + " " + prefix
if isinstance(node, c_ast.PtrDecl):
prefix = "*" + prefix
if isinstance(node, c_ast.FuncDecl):
func: ExtractedFunction = {
'return_type': get_type_names(node.type),
'params': [],
}
for param in node.args.params:
if param.name is None:
continue
func['params'].append((param.name, get_type_names(param)))
return func
if hasattr(node, 'names'):
return prefix + node.names[0] # type: ignore
elif hasattr(node, 'type'):
return get_type_names(node.type, prefix) # type: ignore
return None
class Visitor(c_ast.NodeVisitor):
def __init__(self):
self.structs: dict[str, ExtractedStruct] = {}
self.enums: dict[str, ExtractedEnum] = {}
self.functions: dict[str, ExtractedFunction] = {}
def visit_FuncDecl(self, node: c_ast.FuncDecl):
# node.show()
# logger.debug(node)
node_type = node.type
is_pointer = False
if isinstance(node.type, c_ast.PtrDecl):
node_type = node.type.type
is_pointer = True
if hasattr(node_type, "declname"):
return_type = get_type_names(node_type.type)
if return_type is not None and isinstance(return_type, str) and is_pointer:
return_type = "*" + return_type
func: ExtractedFunction = {
'return_type': return_type,
'params': [],
}
for param in node.args.params:
if param.name is None:
continue
func['params'].append((param.name, get_type_names(param)))
self.functions[node_type.declname] = func
self.generic_visit(node)
def visit_Struct(self, node: c_ast.Struct):
# node.show()
if node.name and node.decls:
struct = {}
for decl in node.decls:
struct[decl.name] = {
"type": get_type_names(decl),
}
self.structs[node.name] = {
'attrs': struct,
}
self.generic_visit(node)
def visit_Typedef(self, node: c_ast.Typedef):
# node.show()
if hasattr(node.type, 'type') and hasattr(node.type.type, 'decls') and node.type.type.decls:
struct = {}
for decl in node.type.type.decls:
if hasattr(decl, 'type') and hasattr(decl.type, 'type') and isinstance(decl.type.type, c_ast.Union):
union = {}
for field in decl.type.type.decls:
union[field.name] = get_type_names(field)
struct[decl.name] = {
'union': union
}
else:
struct[decl.name] = {
"type": get_type_names(decl),
}
self.structs[node.name] = {
'attrs': struct,
'is_union': isinstance(node.type.type, c_ast.Union),
}
if hasattr(node.type, 'type') and isinstance(node.type.type, c_ast.Enum):
enum = {}
for enumerator in node.type.type.values.enumerators:
if enumerator.value is None:
enum[enumerator.name] = None
else:
enum[enumerator.name] = enumerator.value.value
self.enums[node.name] = enum
self.generic_visit(node)
def parse_headers(input_files: list[Path], tmp_dir: Path) -> ExtractedSymbols:
cpp_args = ["-nostdinc", "-D__attribute__(x)=", "-E"]
# Make a new clay.h that combines the provided input files, so that we can add bindings for customized structs
with open(tmp_dir / 'merged_clay.h', 'w') as f:
for input_file in input_files:
with open(input_file, 'r') as f2:
for line in f2:
# Ignore includes, as they should be manually included in input_files.
if line.startswith("#include"):
continue
# Ignore the CLAY_IMPLEMENTATION define, because we only want to parse the public api code.
# This is helpful so that the user can provide their implementation code, which will contain any custom extensions
if "#define CLAY_IMPLEMENTATION" in line:
continue
f.write(line)
# Preprocess the file
logger.info("Preprocessing file")
preprocessed = preprocess_file(tmp_dir / 'merged_clay.h', cpp_path="cpp", cpp_args=cpp_args) # type: ignore
with open(tmp_dir / 'clay.preprocessed.h', 'w') as f:
f.write(preprocessed)
# Parse the file
logger.info("Parsing file")
ast = parse_file(tmp_dir / 'clay.preprocessed.h', use_cpp=False) # type: ignore
# Extract symbols
visitor = Visitor()
visitor.visit(ast)
result = ExtractedSymbols(
structs=visitor.structs,
enums=visitor.enums,
functions=visitor.functions
)
return result

View File

@ -0,0 +1 @@
pycparser==2.22