clay/examples/raylib-transitions/main.c
2026-03-31 22:26:14 +11:00

368 lines
16 KiB
C

#define CLAY_IMPLEMENTATION
#include "../../clay.h"
#include "../../renderers/raylib/clay_renderer_raylib.c"
const uint32_t FONT_ID_BODY_24 = 0;
const uint32_t FONT_ID_BODY_16 = 1;
#define COLOR_ORANGE (Clay_Color) {225, 138, 50, 255}
#define COLOR_BLUE (Clay_Color) {111, 173, 162, 255}
Texture2D profilePicture;
#define RAYLIB_VECTOR2_TO_CLAY_VECTOR2(vector) (Clay_Vector2) { .x = vector.x, .y = vector.y }
Clay_String profileText = CLAY_STRING_CONST("Profile Page one two three four five six seven eight nine ten eleven twelve thirteen fourteen fifteen");
Clay_TextElementConfig headerTextConfig = { .fontId = 1, .letterSpacing = 5, .fontSize = 16, .textColor = {0,0,0,255} };
void HandleHeaderButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, intptr_t userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
// Do some click handling
}
}
Clay_ElementDeclaration HeaderButtonStyle(bool hovered) {
return (Clay_ElementDeclaration) {
.layout = {.padding = {16, 16, 8, 8}},
.backgroundColor = hovered ? COLOR_ORANGE : COLOR_BLUE,
};
}
// Examples of re-usable "Components"
void RenderHeaderButton(Clay_String text) {
CLAY_AUTO_ID(HeaderButtonStyle(Clay_Hovered())) {
CLAY_TEXT(text, CLAY_TEXT_CONFIG(headerTextConfig));
}
}
Clay_LayoutConfig dropdownTextItemLayout = { .padding = {8, 8, 4, 4} };
Clay_TextElementConfig dropdownTextElementConfig = { .fontSize = 24, .textColor = {255,255,255,255} };
void RenderDropdownTextItem(int index) {
CLAY_AUTO_ID({ .layout = dropdownTextItemLayout, .backgroundColor = {180, 180, 180, 255} }) {
CLAY_TEXT(CLAY_STRING("I'm a text field in a scroll container."), dropdownTextElementConfig);
}
}
typedef struct {
int id;
Clay_Color color;
const char* stringId;
} SortableBox;
int maxCount = 30;
int cellCount = 30;
char* charData;
SortableBox colors[100] = {};
bool blueColor = 0;
#define GG { CLAY_SIZING_GROW(), CLAY_SIZING_GROW() }
#define cWHITE { 255, 255, 255, 255 }
typedef struct {
int value;
} Test;
typedef struct {
void* memory;
uintptr_t offset;
} Arena;
Arena frameArena = {};
Clay_TransitionData EnterExitSlideUp(Clay_TransitionData initialState, Clay_TransitionProperty properties) {
Clay_TransitionData targetState = initialState;
if (properties & CLAY_TRANSITION_PROPERTY_Y) {
targetState.boundingBox.y += 20;
}
if (properties & CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR) {
targetState.overlayColor = (Clay_Color) { 255, 255, 255, 255 };
}
return targetState;
}
// Swaps two elements in an array
void swap(SortableBox *a, SortableBox *b) {
SortableBox temp = *a;
*a = *b;
*b = temp;
}
void shuffle(SortableBox *array, size_t n) {
if (n <= 1) return;
for (size_t i = n - 1; i > 0; i--) {
size_t j = rand() % (i + 1);
swap(&array[i], &array[j]);
}
}
void add(SortableBox *array, int32_t length, int32_t index, SortableBox toAdd) {
for (int i = length; i > index; --i) {
array[i] = array[i - 1];
}
array[index] = toAdd;
}
void HandleRandomiseButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
shuffle(colors, cellCount);
}
}
void HandlePinkButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
for (int i = 0; i < cellCount; i++) {
int index = colors[i].id;
colors[i] = (SortableBox) {
.id = index,
.color = { 255 - index, 255 - index * 4, 255 - index * 2, 255 },
.stringId = colors[i].stringId
};
}
}
}
void HandleNewButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
int32_t randomIndex = rand() % (cellCount + 1);
int32_t newId = maxCount;
snprintf(&charData[newId * 3], 3, "%02d", newId);
add(colors, maxCount, randomIndex, (SortableBox) {
.id = newId,
.color = { 255 - newId, 255 - newId * 4, 255 - newId * 2, 255 },
.stringId = &charData[newId * 3]
});
cellCount++;
maxCount++;
}
}
void HandleBlueButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
for (int i = 0; i < cellCount; i++) {
int index = colors[i].id;
colors[i] = (SortableBox) {
.id = index,
.color = { 255 - index * 4, 255 - index * 2, 255 - index, 255 },
.stringId = colors[i].stringId
};
}
}
}
void HandleCellButtonInteraction(Clay_ElementId elementId, Clay_PointerData pointerData, void *userData) {
if (pointerData.state == CLAY_POINTER_DATA_PRESSED_THIS_FRAME) {
for (int i = (uintptr_t)userData; i < cellCount; i++) {
colors[i] = colors[i + 1];
}
cellCount = CLAY__MAX(cellCount - 1, 0);
}
}
Clay_RenderCommandArray CreateLayout(void) {
frameArena.offset = 0;
Clay_BeginLayout();
CLAY(CLAY_ID("OuterContainer"), { .layout = { .layoutDirection = CLAY_TOP_TO_BOTTOM, .sizing = { .width = CLAY_SIZING_GROW(0), .height = CLAY_SIZING_GROW(0) }, .padding = { 16, 16, 16, 16 }, .childGap = 12 }, .backgroundColor = cWHITE }) {
CLAY_AUTO_ID({ .layout.sizing = { CLAY_SIZING_GROW(), CLAY_SIZING_FIXED(60) }, .layout.padding.left = 16, .layout.childGap = 16, .layout.childAlignment.y = CLAY_ALIGN_Y_CENTER, .cornerRadius = { 12, 12, 12, 12 }, .backgroundColor = {174, 143, 204, 255 } }) {
CLAY(CLAY_ID("ShuffleButton"), {
.backgroundColor = Clay_Hovered() ? (Clay_Color){ 154, 123, 184, 255 } : (Clay_Color){ },
.layout.padding = { 16, 16, 8, 8 },
.cornerRadius = CLAY_CORNER_RADIUS(6),
.border = { .color = cWHITE, .width = CLAY_BORDER_OUTSIDE(2) },
}) {
Clay_OnHover(HandleRandomiseButtonInteraction, 0);
CLAY_TEXT(CLAY_STRING("Randomise"), CLAY_TEXT_CONFIG({ .fontSize = 20, .textColor = cWHITE }));
}
CLAY(CLAY_ID("bluebutton"), {
.backgroundColor = Clay_Hovered() ? (Clay_Color){ 154, 123, 184, 255 } : (Clay_Color){ },
.layout.padding = { 16, 16, 8, 8 },
.cornerRadius = CLAY_CORNER_RADIUS(6),
.border = { .color = cWHITE, .width = CLAY_BORDER_OUTSIDE(2) },
}) {
Clay_OnHover(HandleBlueButtonInteraction, 0);
CLAY_TEXT(CLAY_STRING("Blue"), CLAY_TEXT_CONFIG({ .fontSize = 20, .textColor = cWHITE }));
}
CLAY(CLAY_ID("PinkButton"), {
.backgroundColor = Clay_Hovered() ? (Clay_Color){ 154, 123, 184, 255 } : (Clay_Color){ },
.layout.padding = { 16, 16, 8, 8 },
.cornerRadius = CLAY_CORNER_RADIUS(6),
.border = { .color = cWHITE, .width = CLAY_BORDER_OUTSIDE(2) },
}) {
Clay_OnHover(HandlePinkButtonInteraction, 0);
CLAY_TEXT(CLAY_STRING("Pink"), CLAY_TEXT_CONFIG({ .fontSize = 20, .textColor = cWHITE }));
}
CLAY(CLAY_ID("AddButton"), {
.backgroundColor = Clay_Hovered() ? (Clay_Color){ 154, 123, 184, 255 } : (Clay_Color){ },
.layout.padding = { 16, 16, 8, 8 },
.cornerRadius = CLAY_CORNER_RADIUS(6),
.border = { .color = cWHITE, .width = CLAY_BORDER_OUTSIDE(2) },
}) {
Clay_OnHover(HandleNewButtonInteraction, 0);
CLAY_TEXT(CLAY_STRING("Add Box"), CLAY_TEXT_CONFIG({ .fontSize = 20, .textColor = cWHITE }));
}
}
for (int i = 0; i < 5; i++) {
Clay_ElementId rowId = CLAY_IDI("row", i);
Clay_ElementData rowData = Clay_GetElementData(rowId);
CLAY(rowId, { .layout.childGap = 12, .layout.sizing = GG }) {
for (int j = 0; j < 6; j++) {
int index = i * 6 + j;
if (index >= cellCount) {
break;
}
Clay_Color boxColor = colors[index].color;
Clay_Color darker = { boxColor.r * 0.9, boxColor.g * 0.9, boxColor.b * 0.9, 255 };
CLAY(CLAY_IDI("box", colors[index].id), {
.layout.sizing = {CLAY_SIZING_GROW(), CLAY_SIZING_GROW() },
.layout.childAlignment = { CLAY_ALIGN_X_CENTER, CLAY_ALIGN_Y_CENTER },
.backgroundColor = boxColor,
.overlayColor = Clay_Hovered() ? (Clay_Color) { 140, 140, 140, 80 } : (Clay_Color) { 255, 255, 255, 0 },
.cornerRadius = {12, 12, 12, 12},
.border = { darker, CLAY_BORDER_OUTSIDE(3) },
.transition = {
.handler = Clay_EaseOut,
.duration = Clay_Hovered() && Clay_GetPointerState().state != CLAY_POINTER_DATA_PRESSED_THIS_FRAME ? 0.f : 0.5f,
.properties = CLAY_TRANSITION_PROPERTY_WIDTH | CLAY_TRANSITION_PROPERTY_POSITION | CLAY_TRANSITION_PROPERTY_OVERLAY_COLOR | CLAY_TRANSITION_PROPERTY_BACKGROUND_COLOR,
.enter = { .setInitialState = EnterExitSlideUp },
.exit = { .setFinalState = EnterExitSlideUp },
}
}) {
Clay_OnHover(HandleCellButtonInteraction, (void*)(uint64_t)index);
CLAY_TEXT(((Clay_String) { .length = 2, .chars = colors[index].stringId, .isStaticallyAllocated = true }), CLAY_TEXT_CONFIG({
.fontSize = 32,
.textColor = colors[index].id > 29 ? (Clay_Color) {255, 255, 255, 255} : (Clay_Color) {154, 123, 184, 255 }
}));
}
}
}
}
}
return Clay_EndLayout(GetFrameTime());
}
typedef struct
{
Clay_Vector2 clickOrigin;
Clay_Vector2 positionOrigin;
bool mouseDown;
} ScrollbarData;
ScrollbarData scrollbarData = {0};
bool debugEnabled = false;
void UpdateDrawFrame(Font* fonts)
{
Vector2 mouseWheelDelta = GetMouseWheelMoveV();
float mouseWheelX = mouseWheelDelta.x;
float mouseWheelY = mouseWheelDelta.y;
if (IsKeyPressed(KEY_D)) {
debugEnabled = !debugEnabled;
Clay_SetDebugModeEnabled(debugEnabled);
}
//----------------------------------------------------------------------------------
// Handle scroll containers
Clay_Vector2 mousePosition = RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition());
Clay_SetPointerState(mousePosition, IsMouseButtonDown(0) && !scrollbarData.mouseDown);
Clay_SetLayoutDimensions((Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() });
if (!IsMouseButtonDown(0)) {
scrollbarData.mouseDown = false;
}
if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(Clay__HashString(CLAY_STRING("ScrollBar"), 0))) {
Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0));
scrollbarData.clickOrigin = mousePosition;
scrollbarData.positionOrigin = *scrollContainerData.scrollPosition;
scrollbarData.mouseDown = true;
} else if (scrollbarData.mouseDown) {
Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(Clay__HashString(CLAY_STRING("MainContent"), 0));
if (scrollContainerData.contentDimensions.height > 0) {
Clay_Vector2 ratio = (Clay_Vector2) {
scrollContainerData.contentDimensions.width / scrollContainerData.scrollContainerDimensions.width,
scrollContainerData.contentDimensions.height / scrollContainerData.scrollContainerDimensions.height,
};
if (scrollContainerData.config.vertical) {
scrollContainerData.scrollPosition->y = scrollbarData.positionOrigin.y + (scrollbarData.clickOrigin.y - mousePosition.y) * ratio.y;
}
if (scrollContainerData.config.horizontal) {
scrollContainerData.scrollPosition->x = scrollbarData.positionOrigin.x + (scrollbarData.clickOrigin.x - mousePosition.x) * ratio.x;
}
}
}
Clay_UpdateScrollContainers(true, (Clay_Vector2) {mouseWheelX, mouseWheelY}, GetFrameTime());
// Generate the auto layout for rendering
double currentTime = GetTime();
Clay_RenderCommandArray renderCommands = CreateLayout();
printf("layout time: %f microseconds\n", (GetTime() - currentTime) * 1000 * 1000);
// RENDERING ---------------------------------
// currentTime = GetTime();
BeginDrawing();
ClearBackground(BLACK);
Clay_Raylib_Render(renderCommands, fonts);
EndDrawing();
// printf("render time: %f ms\n", (GetTime() - currentTime) * 1000);
//----------------------------------------------------------------------------------
}
bool reinitializeClay = false;
void HandleClayErrors(Clay_ErrorData errorData) {
printf("%s\n", errorData.errorText.chars);
if (errorData.errorType == CLAY_ERROR_TYPE_ELEMENTS_CAPACITY_EXCEEDED) {
reinitializeClay = true;
Clay_SetMaxElementCount(Clay_GetMaxElementCount() * 2);
} else if (errorData.errorType == CLAY_ERROR_TYPE_TEXT_MEASUREMENT_CAPACITY_EXCEEDED) {
reinitializeClay = true;
Clay_SetMaxMeasureTextCacheWordCount(Clay_GetMaxMeasureTextCacheWordCount() * 2);
}
}
int main(void) {
uint64_t totalMemorySize = Clay_MinMemorySize();
Clay_Arena clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
Clay_Context *context = Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors, 0 });
Clay_Raylib_Initialize(1024, 768, "Clay - Raylib Renderer Example", FLAG_VSYNC_HINT | FLAG_WINDOW_RESIZABLE | FLAG_MSAA_4X_HINT);
profilePicture = LoadTexture("resources/profile-picture.png");
Font fonts[2];
fonts[FONT_ID_BODY_24] = LoadFontEx("resources/Roboto-Regular.ttf", 48, 0, 400);
SetTextureFilter(fonts[FONT_ID_BODY_24].texture, TEXTURE_FILTER_BILINEAR);
fonts[FONT_ID_BODY_16] = LoadFontEx("resources/Roboto-Regular.ttf", 32, 0, 400);
SetTextureFilter(fonts[FONT_ID_BODY_16].texture, TEXTURE_FILTER_BILINEAR);
Clay_SetMeasureTextFunction(Raylib_MeasureText, fonts);
frameArena = (Arena) {.memory = malloc(1024) };
charData = (char*)malloc(100 * 3);
for (int i = 0; i < cellCount; i++) {
snprintf(&charData[i * 3], 3, "%02d", i);
colors[i] = (SortableBox) {
.id = i,
.color = { 255 - i, 255 - i * 4, 255 - i * 2, 255 },
.stringId = &charData[i * 3],
};
}
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
if (reinitializeClay) {
Clay_SetMaxElementCount(8192);
totalMemorySize = Clay_MinMemorySize();
clayMemory = Clay_CreateArenaWithCapacityAndMemory(totalMemorySize, malloc(totalMemorySize));
Clay_Initialize(clayMemory, (Clay_Dimensions) { (float)GetScreenWidth(), (float)GetScreenHeight() }, (Clay_ErrorHandler) { HandleClayErrors, 0 });
reinitializeClay = false;
}
UpdateDrawFrame(fonts);
}
Clay_Raylib_Close();
return 0;
}