This commit is contained in:
Jefferey S 2025-04-12 10:44:07 +01:00 committed by GitHub
commit 430bea7c40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 5665 additions and 0 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;
}