Compare commits

...

11 Commits

Author SHA1 Message Date
Jefferey S
10c721a060
Merge c133e07096 into 3961720ef0 2025-02-11 13:51:47 -06: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
Nic Barker
5a328da308 [Bindings/Odin] Switch error enum to correct size 2025-02-11 10:51:10 +13:00
Jefferey Schlueter
c133e07096 Added in some missing Clay macros and moved re-used config declarations out of global scope into main for video-example.c3 2025-02-04 14:49:57 -05:00
Jefferey S
afe52f946b
Update README.md 2025-01-29 12:34:07 -05:00
Jefferey Schlueter
773d18a1cc Merge branch 'clay-bindings-c3'
Conflicts:
	bindings/c3/examples/video-example.c3
	bindings/c3/project.json
	bindings/c3/source/clay-raylib-renderer.c3
2025-01-29 12:26:19 -05:00
Jefferey Schlueter
1b67696843 added .gitkeeps to the lib and build folder 2025-01-29 12:17:46 -05:00
Jefferey Schlueter
9dc9e4992c Somehow I deleted the example .c3 file. Fixed project settings, I forgot I can't drag and drop when it comes to relative project files. 2025-01-29 12:04:40 -05:00
Jefferey S
91295a2a9a
Update README.md 2025-01-28 23:54:37 -05:00
Jefferey Schlueter
f48ee0b9fd Added C3 Bindings 2025-01-28 23:49:19 -05:00
17 changed files with 6113 additions and 163 deletions

71
bindings/c3/README.md Normal file
View File

@ -0,0 +1,71 @@
# Clay-C3-Bindings
C3 Bindings for [Clay](https://github.com/nicbarker/clay.git), a UI layout library written in C.
This directory contains the clay.c3 bindings file as well as a recreation of the clay-raylib-renderer and the video-example raylib/clay project.
Special thanks to:
- [Christoffer L](https://github.com/lerno) C3's core developer (as I understand it)
- Book-reader in the [C3-lang Discord](https://discord.gg/qN76R87)
## TODO:
- Find out how to build a static-lib with additional C sources
## - C3 macros
Traditional Clay C Macro System
```cpp
/* FILTER BUTTON */
CLAY(
CLAY_ID("FilterButton"),
Clay_Hovered() ? CLAY_RECTANGLE({
.color = Clay_Hovered() ? FIRE_ORANGE : (Clay_Color){80, 25, 200, 255},
.cornerRadius = 8,
}) : 0,
CLAY_LAYOUT({
.sizing = {
.width = CLAY_SIZING_FIT(),
.height = CLAY_SIZING_GROW()
},
.padding = 10
})
) {
// define children...
}
```
Clay C3 Macro System
```cpp
/* FILTER BUTTON */
@clay(
clay::id("FilterButton"),
clay::@bodyIf(clay::hovered(), clay::rectangle({
.color = clay::hovered() ? FIRE_ORANGE : {80, 25, 200, 255},
.cornerRadius = clay::cornerRadiusUni(8)
})
),
clay::layout({
.sizing = {
.width = clay::sizingFit(),
.height = clay::sizingGrow()
},
.padding = clay::paddingUni(8)
})
){
// define children...
};
```
## To Get Started:
- Download c3c [here](https://c3-lang.org/getting-started/prebuilt-binaries/)
- If you wish to compile the website-example, I've already provided a target to build in the [project.json](project.json)
- - set your `cd` to [c3](./)
- - use the `c3c vendor-fetch raylib55` command to download a c3 compressed archive of raylib
- - - *once you have raylib55.c3l in the [lib](lib) folder you've got it right*
- - then use the command `c3c run video-example` to compile and run the video example
- - - (*note: to use the `c3c build <target>` command with video-example, you'll need to copy the resource folder into the [build](build) directory to run it*
- - - *`run` executes the build result from the project directory, somehow. This means that `run` will look for the resource folder in [c3](../c3), while `build` will look for it in [build](build)*)
## RESOURCES:
### - [C3](https://github.com/c3lang/c3c.git) (A C-a-like, that aims to bring modern language QA features and a revamped Macro system to C)
### - [Raylib](https://github.com/raysan5/raylib.git) (C Videogame and Graphical API)
### - [Lexend](https://github.com/googlefonts/lexend.git) (Accessible/ Dyslexic Friendly Font)

View File

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
module videoexample;
import clay;
import raylib5::rl;
const uint FONT_ID_BODY_16 = 0;
fn void errorHandler(ErrorData errorData)
{
// std::io::printfn("ERROR: \"%s\"", errorData.errorText.chars);
}
fn void renderDropdownMenuItem(String text) {
@clay(
clay::layout({ .padding = clay::paddingUni(16)})
) {
clay::text(
text, clay::textConfig({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
}));
};
}
fn void renderHeaderButton(String text) {
@clay(
clay::layout({ .padding = { 16, 16, 8, 8 }}),
clay::rectangle({
.color = { 140, 140, 140, 255 },
.cornerRadius = clay::cornerRadiusUni(5)
})
) {
clay::text(text, clay::textConfig({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
}));
};
}
fn void handleSidebarInteraction(
ElementId elementId,
PointerData pointerData,
iptr userData
) {
// If this button was clicked
if (pointerData.state == PointerState.RELEASED_THIS_FRAME) {
if (userData >= 0 && userData < ALL_DOCUMENTS.len) {
// Select the corresponding document
selectedDocumentIndex = (uint)userData;
}
}
}
struct Document {
String title;
String contents;
}
const Document[*] ALL_DOCUMENTS = {
{ "Squirrels", "The Secret Life of Squirrels: Nature's Clever Acrobats\n""Squirrels are often overlooked creatures, dismissed as mere park inhabitants or backyard nuisances. Yet, beneath their fluffy tails and twitching noses lies an intricate world of cunning, agility, and survival tactics that are nothing short of fascinating. As one of the most common mammals in North America, squirrels have adapted to a wide range of environments from bustling urban centers to tranquil forests and have developed a variety of unique behaviors that continue to intrigue scientists and nature enthusiasts alike.\n""\n""Master Tree Climbers\n""At the heart of a squirrel's skill set is its impressive ability to navigate trees with ease. Whether they're darting from branch to branch or leaping across wide gaps, squirrels possess an innate talent for acrobatics. Their powerful hind legs, which are longer than their front legs, give them remarkable jumping power. With a tail that acts as a counterbalance, squirrels can leap distances of up to ten times the length of their body, making them some of the best aerial acrobats in the animal kingdom.\n""But it's not just their agility that makes them exceptional climbers. Squirrels' sharp, curved claws allow them to grip tree bark with precision, while the soft pads on their feet provide traction on slippery surfaces. Their ability to run at high speeds and scale vertical trunks with ease is a testament to the evolutionary adaptations that have made them so successful in their arboreal habitats.\n""\n""Food Hoarders Extraordinaire\n""Squirrels are often seen frantically gathering nuts, seeds, and even fungi in preparation for winter. While this behavior may seem like instinctual hoarding, it is actually a survival strategy that has been honed over millions of years. Known as \"scatter hoarding,\" squirrels store their food in a variety of hidden locations, often burying it deep in the soil or stashing it in hollowed-out tree trunks.\n""Interestingly, squirrels have an incredible memory for the locations of their caches. Research has shown that they can remember thousands of hiding spots, often returning to them months later when food is scarce. However, they don't always recover every stash some forgotten caches eventually sprout into new trees, contributing to forest regeneration. This unintentional role as forest gardeners highlights the ecological importance of squirrels in their ecosystems.\n""\n""The Great Squirrel Debate: Urban vs. Wild\n""While squirrels are most commonly associated with rural or wooded areas, their adaptability has allowed them to thrive in urban environments as well. In cities, squirrels have become adept at finding food sources in places like parks, streets, and even garbage cans. However, their urban counterparts face unique challenges, including traffic, predators, and the lack of natural shelters. Despite these obstacles, squirrels in urban areas are often observed using human infrastructure such as buildings, bridges, and power lines as highways for their acrobatic escapades.\n""There is, however, a growing concern regarding the impact of urban life on squirrel populations. Pollution, deforestation, and the loss of natural habitats are making it more difficult for squirrels to find adequate food and shelter. As a result, conservationists are focusing on creating squirrel-friendly spaces within cities, with the goal of ensuring these resourceful creatures continue to thrive in both rural and urban landscapes.\n""\n""A Symbol of Resilience\n""In many cultures, squirrels are symbols of resourcefulness, adaptability, and preparation. Their ability to thrive in a variety of environments while navigating challenges with agility and grace serves as a reminder of the resilience inherent in nature. Whether you encounter them in a quiet forest, a city park, or your own backyard, squirrels are creatures that never fail to amaze with their endless energy and ingenuity.\n""In the end, squirrels may be small, but they are mighty in their ability to survive and thrive in a world that is constantly changing. So next time you spot one hopping across a branch or darting across your lawn, take a moment to appreciate the remarkable acrobat at work a true marvel of the natural world.\n" },
{ "Lorem Ipsum", "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." },
{ "Vacuum Instructions", "Chapter 3: Getting Started - Unpacking and Setup\n""\n""Congratulations on your new SuperClean Pro 5000 vacuum cleaner! In this section, we will guide you through the simple steps to get your vacuum up and running. Before you begin, please ensure that you have all the components listed in the \"Package Contents\" section on page 2.\n""\n""1. Unboxing Your Vacuum\n""Carefully remove the vacuum cleaner from the box. Avoid using sharp objects that could damage the product. Once removed, place the unit on a flat, stable surface to proceed with the setup. Inside the box, you should find:\n""\n"" The main vacuum unit\n"" A telescoping extension wand\n"" A set of specialized cleaning tools (crevice tool, upholstery brush, etc.)\n"" A reusable dust bag (if applicable)\n"" A power cord with a 3-prong plug\n"" A set of quick-start instructions\n""\n""2. Assembling Your Vacuum\n""Begin by attaching the extension wand to the main body of the vacuum cleaner. Line up the connectors and twist the wand into place until you hear a click. Next, select the desired cleaning tool and firmly attach it to the wand's end, ensuring it is securely locked in.\n""\n""For models that require a dust bag, slide the bag into the compartment at the back of the vacuum, making sure it is properly aligned with the internal mechanism. If your vacuum uses a bagless system, ensure the dust container is correctly seated and locked in place before use.\n""\n""3. Powering On\n""To start the vacuum, plug the power cord into a grounded electrical outlet. Once plugged in, locate the power switch, usually positioned on the side of the handle or body of the unit, depending on your model. Press the switch to the \"On\" position, and you should hear the motor begin to hum. If the vacuum does not power on, check that the power cord is securely plugged in, and ensure there are no blockages in the power switch.\n""\n""Note: Before first use, ensure that the vacuum filter (if your model has one) is properly installed. If unsure, refer to \"Section 5: Maintenance\" for filter installation instructions." },
{ "Article 4", "Article 4" },
{ "Article 5", "Article 5" }
};
uint selectedDocumentIndex = 0;
bool isDebugModeEnabled = false;
fn void main() @public
{
rl::setConfigFlags(rl::FLAG_VSYNC_HINT | rl::FLAG_WINDOW_RESIZABLE | rl::FLAG_WINDOW_HIGHDPI | rl::FLAG_MSAA_4X_HINT);
rl::initWindow(1024, 768, "Introducing Clay C3-Demo");
defer rl::closeWindow();
uint clayRequiredMemory = clay::minMemorySize();
Arena clayMemory = clay::createArena(clayRequiredMemory, malloc(clayRequiredMemory));
clay::initialize(clayMemory, { rl::getScreenWidth(), rl::getScreenHeight() }, { &errorHandler, 0 });
clay::setMeasureTextFunction(&clay::renderer::raylibMeasureText);
clay::renderer::raylibFonts[FONT_ID_BODY_16] = {
FONT_ID_BODY_16,
rl::loadFontEx("resources/Lexend-Regular.ttf", 48, null, 400)
};
rl::setTextureFilter(
clay::renderer::raylibFonts[FONT_ID_BODY_16].font.texture,
TextureFilter.BILINEAR
);
// ==============================================
// ===== predeclarations of re-used configs =====
// ==============================================
RectangleElementConfig contentBackgroundConfig = {
{ 90, 90, 90, 255 },
clay::@cornerRadiusUniCT(8)
};
Sizing layoutExpand = Sizing{ clay::sizingGrow(), clay::sizingGrow() };
LayoutConfig sidebarButtonLayout = LayoutConfig{
.sizing = { .width = clay::sizingGrow() },
.padding = clay::paddingCT(16, 16, 16, 16)
};
while (!rl::windowShouldClose()) {
clay::setLayoutDimensions({rl::getScreenWidth(), rl::getScreenHeight()});
Vector2 mouse_position = rl::getMousePosition();
Vector2 scroll_delta = rl::getMouseWheelMoveV();
clay::setPointerState({mouse_position.x, mouse_position.y}, rl::isMouseButtonDown(MouseButton.LEFT));
clay::updateScrollContainer(true, {scroll_delta.x, scroll_delta.y}, rl::getFrameTime());
if (rl::isKeyReleased(rl::KEY_D)) { isDebugModeEnabled = !isDebugModeEnabled; clay::setDebugModeEnabled(isDebugModeEnabled); }
clay::beginLayout();
@clay(
clay::id("OuterContainer"),
clay::rectangle({ .color = { 43, 41, 51, 255 } }),
clay::layout({
.sizing = layoutExpand,
.padding = clay::paddingUni(16),
.childGap = 16,
.layoutDirection = LayoutDirection.TOP_TO_BOTTOM,
})
) {
@clay(
clay::id("HeaderBar"),
clay::rectangle(contentBackgroundConfig),
clay::layout({
.sizing = {
.height = clay::sizingFixed(60),
.width = clay::sizingGrow()
},
.padding = { 16, 16, 0, 0 },
.childGap = 16,
.childAlignment = {
.y = AlignY.CENTER
}
})
) {
// Header buttons go here
@clay(
clay::id("FileButton"),
clay::layout({ .padding = { 16, 16, 8, 8 }}),
clay::rectangle({
.color = { 140, 140, 140, 255 },
.cornerRadius = clay::cornerRadiusUni(5)
})
) {
clay::text("File", clay::textConfig({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID_BODY_16,
.fontSize = 16,
}));
bool fileMenuVisible =
clay::pointerOver(clay::getElementId("FileButton"))
||
clay::pointerOver(clay::getElementId("FileMenu"));
if (fileMenuVisible) { // Below has been changed slightly to fix the small bug where the menu would dismiss when mousing over the top gap
@clay(
clay::id("FileMenu"),
clay::floating({
.attachment = {
.parent = AttachPoint.LEFT_BOTTOM
},
}),
clay::layout({
.padding = {0, 0, 8, 8 }
})
) {
@clay(
clay::layout({
.sizing = {
.width = clay::sizingFixed(200)
},
.layoutDirection = LayoutDirection.TOP_TO_BOTTOM,
}),
clay::rectangle({
.color = { 40, 40, 40, 255 },
.cornerRadius = clay::cornerRadiusUni(8)
})
) {
// Render dropdown items here
renderDropdownMenuItem("New");
renderDropdownMenuItem("Open");
renderDropdownMenuItem("Close");
};
};
}
};
renderHeaderButton("Edit");
@clay(clay::layout({ .sizing = { .width = clay::sizingGrow() }})) {};
renderHeaderButton("Upload");
renderHeaderButton("Media");
renderHeaderButton("Support");
};
@clay(
clay::id("LowerContent"),
clay::layout({ .sizing = layoutExpand, .childGap = 16 })
) {
@clay(
clay::id("Sidebar"),
contentBackgroundConfig,
clay::layout({
.sizing = {
.width = clay::sizingFixed(250),
.height = clay::sizingGrow()
},
.padding = clay::paddingUni(16),
.childGap = 8,
.layoutDirection = LayoutDirection.TOP_TO_BOTTOM,
})
) {
for (int i = 0; i < ALL_DOCUMENTS.len; i++) {
Document document = ALL_DOCUMENTS[i];
if (i == selectedDocumentIndex) {
@clay(
clay::layout(sidebarButtonLayout),
clay::rectangle({
.color = { 120, 120, 120, 255 },
.cornerRadius = clay::cornerRadiusUni(8),
})
) {
clay::text(document.title, clay::textConfig({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID_BODY_16,
.fontSize = 20,
}));
};
} else {
@clay(
clay::layout(sidebarButtonLayout),
clay::onHover(&handleSidebarInteraction, i),
clay::@bodyIf(
clay::hovered(),
clay::rectangle({
.color = { 120, 120, 120, 120 },
.cornerRadius = clay::cornerRadiusUni(8)
})
),
) {
clay::text(document.title, clay::textConfig({
.textColor = { 255, 255, 255, 255 },
.fontId = FONT_ID_BODY_16,
.fontSize = 20,
}));
};
}
}
};
@clay(
clay::id("MainContent"),
clay::rectangle(contentBackgroundConfig),
clay::scroll({ .vertical = true }),
clay::layout({
.layoutDirection = LayoutDirection.TOP_TO_BOTTOM,
.childGap = 14,
.padding = clay::paddingUni(16),
.sizing = layoutExpand,
}),
) {
Document document = ALL_DOCUMENTS[selectedDocumentIndex];
clay::text(document.title, clay::textConfig({
.fontId = FONT_ID_BODY_16,
.fontSize = 24,
.textColor = {255, 255, 255, 255},
}));
clay::text(document.contents, clay::textConfig({
.fontId = FONT_ID_BODY_16,
.fontSize = 24,
.textColor = {255, 255, 255, 255},
}));
};
};
};
RenderCommandArray renderCommands = clay::endLayout();
rl::beginDrawing();
rl::clearBackground(rl::WHITE);
clay::renderer::raylibRender(renderCommands);
rl::endDrawing();
}
}

0
bindings/c3/lib/.gitkeep Normal file
View File

33
bindings/c3/project.json Normal file
View File

@ -0,0 +1,33 @@
{
"langrev": "1",
"authors": [ "Jefferey Schlueter <jefferey.l.schlueter@gmail.com>" ],
"version": "0.1.0",
"dependency-search-paths": [ "lib" ],
"targets": {
// TODO: found out how to stop this from outputting a .lib and .pdb in addition to the .exe (they don't do anything)
"video-example": {
"output": "build/video-example/",
"c-sources": [ "c-lang/source/clay.c" ],
"cflags": "-DCLAY_IMPLEMENTATION", // makes the clay source actually define things
"type": "executable",
"dependencies": [ "raylib55" ],
"sources": [ "source/clay.c3", "source/clay-raylib-renderer.c3", "examples/video-example.c3" ],
// "link-libc": false, // TODO; leads to duplicate definitions (eg math_nolibc)
// "use-stdlib": false, // TODO: leads to ZString being undefined -> then missing main @main_to_void_main
// "features": ["NO_STDLIB"]
},
// TODO: figure out why creating static/dynamic libraries with C sources doesn't work (emits no errors just crashes out and dumps an empty clay-win.h into the project dir)
// "clay-win": {
// "output": "build/clay.c3l/windows-x64",
// "c-sources": [ "c-lang/source/clay.c" ],
// "sources": [ "source/clay.c3" ],
// "type": "static-lib"
// },
// build args with no C references
//../c3c.exe --link-libc=no --use-stdlib=no -o clay --template static-lib --output-dir build/clay.c3l/windows-x64 --print-linking --print-output -vvv static-lib source/clay-slim.c3
},
"cc": "gcc",
"cpu": "generic",
}

Binary file not shown.

View File

@ -0,0 +1,447 @@
module raylib5::rl @if($feature(NO_STDLIB));
distinct ZString = inline char*;
module clay::renderer;
import raylib5::rl;
// TODO: this entire file was very rushed so it can probably be cleaned up a LOT
fn double calculate_sine(double x) {
double term = x;
double sum = term;
double n = 1;
for (int i = 1; i <= 10; i++) {
term *= (-1) * x * x / ((2 * n) * (2 * n + 1));
sum += term;
n++;
}
return sum;
}
fn double calculate_cosine(double x) {
double term = 1;
double sum = term;
double n = 1;
for (int i = 1; i <= 10; i++) {
term *= (-1) * x * x / ((2 * n - 1) * (2 * n));
sum += term;
n++;
}
return sum;
}
macro double calculate_tangent(double x) {
double sine = calculate_sine(x);
double cosine = calculate_cosine(x);
// Check for cases where cosine is zero (tangent is undefined)
if (cosine == 0.0) {
return 0.0;
}
return sine / cosine;
}
fn Color clayToRaylibColor(ClayColor color) @inline
{
return { (char)$$round(color.r), (char)$$round(color.g), (char)$$round(color.b), (char)$$round(color.a)};
}
struct RaylibFont
{
int fontId;
rl::Font font;
}
RaylibFont[10] raylibFonts;
rl::Camera raylibCamera;
const Matrix MATRIX_IDENTITY = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1,
};
enum CustomLayoutElementType
{
MODEL_3D
}
struct Model3DLayoutElement
{
rl::Model model;
float scale;
rl::Vector3 position;
rl::Matrix rotation;
}
struct CustomLayoutElement
{
CustomLayoutElementType type;
union element
{
Model3DLayoutElement model;
}
}
fn Matrix matrixPerspective(double fovY, double aspect, double nearPlane, double farPlane)
{
Matrix result = { 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 };
double top = nearPlane * calculate_tangent(fovY*0.5*rl::RAD2DEG);
double bottom = -top;
double right = top*aspect;
double left = -right;
// MatrixFrustum(-right, right, -top, top, near, far);
float rl = (float)(right - left);
float tb = (float)(top - bottom);
float far_near = (float)(farPlane - nearPlane);
result.m0 = ((float)nearPlane*2.0f)/rl;
result.m5 = ((float)nearPlane*2.0f)/tb;
result.m8 = ((float)right + (float)left)/rl;
result.m9 = ((float)top + (float)bottom)/tb;
result.m10 = -((float)farPlane + (float)nearPlane)/far_near;
result.m11 = -1.0f;
result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/far_near;
return result;
}
fn Vector3 vector3Unproject(Vector3 source, Matrix projection, Matrix view)
{
Vector3 result = { 0, 0, 0 };
// Calculate unprojected matrix (multiply view matrix by projection matrix) and invert it
Matrix matViewProj = { // MatrixMultiply(view, projection);
view.m0*projection.m0 + view.m1*projection.m4 + view.m2*projection.m8 + view.m3*projection.m12,
view.m0*projection.m1 + view.m1*projection.m5 + view.m2*projection.m9 + view.m3*projection.m13,
view.m0*projection.m2 + view.m1*projection.m6 + view.m2*projection.m10 + view.m3*projection.m14,
view.m0*projection.m3 + view.m1*projection.m7 + view.m2*projection.m11 + view.m3*projection.m15,
view.m4*projection.m0 + view.m5*projection.m4 + view.m6*projection.m8 + view.m7*projection.m12,
view.m4*projection.m1 + view.m5*projection.m5 + view.m6*projection.m9 + view.m7*projection.m13,
view.m4*projection.m2 + view.m5*projection.m6 + view.m6*projection.m10 + view.m7*projection.m14,
view.m4*projection.m3 + view.m5*projection.m7 + view.m6*projection.m11 + view.m7*projection.m15,
view.m8*projection.m0 + view.m9*projection.m4 + view.m10*projection.m8 + view.m11*projection.m12,
view.m8*projection.m1 + view.m9*projection.m5 + view.m10*projection.m9 + view.m11*projection.m13,
view.m8*projection.m2 + view.m9*projection.m6 + view.m10*projection.m10 + view.m11*projection.m14,
view.m8*projection.m3 + view.m9*projection.m7 + view.m10*projection.m11 + view.m11*projection.m15,
view.m12*projection.m0 + view.m13*projection.m4 + view.m14*projection.m8 + view.m15*projection.m12,
view.m12*projection.m1 + view.m13*projection.m5 + view.m14*projection.m9 + view.m15*projection.m13,
view.m12*projection.m2 + view.m13*projection.m6 + view.m14*projection.m10 + view.m15*projection.m14,
view.m12*projection.m3 + view.m13*projection.m7 + view.m14*projection.m11 + view.m15*projection.m15 };
// Calculate inverted matrix . MatrixInvert(matViewProj);
// Cache the matrix values (speed optimization)
float a00 = matViewProj.m0;
float a01 = matViewProj.m1;
float a02 = matViewProj.m2;
float a03 = matViewProj.m3;
float a10 = matViewProj.m4;
float a11 = matViewProj.m5;
float a12 = matViewProj.m6;
float a13 = matViewProj.m7;
float a20 = matViewProj.m8;
float a21 = matViewProj.m9;
float a22 = matViewProj.m10;
float a23 = matViewProj.m11;
float a30 = matViewProj.m12;
float a31 = matViewProj.m13;
float a32 = matViewProj.m14;
float a33 = matViewProj.m15;
float b00 = a00*a11 - a01*a10;
float b01 = a00*a12 - a02*a10;
float b02 = a00*a13 - a03*a10;
float b03 = a01*a12 - a02*a11;
float b04 = a01*a13 - a03*a11;
float b05 = a02*a13 - a03*a12;
float b06 = a20*a31 - a21*a30;
float b07 = a20*a32 - a22*a30;
float b08 = a20*a33 - a23*a30;
float b09 = a21*a32 - a22*a31;
float b10 = a21*a33 - a23*a31;
float b11 = a22*a33 - a23*a32;
// Calculate the invert determinant (inlined to avoid double-caching)
float invDet = 1.0f/(b00*b11 - b01*b10 + b02*b09 + b03*b08 - b04*b07 + b05*b06);
Matrix matViewProjInv = {
(a11*b11 - a12*b10 + a13*b09)*invDet,
(-a01*b11 + a02*b10 - a03*b09)*invDet,
(a31*b05 - a32*b04 + a33*b03)*invDet,
(-a21*b05 + a22*b04 - a23*b03)*invDet,
(-a10*b11 + a12*b08 - a13*b07)*invDet,
(a00*b11 - a02*b08 + a03*b07)*invDet,
(-a30*b05 + a32*b02 - a33*b01)*invDet,
(a20*b05 - a22*b02 + a23*b01)*invDet,
(a10*b10 - a11*b08 + a13*b06)*invDet,
(-a00*b10 + a01*b08 - a03*b06)*invDet,
(a30*b04 - a31*b02 + a33*b00)*invDet,
(-a20*b04 + a21*b02 - a23*b00)*invDet,
(-a10*b09 + a11*b07 - a12*b06)*invDet,
(a00*b09 - a01*b07 + a02*b06)*invDet,
(-a30*b03 + a31*b01 - a32*b00)*invDet,
(a20*b03 - a21*b01 + a22*b00)*invDet };
// Create quaternion from source point
rl::Quaternion quat = { source.x, source.y, source.z, 1.0f };
// Multiply quat point by unprojecte matrix
rl::Quaternion qtransformed = { // QuaternionTransform(quat, matViewProjInv)
matViewProjInv.m0*quat.x + matViewProjInv.m4*quat.y + matViewProjInv.m8*quat.z + matViewProjInv.m12*quat.w,
matViewProjInv.m1*quat.x + matViewProjInv.m5*quat.y + matViewProjInv.m9*quat.z + matViewProjInv.m13*quat.w,
matViewProjInv.m2*quat.x + matViewProjInv.m6*quat.y + matViewProjInv.m10*quat.z + matViewProjInv.m14*quat.w,
matViewProjInv.m3*quat.x + matViewProjInv.m7*quat.y + matViewProjInv.m11*quat.z + matViewProjInv.m15*quat.w };
// Normalized world points in vectors
result.x = qtransformed.x/qtransformed.w;
result.y = qtransformed.y/qtransformed.w;
result.z = qtransformed.z/qtransformed.w;
return result;
}
fn Matrix matrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane)
{
Matrix result = { 0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0,
0, 0, 0, 0 };
float rl = (float)(right - left);
float tb = (float)(top - bottom);
float far_near = (float)(farPlane - nearPlane);
result.m0 = 2.0f/rl;
result.m1 = 0.0f;
result.m2 = 0.0f;
result.m3 = 0.0f;
result.m4 = 0.0f;
result.m5 = 2.0f/tb;
result.m6 = 0.0f;
result.m7 = 0.0f;
result.m8 = 0.0f;
result.m9 = 0.0f;
result.m10 = -2.0f/far_near;
result.m11 = 0.0f;
result.m12 = -((float)left + (float)right)/rl;
result.m13 = -((float)top + (float)bottom)/tb;
result.m14 = -((float)farPlane + (float)nearPlane)/far_near;
result.m15 = 1.0f;
return result;
}
fn Vector3 vector3Normalize(Vector3 v)
{
Vector3 result = v;
float length = $$sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
if (length != 0.0f)
{
float ilength = 1.0f/length;
result.x *= ilength;
result.y *= ilength;
result.z *= ilength;
}
return result;
}
fn Vector3 vector3Subtract(Vector3 v1, Vector3 v2)
{
Vector3 result = { v1.x - v2.x, v1.y - v2.y, v1.z - v2.z };
return result;
}
fn Ray getScreenToWorldPointWithZDistance(Vector2 position, Camera camera, int screenWidth, int screenHeight, float zDistance)
{
Ray ray = { {0,0,0}, {0,0,0} };
float x = (2.0f*position.x)/(float)screenWidth - 1.0f;
float y = 1.0f - (2.0f*position.y)/(float)screenHeight;
float z = 1.0f;
// Store values in a vector
Vector3 deviceCoords = { x, y, z };
// Calculate view matrix from camera look at
Matrix matView = rl::getCameraMatrix(camera);
Matrix matProj = MATRIX_IDENTITY;
if (camera.projection == CameraProjection.PERSPECTIVE)
{
// Calculate projection matrix from perspective
matProj = matrixPerspective((double)(camera.fovy*rl::DEG2RAD), ((double)screenWidth/(double)screenHeight), 0.01f, zDistance);
}
else if (camera.projection == CameraProjection.ORTHOGRAPHIC)
{
double aspect = (double)screenWidth/(double)screenHeight;
double top = camera.fovy/2.0;
double right = top*aspect;
// Calculate projection matrix from orthographic
matProj = matrixOrtho(-right, right, -top, top, 0.01, 1000.0);
}
// Unproject far/near points
Vector3 nearPoint = vector3Unproject({ deviceCoords.x, deviceCoords.y, 0.0f }, matProj, matView);
Vector3 farPoint = vector3Unproject({ deviceCoords.x, deviceCoords.y, 1.0f }, matProj, matView);
// Calculate normalized direction vector
Vector3 direction = vector3Normalize(vector3Subtract(farPoint, nearPoint));
ray.position = farPoint;
// Apply calculated vectors to ray
ray.direction = direction;
return ray;
}
fn Dimensions raylibMeasureText(ClayString *text, TextElementConfig *config)
{
// Measure string size for Font
Dimensions textSize = { 0, 0 };
float maxTextWidth = 0.0f;
float lineTextWidth = 0;
float textHeight = config.fontSize;
Font fontToUse = raylibFonts[config.fontId].font;
float scaleFactor = config.fontSize/(float)fontToUse.baseSize;
for (int i = 0; i < text.length; ++i)
{
if (text.chars[i] == '\n') {
maxTextWidth = $$max(maxTextWidth, lineTextWidth);
lineTextWidth = 0;
continue;
}
int index = text.chars[i] - 32;
if (fontToUse.glyphs[index].advanceX != 0) {lineTextWidth += fontToUse.glyphs[index].advanceX;}
else {lineTextWidth += (fontToUse.recs[index].width + fontToUse.glyphs[index].offsetX);}
}
maxTextWidth = $$max(maxTextWidth, lineTextWidth);
textSize.width = maxTextWidth * scaleFactor;
textSize.height = textHeight;
return textSize;
}
fn void raylibRender(RenderCommandArray renderCommands)
{
for (int j = 0; j < renderCommands.length; j++)
{
RenderCommand *renderCommand = renderCommands.get(j);
ClayBoundingBox boundingBox = renderCommand.boundingBox;
switch (renderCommand.commandType)
{
case RenderCommandType.TEXT: {
// Raylib uses standard C strings so isn't compatible with cheap slices, we need to clone the string to append null terminator
ClayString text = renderCommand.text;
ZString cloned = (ZString)malloc((ulong)text.length + 1);
$$memcpy(cloned, text.chars, (isz)text.length, false, (isz)0, (isz)0);
cloned[text.length] = '\0';
Font fontToUse = raylibFonts[renderCommand.config.textElementConfig.fontId].font;
rl::drawTextEx(fontToUse, cloned, {boundingBox.x, boundingBox.y}, (float)renderCommand.config.textElementConfig.fontSize, (float)renderCommand.config.textElementConfig.letterSpacing, clayToRaylibColor(renderCommand.config.textElementConfig.textColor));
free(cloned);
break;
}
case RenderCommandType.IMAGE: {
Texture2D imageTexture = *(Texture2D *)renderCommand.config.imageElementConfig.imageData;
rl::drawTextureEx(
imageTexture,
{boundingBox.x, boundingBox.y},
0,
boundingBox.width / (float)imageTexture.width,
rl::WHITE);
break;
}
case RenderCommandType.SCISSOR_START: {
rl::beginScissorMode((int)$$round(boundingBox.x), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width), (int)$$round(boundingBox.height));
break;
}
case RenderCommandType.SCISSOR_END: {
rl::endScissorMode();
break;
}
case RenderCommandType.RECTANGLE: {
RectangleElementConfig *config = renderCommand.config.rectangleElementConfig;
if (config.cornerRadius.topLeft > 0) {
float radius = (config.cornerRadius.topLeft * 2) / (float)(boundingBox.width > boundingBox.height ? boundingBox.height : boundingBox.width);
rl::drawRectangleRounded({ boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height }, radius, 8, clayToRaylibColor(config.color));
} else {
rl::drawRectangle((int)$$round(boundingBox.x), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width), (int)$$round(boundingBox.height), clayToRaylibColor(config.color));
}
break;
}
case RenderCommandType.BORDER: {
BorderElementConfig *config = renderCommand.config.borderElementConfig;
// Left border
if (config.left.width > 0) {
rl::drawRectangle((int)$$round(boundingBox.x), (int)$$round(boundingBox.y + config.cornerRadius.topLeft), (int)config.left.width, (int)$$round(boundingBox.height - config.cornerRadius.topLeft - config.cornerRadius.bottomLeft), clayToRaylibColor(config.left.color));
}
// Right border
if (config.right.width > 0) {
rl::drawRectangle((int)$$round(boundingBox.x + boundingBox.width - config.right.width), (int)$$round(boundingBox.y + config.cornerRadius.topRight), (int)config.right.width, (int)$$round(boundingBox.height - config.cornerRadius.topRight - config.cornerRadius.bottomRight), clayToRaylibColor(config.right.color));
}
// Top border
if (config.top.width > 0) {
rl::drawRectangle((int)$$round(boundingBox.x + config.cornerRadius.topLeft), (int)$$round(boundingBox.y), (int)$$round(boundingBox.width - config.cornerRadius.topLeft - config.cornerRadius.topRight), (int)config.top.width, clayToRaylibColor(config.top.color));
}
// Bottom border
if (config.bottom.width > 0) {
rl::drawRectangle((int)$$round(boundingBox.x + config.cornerRadius.bottomLeft), (int)$$round(boundingBox.y + boundingBox.height - config.bottom.width), (int)$$round(boundingBox.width - config.cornerRadius.bottomLeft - config.cornerRadius.bottomRight), (int)config.bottom.width, clayToRaylibColor(config.bottom.color));
}
if (config.cornerRadius.topLeft > 0) {
rl::drawRing({ $$round(boundingBox.x + config.cornerRadius.topLeft), $$round(boundingBox.y + config.cornerRadius.topLeft) }, $$round(config.cornerRadius.topLeft - config.top.width), config.cornerRadius.topLeft, 180, 270, 10, clayToRaylibColor(config.top.color));
}
if (config.cornerRadius.topRight > 0) {
rl::drawRing({ $$round(boundingBox.x + boundingBox.width - config.cornerRadius.topRight), $$round(boundingBox.y + config.cornerRadius.topRight) }, $$round(config.cornerRadius.topRight - config.top.width), config.cornerRadius.topRight, 270, 360, 10, clayToRaylibColor(config.top.color));
}
if (config.cornerRadius.bottomLeft > 0) {
rl::drawRing({ $$round(boundingBox.x + config.cornerRadius.bottomLeft), $$round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomLeft) }, $$round(config.cornerRadius.bottomLeft - config.top.width), config.cornerRadius.bottomLeft, 90, 180, 10, clayToRaylibColor(config.bottom.color));
}
if (config.cornerRadius.bottomRight > 0) {
rl::drawRing({ $$round(boundingBox.x + boundingBox.width - config.cornerRadius.bottomRight), $$round(boundingBox.y + boundingBox.height - config.cornerRadius.bottomRight) }, $$round(config.cornerRadius.bottomRight - config.bottom.width), config.cornerRadius.bottomRight, 0.1, 90, 10, clayToRaylibColor(config.bottom.color));
}
break;
}
case RenderCommandType.CUSTOM: {
CustomLayoutElement *customElement = (CustomLayoutElement *)renderCommand.config.customElementConfig.customData;
if (!customElement) continue;
switch (customElement.type) {
case CustomLayoutElementType.MODEL_3D: {
ClayBoundingBox rootBox = renderCommands.data[0].boundingBox;
float scaleValue = $$min($$min(1f, 768f / rootBox.height) * $$max(1f, rootBox.width / 1024f), 1.5f);
Ray positionRay = getScreenToWorldPointWithZDistance({ renderCommand.boundingBox.x + renderCommand.boundingBox.width / 2, renderCommand.boundingBox.y + (renderCommand.boundingBox.height / 2) + 20 }, raylibCamera, (int)$$round(rootBox.width), (int)$$round(rootBox.height), 140);
rl::beginMode3D(raylibCamera);
rl::drawModel(customElement.element.model.model, positionRay.position, customElement.element.model.scale * scaleValue, rl::WHITE); // Draw 3d model with texture
rl::endMode3D();
break;
}
default: break;
}
break;
}
default: {
// std::io::printfn("ERROR: unhandled render command."); TODO: maybe make a workout for this
}
}
}
}

693
bindings/c3/source/clay.c3 Normal file
View File

@ -0,0 +1,693 @@
module clay;
import clay::carray;
// =======================
// ===== USER MACROS =====
// =======================
macro @clay(...; @body()) @builtin
{
clay::openElement();
$for (var $i = 0; $i < $vacount; $i++)
$vaexpr[$i]; // If you get an error here consider the @body[...]() macros
$endfor
clay::elementPostConfiguration();
@body();
clay::closeElement();
}
macro text(String text, TextElementConfig *config) { clay::openTextElement({text.len, text}, config); }
macro @bodyIf(#condition, #ifRes) { if (#condition) { #ifRes; } }
macro @bodyIfElse(#condition, #ifRes, #elseRes) { if (#condition) { #ifRes; } else { #elseRes; } }
macro rectangle(RectangleElementConfig config) { clay::attachElementConfig({ .rectangleElementConfig = clay::storeRectangleElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_RECTANGLE ); }
macro layout(LayoutConfig config) { clay::attachLayoutConfig( clay::storeLayoutConfig(config) ); }
macro scroll(ScrollElementConfig config) { clay::attachElementConfig({ .scrollElementConfig = clay::storeScrollElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER ); }
macro floating(FloatingElementConfig config) { clay::attachElementConfig({ .floatingElementConfig = clay::storeFloatingElementConfig(config) }, clay::ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER ); }
macro borderRadiusUni(uint width, ClayColor color, float cornerRadius = 0) { clay::attachElementConfig({ .borderElementConfig = clay::storeBorderElementConfig({ .left = { width, color }, .right = { width, color }, .top = { width, color }, .bottom = { width, color }, .cornerRadius = {cornerRadius, cornerRadius, cornerRadius, cornerRadius}})}, clay::ELEMENT_CONFIG_TYPE_BORDER_CONTAINER); }
macro id(String idString) { clay::attachId(clay::hashString({idString.len, idString}, 0, 0)); }
macro idi(String idString, uint seed) { clay::attachId(clay::hashString({idString.len, idString}, 0, seed)); }
macro idLocal(String idString, uint offset) { clay::attachId(clay::hashString({idString.len, idString}, offset, 0)); }
macro idiLocal(String idString, uint offset, uint seed) { clay::attachId(clay::hashString({idString.len, idString}, offset, seed)); }
macro TextElementConfig* textConfig(TextElementConfig config) { return clay::storeTextElementConfig(config); }
macro SizingAxis sizingFit(float min = 0) { return { .size.minMax = {min, float.max}, .type = SizingType.FIT }; }
macro SizingAxis sizingGrow(float max = 0) { return { .size.minMax = {0, max}, .type = SizingType.GROW }; }
macro SizingAxis sizingFixed(float pixels) { return { .size.minMax = {pixels, pixels}, .type = SizingType.FIXED }; }
macro SizingAxis sizingPercent(float percent) { return { .size.percent = percent, .type = SizingType.PERCENT }; }
macro Padding paddingUni(ushort uniform) { return {uniform, uniform, uniform, uniform}; }
macro Padding padding(ushort horizontal, ushort vertical) { return {horizontal, horizontal, vertical, vertical}; }
macro CornerRadius cornerRadiusUni(float uniform) { return {uniform, uniform, uniform, uniform}; }
macro SizingAxis sizingFitCT(float $min = 0, float $max = float.max) { return { .size.minMax = {$min, $max}, .type = SizingType.FIT }; }
macro SizingAxis sizingFixedCT(float $pixels) { return { .size.minMax = {$pixels, $pixels}, .type = SizingType.FIXED }; }
macro SizingAxis sizingPercentCT(float $percent) { return { .size.percent = $percent, .type = SizingType.PERCENT }; }
macro Padding paddingCT(ushort $a, ushort $b, ushort $c, ushort $d) { return { $a, $b, $c, $d }; }
macro CornerRadius @cornerRadiusUniCT(float #uniform) { return {#uniform, #uniform, #uniform, #uniform}; }
// TODO: figure out why this isn't working
// macro ClayColor @colorHex(#hex) { return {(float)(((#hex >> 16) & 0xFF) / 255.0), (float)(((#hex >> 8) & 0xFF) / 255.0), (float)(((#hex) & 0xFF) / 255.0), 255}; }
struct ClayString
{
int length;
char *chars;
}
def ClayStringArray = carray::Array(<ClayString>) @private;
struct Arena
{
uint128 nextAllocation;
uint128 capacity;
char *memory;
}
struct Dimensions
{
float width, height;
}
struct ClayVector2
{
float x, y;
}
struct ClayColor
{
float r, g, b, a;
}
struct ElementId
{
uint id;
uint offset;
uint baseId;
ClayString stringId;
}
struct CornerRadius
{
float topLeft;
float topRight;
float bottomLeft;
float bottomRight;
}
distinct ElementConfigType = char;
const ElementConfigType ELEMENT_CONFIG_TYPE_NONE = 0;
const ElementConfigType ELEMENT_CONFIG_TYPE_RECTANGLE = 1;
const ElementConfigType ELEMENT_CONFIG_TYPE_BORDER_CONTAINER = 2;
const ElementConfigType ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER = 4;
const ElementConfigType ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER = 8;
const ElementConfigType ELEMENT_CONFIG_TYPE_IMAGE = 16;
const ElementConfigType ELEMENT_CONFIG_TYPE_TEXT = 32;
const ElementConfigType ELEMENT_CONFIG_TYPE_CUSTOM = 64;
enum LayoutDirection : char @export
{
LEFT_TO_RIGHT,
TOP_TO_BOTTOM,
}
enum AlignX : char @export
{
LEFT,
RIGHT,
CENTER,
}
enum AlignY : char @export
{
TOP,
BOTTOM,
CENTER,
}
enum SizingType : char @export
{
FIT,
GROW,
PERCENT,
FIXED,
}
struct ChildAlignment
{
AlignX x;
AlignY y;
}
struct SizingMinMax
{
float min;
float max;
}
struct SizingAxis
{
union size
{
SizingMinMax minMax;
float percent;
}
SizingType type;
}
struct Sizing
{
SizingAxis width;
SizingAxis height;
}
struct Padding
{
ushort left;
ushort right;
ushort top;
ushort bottom;
}
struct LayoutConfig
{
Sizing sizing;
Padding padding;
ushort childGap;
ChildAlignment childAlignment;
LayoutDirection layoutDirection;
}
struct RectangleElementConfig
{
ClayColor color;
CornerRadius cornerRadius;
// No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_RECTANGLE)
// TODO: C3 doesn't support conditional compilation outside of functions/methods/macros
}
enum WrapMode
{
WORDS,
NEWLINES,
NONE,
}
struct TextElementConfig
{
ClayColor textColor;
ushort fontId;
ushort fontSize;
ushort letterSpacing;
ushort lineHeight;
WrapMode wrapMode;
// No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_TEXT)
// TODO: C3 doesn't support conditional compilation outside of functions/methods/macros
}
struct ImageElementConfig
{
void *imageData;
Dimensions sourceDimensions;
// No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_IMAGE)
// TODO: C3 doesn't support conditional compilation outside of functions/methods/macros
}
enum AttachPoint : char
{
LEFT_TOP,
LEFT_CENTER,
LEFT_BOTTOM,
CENTER_TOP,
CENTER_CENTER,
CENTER_BOTTOM,
RIGHT_TOP,
RIGHT_CENTER,
RIGHT_BOTTOM,
}
struct FloatingAttachPoints
{
AttachPoint element;
AttachPoint parent;
}
enum PointerCaptureMode
{
CAPTURE,
// MODE_PASSTHROUGH, // not fully implemented in C as of bindings dev-time
PARENT,
}
struct FloatingElementConfig
{
ClayVector2 offset;
Dimensions expand;
ushort zIndex;
uint parentId;
FloatingAttachPoints attachment;
PointerCaptureMode pointerCaptureMode;
}
struct CustomElementConfig
{
void *customData;
// No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_FLOATING)
// TODO: C3 doesn't support conditional compilation outside of functions/methods/macros
}
struct ScrollElementConfig
{
bool horizontal;
bool vertical;
}
// Border
struct Border
{
uint width;
ClayColor color;
}
struct BorderElementConfig
{
Border left;
Border right;
Border top;
Border bottom;
Border betweenChildren;
CornerRadius cornerRadius;
// No C-like struct extension macros (eg. CLAY_EXTEND_CONFIG_BORDER)
// TODO: C3 doesn't support conditional compilation outside of functions/methods/macros
}
union ElementConfigUnion
{
RectangleElementConfig *rectangleElementConfig;
TextElementConfig *textElementConfig;
ImageElementConfig *imageElementConfig;
FloatingElementConfig *floatingElementConfig;
CustomElementConfig *customElementConfig;
ScrollElementConfig *scrollElementConfig;
BorderElementConfig *borderElementConfig;
}
struct ElementConfig
{
ElementConfigType type;
ElementConfigUnion config;
}
struct ClayBoundingBox
{
float x, y, width, height;
}
enum RenderCommandType : char @export
{
NONE,
RECTANGLE,
BORDER,
TEXT,
IMAGE,
SCISSOR_START,
SCISSOR_END,
CUSTOM,
}
struct RenderCommand
{
ClayBoundingBox boundingBox;
ElementConfigUnion config;
ClayString text;
uint id;
RenderCommandType commandType;
}
def RenderCommandArray = carray::Array(<RenderCommand>);
struct ScrollContainerData
{
ClayVector2 *scrollPosition;
Dimensions scrollContainerDimensions;
Dimensions contentDimensions;
ScrollElementConfig config;
bool found;
}
struct ElementData
{
ClayBoundingBox boundingBox;
bool found;
}
enum PointerState
{
PRESSED_THIS_FRAME,
PRESSED,
RELEASED_THIS_FRAME,
RELEASED,
}
struct PointerData
{
ClayVector2 position;
PointerState state;
}
def OnHoverEvent = fn void(ElementId elementId, PointerData pointerData, iptr userData);
def MeasureTextFunc = fn Dimensions(ClayString *text, TextElementConfig *config);
def QueryScrollOffsetFunc = fn ClayVector2(uint elementId);
enum ErrorType
{
TEXT_MEASUREMENT_FUNCTION_NOT_PROVIDED,
ARENA_CAPACITY_EXCEEDED,
ELEMENTS_CAPACITY_EXCEEDED,
TEXT_MEASUREMENT_CAPACITY_EXCEEDED,
DUPLICATE_ID,
FLOATING_CONTAINER_PARENT_NOT_FOUND,
INTERNAL_ERROR,
}
struct ErrorData
{
ErrorType errorType;
ClayString errorText;
uint128 userData;
}
def ErrorHandleFunc = fn void(ErrorData errorText);
struct ErrorHandler
{
ErrorHandleFunc errorHandler;
uint128 userData;
}
struct BooleanWarnings
{
bool maxElementsExceeded;
bool maxRenderCommandsExceeded;
bool maxTextMeasureCacheExceeded;
}
struct Warning
{
ClayString baseMessage;
ClayString dynamicMessage;
}
def WarningArray = carray::Array(<Warning>);
struct LayoutElementChildren
{
int *elements;
ushort length;
}
struct LayoutElement
{
union childrenOrTextContent {
LayoutElementChildren children;
TextElementData *textElementData;
}
Dimensions dimensions;
Dimensions minDimensions;
LayoutConfig *layoutConfig;
ElementConfigArraySlice elementConfigs;
uint configsEnabled;
uint id;
}
struct WrappedTextLine
{
Dimensions dimensions;
ClayString line;
}
struct WrappedTextLineArraySlice
{
int length;
WrappedTextLine *internalArray;
}
struct TextElementData
{
ClayString text;
Dimensions preferredDimensions;
int elementIndex;
WrappedTextLineArraySlice wrappedLines;
}
struct ElementConfigArraySlice
{
int length;
ElementConfig *internalArray;
}
def DebugElementData = bool[<2>];
struct LayoutElementHashMapItem
{
ClayBoundingBox boundingBox;
ElementId elementId;
LayoutElement* layoutElement;
OnHoverEvent onHoverFunction;
int128 hoverFunctionUserData;
int nextIndex;
uint128 generation;
DebugElementData *debugData;
}
struct LayoutElementTreeRoot
{
int layoutElementIndex;
uint parentId; // This can be zero in the case of the root layout tree
uint clipElementId; // This can be zero if there is no clip element
int zIndex;
ClayVector2 pointerOffset; // Only used when scroll containers are managed externally
}
struct LayoutElementTreeNode
{
LayoutElement *layoutElement;
ClayVector2 position;
ClayVector2 nextChildOffset;
}
struct MeasuredWord
{
int startOffset;
int length;
float width;
int next;
}
struct MeasureTextCacheItem
{
Dimensions unwrappedDimensions;
int measuredWordsStartIndex;
bool containsNewlines;
// Hash map data
uint id;
int nextIndex;
uint generation;
}
struct ScrollContainerDataInternal
{
LayoutElement *layoutElement;
ClayBoundingBox boundingBox;
Dimensions contentSize;
ClayVector2 scrollOrigin;
ClayVector2 pointerOrigin;
ClayVector2 scrollMomentum;
ClayVector2 scrollPosition;
ClayVector2 previousDelta;
float momentumTime;
uint elementId;
bool openThisFrame;
bool pointerScrollActive;
}
def LayoutElementArray = carray::Array(<LayoutElement>) ;
def IntArray = carray::Array(<int>) ;
def TextElementDataArray = carray::Array(<TextElementData>) ;
def RectangleElementConfigArray = carray::Array(<RectangleElementConfig>) ;
def TextElementConfigArray = carray::Array(<TextElementConfig>) ;
def ImageElementConfigArray = carray::Array(<ImageElementConfig>) ;
def FloatingElementConfigArray = carray::Array(<FloatingElementConfig>) ;
def ScrollElementConfigArray = carray::Array(<ScrollElementConfig>) ;
def CustomElementConfigArray = carray::Array(<CustomElementConfig>) ;
def BorderElementConfigArray = carray::Array(<BorderElementConfig>) ;
def LayoutElementPointerArray = carray::Array(<LayoutElement*>) ;
def LayoutConfigArray = carray::Array(<LayoutConfig>) ;
def ElementConfigArray = carray::Array(<ElementConfig>) ;
def WrappedTextLineArray = carray::Array(<WrappedTextLine>) ;
def LayoutElementTreeNodeArray = carray::Array(<LayoutElementTreeNode>) ;
def LayoutElementTreeRootArray = carray::Array(<LayoutElementTreeRoot>) ;
def LayoutElementHashMapItemArray = carray::Array(<LayoutElementHashMapItem>) ;
def MeasureTextCacheItemArray = carray::Array(<MeasureTextCacheItem>) ;
def MeasuredWordArray = carray::Array(<MeasuredWord>) ;
def ElementIdArray = carray::Array(<ElementId>) ;
def ScrollContainerDataInternalArray = carray::Array(<ScrollContainerDataInternal>) ;
def BoolArray = carray::Array(<bool>) ;
def CharArray = carray::Array(<char>) ;
def DebugElementDataArray = carray::Array(<DebugElementData>) ;
// TODO: find out why alignment is off, accessing from Context doesn't really work
struct Context
{
int maxElementCount;
int maxMeasureTextCacheWordCount;
bool warningsEnabled;
ErrorHandler errorHandler;
BooleanWarnings booleanWarnings;
WarningArray warnings;
PointerData pointerInfo;
Dimensions layoutDimensions;
ElementId dynamicElementIndexBaseHash;
uint dynamicElementIndex;
bool debugModeEnabled;
bool disableCulling;
bool externalScrollHandlingEnabled;
uint debugSelectedElementId;
uint generation;
uint128 arenaResetOffset;
Arena internalArena;
// Layout Elements / Render Commands
LayoutElementArray layoutElements;
RenderCommandArray renderCommands;
IntArray openLayoutElementStack;
IntArray layoutElementChildren;
IntArray layoutElementChildrenBuffer;
TextElementDataArray textElementData;
LayoutElementPointerArray imageElementPointers;
IntArray reusableElementIndexBuffer;
IntArray layoutElementClipElementIds;
// Configs
LayoutConfigArray layoutConfigs;
ElementConfigArray elementConfigBuffer;
ElementConfigArray elementConfigs;
RectangleElementConfigArray rectangleElementConfigs;
TextElementConfigArray textElementConfigs;
ImageElementConfigArray imageElementConfigs;
FloatingElementConfigArray floatingElementConfigs;
ScrollElementConfigArray scrollElementConfigs;
CustomElementConfigArray customElementConfigs;
BorderElementConfigArray borderElementConfigs;
// Misc Data Structures
ClayStringArray layoutElementIdStrings;
WrappedTextLineArray wrappedTextLines;
LayoutElementTreeNodeArray layoutElementTreeNodeArray1;
LayoutElementTreeRootArray layoutElementTreeRoots;
LayoutElementHashMapItemArray layoutElementsHashMapInternal;
IntArray layoutElementsHashMap;
MeasureTextCacheItemArray measureTextHashMapInternal;
IntArray measureTextHashMapInternalFreeList;
IntArray measureTextHashMap;
MeasuredWordArray measuredWords;
IntArray measuredWordsFreeList;
IntArray openClipElementStack;
ElementIdArray pointerOverIds;
ScrollContainerDataInternalArray scrollContainerDatas;
BoolArray treeNodeVisited;
CharArray dynamicStringData;
DebugElementDataArray debugElementData;
}
// =====================
// ===== FUNCTIONS =====
// =====================
// ===== Public Clay API C3 Functions (String replacement) =====
fn ElementId getElementIdWithIndex(String idString, uint index) @inline
{ return __getElementIdWithIndex({idString.len, idString}, (uint)index); }
fn ElementId getElementId(String idString) @inline
{ return __getElementId({idString.len, idString}); }
// ===== Public Clay API Functions ===== // TODO: worry about exports/ static library creation later
extern fn uint minMemorySize() @extern("Clay_MinMemorySize") ; // @export;
extern fn Arena createArena(uint capacity, void* offset) @extern("Clay_CreateArenaWithCapacityAndMemory") ; // @export;
extern fn void setPointerState(ClayVector2 position, bool pointerDown) @extern("Clay_SetPointerState") ; // @export;
extern fn Context* initialize(Arena arena, Dimensions layoutDimensions, ErrorHandler errorHandler) @extern("Clay_Initialize") ; // @export;
extern fn Context* getCurrentContext() @extern("Clay_GetCurrentContext") ; // @export;
extern fn void setCurrentContext(Context* context) @extern("Clay_SetCurrentContext") ; // @export;
extern fn void updateScrollContainer(bool enableDragScrolling, ClayVector2 scrollDelta, float deltaTime) @extern("Clay_UpdateScrollContainers") ; // @export;
extern fn void setLayoutDimensions (Dimensions dimensions) @extern("Clay_SetLayoutDimensions") ; // @export;
extern fn ElementData getElementData(ElementId id) @extern("Clay_GetElementData") ; // @export;
extern fn bool hovered() @extern("Clay_Hovered") ; // @export;
extern fn void onHover(OnHoverEvent onHover, iptr userData) @extern("Clay_OnHover") ; // @export;
extern fn bool pointerOver(ElementId elementId) @extern("Clay_PointerOver") ; // @export;
extern fn ScrollContainerData getScrollContainerData(ElementId id) @extern("Clay_GetScrollContainerData") ; // @export;
extern fn void setMeasureTextFunction(MeasureTextFunc measureText) @extern("Clay_SetMeasureTextFunction") ; // @export;
extern fn void setQueryScrollOffsetFunction(QueryScrollOffsetFunc queryScrollOffset) @extern("Clay_SetQueryScrollOffsetFunction") ; // @export;
extern fn RenderCommand * RenderCommandArray.get(RenderCommandArray* array, int index) @extern("Clay_RenderCommandArray_Get") ; // @export;
extern fn void setDebugModeEnabled(bool enabled) @extern("Clay_SetDebugModeEnabled") ; // @export;
extern fn bool isDebugModeEnabled() @extern("Clay_IsDebugModeEnabled") ; // @export;
extern fn void setCullingEnabled(bool enabled) @extern("Clay_SetCullingEnabled") ; // @export;
extern fn int getMaxMeasuredTextCachedWordCount() @extern("Clay_GetMaxElementCount") ; // @export;
extern fn void setMaxElementCount(int maxElementCount) @extern("Clay_SetMaxElementCount") ; // @export;
extern fn int getMaxElementCount() @extern("Clay_GetMaxMeasureTextCacheWordCount") ; // @export;
extern fn void setMaxMeasureTextCacheWordCount(int maxMeasureTextCacheWordCount) @extern("Clay_SetMaxMeasureTextCacheWordCount") ; // @export;
extern fn void resetMeasureTextCache() @extern("Clay_ResetMeasureTextCache") ; // @export;
extern fn void beginLayout() @extern("Clay_BeginLayout") ; // @export;
extern fn RenderCommandArray endLayout() @extern("Clay_EndLayout") ; // @export;
// ===== (NEW) Internal Clay API Functions (String replacement) =====
extern fn ElementId __getElementIdWithIndex(ClayString idString, uint index) @extern("Clay_GetElementIdWithIndex") ; // @export;
extern fn ElementId __getElementId(ClayString idString) @extern("Clay_GetElementId") ; // @export;
// ===== Internal Clay API Functions =====
extern fn void openElement() @extern ("Clay__OpenElement") ; // @export;
extern fn void closeElement() @extern("Clay__CloseElement") ; // @export;
extern fn void openTextElement(ClayString text, TextElementConfig *textConfig) @extern("Clay__OpenTextElement") ; // @export;
extern fn void elementPostConfiguration() @extern("Clay__ElementPostConfiguration") ; // @export;
extern fn LayoutConfig * storeLayoutConfig(LayoutConfig config) @extern("Clay__StoreLayoutConfig") ; // @export;
extern fn void attachId(ElementId id) @extern("Clay__AttachId") ; // @export;
extern fn void attachLayoutConfig(LayoutConfig *config) @extern("Clay__AttachLayoutConfig") ; // @export;
extern fn void attachElementConfig(ElementConfigUnion config, ElementConfigType type) @extern("Clay__AttachElementConfig") ; // @export;
extern fn RectangleElementConfig * storeRectangleElementConfig(RectangleElementConfig config) @extern("Clay__StoreRectangleElementConfig") ; // @export;
extern fn TextElementConfig * storeTextElementConfig(TextElementConfig config) @extern("Clay__StoreTextElementConfig") ; // @export;
extern fn ImageElementConfig * storeImageElementConfig(ImageElementConfig config) @extern("Clay__StoreImageElementConfig") ; // @export;
extern fn FloatingElementConfig * storeFloatingElementConfig(FloatingElementConfig config) @extern("Clay__StoreFloatingElementConfig") ; // @export;
extern fn CustomElementConfig * storeCustomElementConfig(CustomElementConfig config) @extern("Clay__StoreCustomElementConfig") ; // @export;
extern fn ScrollElementConfig * storeScrollElementConfig(ScrollElementConfig config) @extern("Clay__StoreScrollElementConfig") ; // @export;
extern fn BorderElementConfig * storeBorderElementConfig(BorderElementConfig config) @extern("Clay__StoreBorderElementConfig") ; // @export;
extern fn ElementId hashString(ClayString key, uint offset, uint seed) @extern("Clay__HashString") ; // @export;
extern fn uint getParentElementId() @extern("Clay__GetParentElementId") ; // @export;
// ==========================================================================
// ===== An internal module for wrapping Struct Array's defined in Clay =====
// ==========================================================================
module clay::carray(<ElementType>);
struct Array {
int capacity;
int length;
ElementType *data;
}

View File

@ -317,7 +317,7 @@ ElementDeclaration :: struct {
userData: rawptr
}
ErrorType :: enum {
ErrorType :: enum EnumBackingType {
TextMeasurementFunctionNotProvided,
ArenaCapacityExceeded,
ElementsCapacityExceeded,

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;
}