From e0f7a23f1a0f081efb0ac11418a040765c9bebc6 Mon Sep 17 00:00:00 2001 From: Nic Barker Date: Mon, 26 Aug 2024 19:05:43 +1200 Subject: [PATCH] Expose scroll container data and implement scrollbar example (#1) --- README.md | 70 ++++++++-- clay.h | 120 +++++++++++------- .../raylib-sidebar-scrolling-container/main.c | 45 ++++++- 3 files changed, 178 insertions(+), 57 deletions(-) diff --git a/README.md b/README.md index 9ff5e5f..2c6ceed 100644 --- a/README.md +++ b/README.md @@ -457,6 +457,12 @@ Ends declaration of element macros and calculates the results of the currrent la Returns `true` if the pointer position previously set with `Clay_SetPointerPosition` is inside the bounding box of the layout element with the provided `id`. Note: this is based on the element's position from the **last** frame. If frame-accurate pointer overlap detection is required, perhaps in the case of significant change in UI layout between frames, you can simply run your layout code twice that frame. The second call to `Clay_PointerOver` will be frame-accurate. +### Clay_GetScrollContainerData + +`Clay_ScrollContainerData Clay_GetScrollContainerData(uint32_t id)` + +Returns [Clay_ScrollContainerData](#clay_scrollcontainerdata) for the scroll container matching the provided ID. This function allows imperative manipulation of scroll position, allowing you to build things such as scroll bars, buttons that "jump" to somewhere in a scroll container, etc. + ## Element Macros ### CLAY_CONTAINER @@ -1434,20 +1440,20 @@ Returned by [Clay_EndLayout](#clay_endlayout), this array contains the [Clay_Ren **Fields** -`.capacity` - `uint32_t` +**`.capacity`** - `uint32_t` Represents the total capacity of the allocated memory in `.internalArray`. --- -`.length` - `uint32_t` +**`.length`** - `uint32_t` Represents the total number of `Clay_RenderCommand` elements stored consecutively at the address `.internalArray`. --- -`.internalArray` - `Clay_RenderCommand` +**`.internalArray`** - `Clay_RenderCommand` An array of [Clay_RenderCommand](#clay_rendercommand)s representing the calculated layout. If there was at least one render command, this array will contain elements from `.internalArray[0]` to `.internalArray[.length - 1]`. @@ -1466,9 +1472,7 @@ typedef struct **Fields** ---- - -`.commandType` - `Clay_RenderCommandType` +**`.commandType`** - `Clay_RenderCommandType` An enum indicating how this render command should be handled. Possible values include: @@ -1483,7 +1487,7 @@ An enum indicating how this render command should be handled. Possible values in --- -`.boundingBox` - `Clay_Rectangle` +**`.boundingBox`** - `Clay_Rectangle` ```C typedef struct { @@ -1495,7 +1499,7 @@ A rectangle representing the bounding box of this render command, with `.x` and --- -`.config` - `Clay_ElementConfigUnion` +**`.config`** - `Clay_ElementConfigUnion` A C union containing various pointers to config data, with the type dependent on `.commandType`. Possible values include: @@ -1509,15 +1513,61 @@ A C union containing various pointers to config data, with the type dependent on --- -`.text` - `Clay_String` +**`.text`** - `Clay_String` Only used if `.commandType == CLAY_RENDER_COMMAND_TYPE_TEXT`. A `Clay_String` containing a string slice (char *chars, int length) representing text to be rendered. **Note: This string is not guaranteed to be null terminated.** Clay saves significant performance overhead by using slices when wrapping text instead of having to clone new null terminated strings. If your renderer does not support **ptr, length** style strings (e.g. Raylib), you will need to clone this to a new C string before rendering. --- -`.id` - `uint32_t` +**`.id`** - `uint32_t` The id that was originally used with the element macro that created this render command. See [CLAY_ID](#clay_id) for details. +### Clay_ScrollContainerData +```C +typedef struct +{ + Clay_Vector2 *scrollPosition; + Clay_Dimensions scrollContainerDimensions; + Clay_Dimensions contentDimensions; + Clay_ScrollContainerElementConfig config; + bool found; +} Clay_ScrollContainerData; +``` +**Fields** + +**`.scrollPosition`** - `Clay_Vector2 *` + +A pointer to the internal scroll position of this scroll container. Mutating it will result in elements inside the scroll container shifting up / down (`.y`) or left / right (`.x`). + +--- + +**`.scrollContainerDimensions`** - `Clay_Dimensions` + +```C +typedef struct { + float width, height; +} Clay_Dimensions; +``` + +Dimensions representing the outer width and height of the scroll container itself. + +--- + +**`.contentDimensions`** - `Clay_Dimensions` + +```C +typedef struct { + float width, height; +} Clay_Dimensions; +``` + +Dimensions representing the inner width and height of the content _inside_ the scroll container. Scrolling is only possible when the `contentDimensions` are larger in at least one dimension than the `scrollContainerDimensions`. + +--- + +**`.config`** - `Clay_ScrollContainerElementConfig` + +The [Clay_ScrollContainerElementConfig](#clay_scroll_config) for the matching scroll container element. \ No newline at end of file diff --git a/clay.h b/clay.h index 8436831..377e8fb 100644 --- a/clay.h +++ b/clay.h @@ -785,51 +785,41 @@ typedef struct uint32_t elementId; bool openThisFrame; bool pointerScrollActive; -} Clay__ScrollContainerData; +} Clay__ScrollContainerDataInternal; -Clay__ScrollContainerData CLAY__SCROLL_CONTAINER_DEFAULT = (Clay__ScrollContainerData) {}; +Clay__ScrollContainerDataInternal CLAY__SCROLL_CONTAINER_DEFAULT = (Clay__ScrollContainerDataInternal) {}; -// __GENERATED__ template array_define TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray +// __GENERATED__ template define,array_add,array_get TYPE=Clay__ScrollContainerDataInternal NAME=Clay__ScrollContainerDataInternalArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT #pragma region generated typedef struct { uint32_t capacity; uint32_t length; - Clay__ScrollContainerData *internalArray; -} Clay__ScrollContainerDataArray; + Clay__ScrollContainerDataInternal *internalArray; +} Clay__ScrollContainerDataInternalArray; -Clay__ScrollContainerDataArray Clay__ScrollContainerDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { - return (Clay__ScrollContainerDataArray){.capacity = capacity, .length = 0, .internalArray = (Clay__ScrollContainerData *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__ScrollContainerData), CLAY__ALIGNMENT(Clay__ScrollContainerData), arena)}; +Clay__ScrollContainerDataInternalArray Clay__ScrollContainerDataInternalArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { + return (Clay__ScrollContainerDataInternalArray){.capacity = capacity, .length = 0, .internalArray = (Clay__ScrollContainerDataInternal *)Clay__Array_Allocate_Arena(capacity, sizeof(Clay__ScrollContainerDataInternal), CLAY__ALIGNMENT(Clay__ScrollContainerDataInternal), arena)}; } -#pragma endregion -// __GENERATED__ template - -// __GENERATED__ template array_add TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT -#pragma region generated -Clay__ScrollContainerData *Clay__ScrollContainerDataArray_Add(Clay__ScrollContainerDataArray *array, Clay__ScrollContainerData item) { +Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Add(Clay__ScrollContainerDataInternalArray *array, Clay__ScrollContainerDataInternal item) { if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { array->internalArray[array->length++] = item; return &array->internalArray[array->length - 1]; } return &CLAY__SCROLL_CONTAINER_DEFAULT; } -#pragma endregion -// __GENERATED__ template - -// __GENERATED__ template array_get TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=&CLAY__SCROLL_CONTAINER_DEFAULT -#pragma region generated -Clay__ScrollContainerData *Clay__ScrollContainerDataArray_Get(Clay__ScrollContainerDataArray *array, int index) { +Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Get(Clay__ScrollContainerDataInternalArray *array, int index) { return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__SCROLL_CONTAINER_DEFAULT; } #pragma endregion // __GENERATED__ template -// __GENERATED__ template array_remove_swapback TYPE=Clay__ScrollContainerData NAME=Clay__ScrollContainerDataArray DEFAULT_VALUE=CLAY__SCROLL_CONTAINER_DEFAULT +// __GENERATED__ template array_remove_swapback TYPE=Clay__ScrollContainerDataInternal NAME=Clay__ScrollContainerDataInternalArray DEFAULT_VALUE=CLAY__SCROLL_CONTAINER_DEFAULT #pragma region generated -Clay__ScrollContainerData Clay__ScrollContainerDataArray_RemoveSwapback(Clay__ScrollContainerDataArray *array, int index) { +Clay__ScrollContainerDataInternal Clay__ScrollContainerDataInternalArray_RemoveSwapback(Clay__ScrollContainerDataInternalArray *array, int index) { if (Clay__Array_RangeCheck(index, array->length)) { array->length--; - Clay__ScrollContainerData removed = array->internalArray[index]; + Clay__ScrollContainerDataInternal removed = array->internalArray[index]; array->internalArray[index] = array->internalArray[array->length]; return removed; } @@ -1068,7 +1058,7 @@ Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal; Clay__int32_tArray Clay__measureTextHashMap; Clay__int32_tArray Clay__openClipElementStack; Clay__int32_tArray Clay__pointerOverIds; -Clay__ScrollContainerDataArray Clay__scrollContainerOffsets; +Clay__ScrollContainerDataInternalArray Clay__scrollContainerDatas; Clay__BoolArray Clay__treeNodeVisited; #if CLAY_WASM @@ -1272,9 +1262,9 @@ void Clay__OpenCustomElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_ Clay_LayoutElement *Clay__OpenScrollElement(uint32_t id, Clay_LayoutConfig *layoutConfig, Clay_ScrollContainerElementConfig *scrollConfig) { Clay_LayoutElement *scrollElement = Clay__OpenElement(id, CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER, layoutConfig, (Clay_ElementConfigUnion){ .scrollElementConfig = scrollConfig }); Clay__int32_tArray_Add(&Clay__openClipElementStack, (int)scrollElement->id); - Clay__ScrollContainerData *scrollOffset = CLAY__NULL; - for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) { - Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); + Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL; + for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); if (id == mapping->elementId) { scrollOffset = mapping; scrollOffset->layoutElement = scrollElement; @@ -1282,7 +1272,7 @@ Clay_LayoutElement *Clay__OpenScrollElement(uint32_t id, Clay_LayoutConfig *layo } } if (!scrollOffset) { - Clay__ScrollContainerDataArray_Add(&Clay__scrollContainerOffsets, (Clay__ScrollContainerData){.elementId = id, .layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .openThisFrame = true}); + Clay__ScrollContainerDataInternalArray_Add(&Clay__scrollContainerDatas, (Clay__ScrollContainerDataInternal){.elementId = id, .layoutElement = scrollElement, .scrollOrigin = {-1,-1}, .openThisFrame = true}); } return scrollElement; } @@ -1427,7 +1417,7 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { } void Clay__InitializePersistentMemory(Clay_Arena *arena) { - Clay__scrollContainerOffsets = Clay__ScrollContainerDataArray_Allocate_Arena(10, arena); + Clay__scrollContainerDatas = Clay__ScrollContainerDataInternalArray_Allocate_Arena(10, arena); Clay__layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__layoutElementsHashMap = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__measureTextHashMapInternal = Clay__MeasureTextCacheItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); @@ -1504,6 +1494,21 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); Clay_LayoutElement *rootElement = root->layoutElement; Clay__LayoutElementPointerArray_Add(&bfsBuffer, root->layoutElement); + + // Size floating containers to their parents + if (rootElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_FLOATING_CONTAINER) { + Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(rootElement->elementConfig.floatingElementConfig->parentId); + if (parentItem) { + Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement; + if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) { + rootElement->dimensions.width = parentLayoutElement->dimensions.width - (float)parentLayoutElement->layoutConfig->padding.x * 2; + } + if (rootElement->layoutConfig->sizing.height.type == CLAY__SIZING_TYPE_GROW) { + rootElement->dimensions.height = parentLayoutElement->dimensions.height - (float)parentLayoutElement->layoutConfig->padding.x * 2; + } + } + } + rootElement->dimensions.width = CLAY__MIN(CLAY__MAX(rootElement->dimensions.width, rootElement->layoutConfig->sizing.width.sizeMinMax.min), rootElement->layoutConfig->sizing.width.sizeMinMax.max); rootElement->dimensions.height = CLAY__MIN(CLAY__MAX(rootElement->dimensions.height, rootElement->layoutConfig->sizing.height.sizeMinMax.min), rootElement->layoutConfig->sizing.height.sizeMinMax.max); @@ -1609,13 +1614,6 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { } void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { - // layoutElementsHashMap has non-linear access pattern so just resetting .length won't zero out the data. - // Need to zero it all out here - for (int i = 0; i < Clay__layoutElementsHashMap.capacity; ++i) { - Clay__layoutElementsHashMap.internalArray[i] = -1; - } - Clay__layoutElementsHashMapInternal.length = 0; - // Calculate sizing along the X axis Clay__SizeContainersAlongAxis(true); @@ -1759,6 +1757,13 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { // Calculate sizing along the Y axis Clay__SizeContainersAlongAxis(false); + // layoutElementsHashMap has non-linear access pattern so just resetting .length won't zero out the data. + // Need to zero it all out here + for (int i = 0; i < Clay__layoutElementsHashMap.capacity; ++i) { + Clay__layoutElementsHashMap.internalArray[i] = -1; + } + Clay__layoutElementsHashMapInternal.length = 0; + // Calculate final positions and generate render commands Clay__renderCommands.length = 0; dfsBuffer.length = 0; @@ -1855,7 +1860,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { currentElementBoundingBox.height += expand.height * 2; } - Clay__ScrollContainerData *scrollContainerData = CLAY__NULL; + Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL; // Apply scroll offsets to container if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) { Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { @@ -1865,8 +1870,8 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) { }); // This linear scan could theoretically be slow under very strange conditions, but I can't imagine a real UI with more than a few 10's of scroll containers - for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) { - Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); + for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); if (mapping->layoutElement == currentElement) { scrollContainerData = mapping; mapping->boundingBox = currentElementBoundingBox; @@ -2144,18 +2149,18 @@ CLAY_WASM_EXPORT("Clay_UpdateScrollContainers") void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, float deltaTime) { // Don't apply scroll events to ancestors of the inner element int32_t highestPriorityElementIndex = -1; - Clay__ScrollContainerData *highestPriorityScrollData = CLAY__NULL; - for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) { - Clay__ScrollContainerData *scrollData = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); + Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL; + for (int i = 0; i < Clay__scrollContainerDatas.length; i++) { + Clay__ScrollContainerDataInternal *scrollData = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); if (!scrollData->openThisFrame) { - Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i); + Clay__ScrollContainerDataInternalArray_RemoveSwapback(&Clay__scrollContainerDatas, i); continue; } scrollData->openThisFrame = false; Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId); // Element isn't rendered this frame but scroll offset has been retained if (!hashMapItem) { - Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i); + Clay__ScrollContainerDataInternalArray_RemoveSwapback(&Clay__scrollContainerDatas, i); continue; } @@ -2279,6 +2284,35 @@ bool Clay_PointerOver(uint32_t id) { // TODO return priority for separating mult return false; } +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; + Clay_Dimensions scrollContainerDimensions; + Clay_Dimensions contentDimensions; + Clay_ScrollContainerElementConfig config; + // Indicates whether an actual scroll container matched the provided ID or if the default struct was returned. + bool found; +} Clay_ScrollContainerData; + +CLAY_WASM_EXPORT("Clay_GetScrollContainerData") +Clay_ScrollContainerData Clay_GetScrollContainerData(uint32_t id) { + for (int i = 0; i < Clay__scrollContainerDatas.length; ++i) { + Clay__ScrollContainerDataInternal *scrollContainerData = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i); + if (scrollContainerData->elementId == id) { + return (Clay_ScrollContainerData) { + .scrollPosition = &scrollContainerData->scrollPosition, + .scrollContainerDimensions = (Clay_Dimensions) { scrollContainerData->boundingBox.width, scrollContainerData->boundingBox.height }, + .contentDimensions = scrollContainerData->contentSize, + .config = *scrollContainerData->layoutElement->elementConfig.scrollElementConfig, + .found = true + }; + } + } + return (Clay_ScrollContainerData){}; +} + #endif //CLAY_IMPLEMENTATION /* diff --git a/examples/raylib-sidebar-scrolling-container/main.c b/examples/raylib-sidebar-scrolling-container/main.c index 9ac1349..d87d0bd 100644 --- a/examples/raylib-sidebar-scrolling-container/main.c +++ b/examples/raylib-sidebar-scrolling-container/main.c @@ -93,7 +93,7 @@ Clay_RenderCommandArray CreateLayout() { }); }); }); -// + CLAY_FLOATING_CONTAINER(CLAY_ID("Blob4Floating"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.zIndex = 1, .parentId = CLAY_ID("SidebarBlob4")), { CLAY_SCROLL_CONTAINER(CLAY_ID("ScrollContainer"), CLAY_LAYOUT(.sizing = { .height = CLAY_SIZING_FIXED(200) }, .childGap = 2), CLAY_SCROLL_CONFIG(.vertical = true), { CLAY_FLOATING_CONTAINER(CLAY_ID("FloatingContainer"), CLAY_LAYOUT(), CLAY_FLOATING_CONFIG(.zIndex = 1), { @@ -108,11 +108,22 @@ Clay_RenderCommandArray CreateLayout() { }); }); }); + Clay_ScrollContainerData scrollData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + CLAY_FLOATING_CONTAINER(CLAY_ID("ScrollBar"), &CLAY_LAYOUT_DEFAULT, CLAY_FLOATING_CONFIG(.offset = { .y = -(scrollData.scrollPosition->y / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height }, .zIndex = 1, .parentId = CLAY_ID("MainContent"), .attachment = {.element = CLAY_ATTACH_POINT_RIGHT_TOP, .parent = CLAY_ATTACH_POINT_RIGHT_TOP}), { + CLAY_RECTANGLE(CLAY_ID("ScrollBarButton"), CLAY_LAYOUT(.sizing = {CLAY_SIZING_FIXED(12), CLAY_SIZING_FIXED((scrollData.scrollContainerDimensions.height / scrollData.contentDimensions.height) * scrollData.scrollContainerDimensions.height)}), CLAY_RECTANGLE_CONFIG(.cornerRadius = 6, .color = Clay_PointerOver(CLAY_ID("ScrollBar")) ? (Clay_Color){100, 100, 140, 150} : (Clay_Color){120, 120, 160, 150}), {}); + }); }); return Clay_EndLayout(GetScreenWidth(), GetScreenHeight()); } -int display_size_changed = 0; +typedef struct +{ + Clay_Vector2 clickOrigin; + Clay_Vector2 positionOrigin; + bool mouseDown; +} ScrollbarData; + +ScrollbarData scrollbarData = (ScrollbarData) {}; void UpdateDrawFrame(void) { @@ -122,8 +133,34 @@ void UpdateDrawFrame(void) mouseWheelY = mouseWheelDelta.y; //---------------------------------------------------------------------------------- // Handle scroll containers - Clay_SetPointerPosition(RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition())); - Clay_UpdateScrollContainers(IsMouseButtonDown(0), (Clay_Vector2) {mouseWheelX, mouseWheelY}, GetFrameTime()); + Clay_Vector2 mousePosition = RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition()); + Clay_SetPointerPosition(mousePosition); + if (!IsMouseButtonDown(0)) { + scrollbarData.mouseDown = false; + } + + if (IsMouseButtonDown(0) && !scrollbarData.mouseDown && Clay_PointerOver(CLAY_ID("ScrollBar"))) { + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + scrollbarData.clickOrigin = mousePosition; + scrollbarData.positionOrigin = *scrollContainerData.scrollPosition; + scrollbarData.mouseDown = true; + } else if (scrollbarData.mouseDown) { + Clay_ScrollContainerData scrollContainerData = Clay_GetScrollContainerData(CLAY_ID("MainContent")); + 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(false, (Clay_Vector2) {mouseWheelX, mouseWheelY}, GetFrameTime()); // Generate the auto layout for rendering double currentTime = GetTime(); Clay_RenderCommandArray renderCommands = CreateLayout();