Expose scroll container data and implement scrollbar example (#1)

This commit is contained in:
Nic Barker 2024-08-26 19:05:43 +12:00 committed by GitHub
parent 09fc980434
commit e0f7a23f1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 178 additions and 57 deletions

View File

@ -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. 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 ## Element Macros
### CLAY_CONTAINER ### CLAY_CONTAINER
@ -1434,20 +1440,20 @@ Returned by [Clay_EndLayout](#clay_endlayout), this array contains the [Clay_Ren
**Fields** **Fields**
`.capacity` - `uint32_t` **`.capacity`** - `uint32_t`
Represents the total capacity of the allocated memory in `.internalArray`. 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`. 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]`. 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** **Fields**
--- **`.commandType`** - `Clay_RenderCommandType`
`.commandType` - `Clay_RenderCommandType`
An enum indicating how this render command should be handled. Possible values include: 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 ```C
typedef struct { 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: 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. 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. 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.

120
clay.h
View File

@ -785,51 +785,41 @@ typedef struct
uint32_t elementId; uint32_t elementId;
bool openThisFrame; bool openThisFrame;
bool pointerScrollActive; 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 #pragma region generated
typedef struct typedef struct
{ {
uint32_t capacity; uint32_t capacity;
uint32_t length; uint32_t length;
Clay__ScrollContainerData *internalArray; Clay__ScrollContainerDataInternal *internalArray;
} Clay__ScrollContainerDataArray; } Clay__ScrollContainerDataInternalArray;
Clay__ScrollContainerDataArray Clay__ScrollContainerDataArray_Allocate_Arena(uint32_t capacity, Clay_Arena *arena) { Clay__ScrollContainerDataInternalArray Clay__ScrollContainerDataInternalArray_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)}; 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 Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Add(Clay__ScrollContainerDataInternalArray *array, Clay__ScrollContainerDataInternal item) {
// __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) {
if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) { if (Clay__Array_IncrementCapacityCheck(array->length, array->capacity)) {
array->internalArray[array->length++] = item; array->internalArray[array->length++] = item;
return &array->internalArray[array->length - 1]; return &array->internalArray[array->length - 1];
} }
return &CLAY__SCROLL_CONTAINER_DEFAULT; return &CLAY__SCROLL_CONTAINER_DEFAULT;
} }
#pragma endregion Clay__ScrollContainerDataInternal *Clay__ScrollContainerDataInternalArray_Get(Clay__ScrollContainerDataInternalArray *array, int index) {
// __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) {
return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__SCROLL_CONTAINER_DEFAULT; return Clay__Array_RangeCheck(index, array->length) ? &array->internalArray[index] : &CLAY__SCROLL_CONTAINER_DEFAULT;
} }
#pragma endregion #pragma endregion
// __GENERATED__ template // __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 #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)) { if (Clay__Array_RangeCheck(index, array->length)) {
array->length--; array->length--;
Clay__ScrollContainerData removed = array->internalArray[index]; Clay__ScrollContainerDataInternal removed = array->internalArray[index];
array->internalArray[index] = array->internalArray[array->length]; array->internalArray[index] = array->internalArray[array->length];
return removed; return removed;
} }
@ -1068,7 +1058,7 @@ Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal;
Clay__int32_tArray Clay__measureTextHashMap; Clay__int32_tArray Clay__measureTextHashMap;
Clay__int32_tArray Clay__openClipElementStack; Clay__int32_tArray Clay__openClipElementStack;
Clay__int32_tArray Clay__pointerOverIds; Clay__int32_tArray Clay__pointerOverIds;
Clay__ScrollContainerDataArray Clay__scrollContainerOffsets; Clay__ScrollContainerDataInternalArray Clay__scrollContainerDatas;
Clay__BoolArray Clay__treeNodeVisited; Clay__BoolArray Clay__treeNodeVisited;
#if CLAY_WASM #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 *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_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__int32_tArray_Add(&Clay__openClipElementStack, (int)scrollElement->id);
Clay__ScrollContainerData *scrollOffset = CLAY__NULL; Clay__ScrollContainerDataInternal *scrollOffset = CLAY__NULL;
for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) { for (int i = 0; i < Clay__scrollContainerDatas.length; i++) {
Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i);
if (id == mapping->elementId) { if (id == mapping->elementId) {
scrollOffset = mapping; scrollOffset = mapping;
scrollOffset->layoutElement = scrollElement; scrollOffset->layoutElement = scrollElement;
@ -1282,7 +1272,7 @@ Clay_LayoutElement *Clay__OpenScrollElement(uint32_t id, Clay_LayoutConfig *layo
} }
} }
if (!scrollOffset) { 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; return scrollElement;
} }
@ -1427,7 +1417,7 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) {
} }
void Clay__InitializePersistentMemory(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__layoutElementsHashMapInternal = Clay__LayoutElementHashMapItemArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena);
Clay__layoutElementsHashMap = Clay__int32_tArray_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); 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__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex);
Clay_LayoutElement *rootElement = root->layoutElement; Clay_LayoutElement *rootElement = root->layoutElement;
Clay__LayoutElementPointerArray_Add(&bfsBuffer, 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.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); 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) { 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 // Calculate sizing along the X axis
Clay__SizeContainersAlongAxis(true); Clay__SizeContainersAlongAxis(true);
@ -1759,6 +1757,13 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) {
// Calculate sizing along the Y axis // Calculate sizing along the Y axis
Clay__SizeContainersAlongAxis(false); 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 // Calculate final positions and generate render commands
Clay__renderCommands.length = 0; Clay__renderCommands.length = 0;
dfsBuffer.length = 0; dfsBuffer.length = 0;
@ -1855,7 +1860,7 @@ void Clay__CalculateFinalLayout(int screenWidth, int screenHeight) {
currentElementBoundingBox.height += expand.height * 2; currentElementBoundingBox.height += expand.height * 2;
} }
Clay__ScrollContainerData *scrollContainerData = CLAY__NULL; Clay__ScrollContainerDataInternal *scrollContainerData = CLAY__NULL;
// Apply scroll offsets to container // Apply scroll offsets to container
if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) { if (currentElement->elementType == CLAY__LAYOUT_ELEMENT_TYPE_SCROLL_CONTAINER) {
Clay_RenderCommandArray_Add(&Clay__renderCommands, (Clay_RenderCommand) { 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 // 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++) { for (int i = 0; i < Clay__scrollContainerDatas.length; i++) {
Clay__ScrollContainerData *mapping = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); Clay__ScrollContainerDataInternal *mapping = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i);
if (mapping->layoutElement == currentElement) { if (mapping->layoutElement == currentElement) {
scrollContainerData = mapping; scrollContainerData = mapping;
mapping->boundingBox = currentElementBoundingBox; mapping->boundingBox = currentElementBoundingBox;
@ -2144,18 +2149,18 @@ CLAY_WASM_EXPORT("Clay_UpdateScrollContainers")
void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, float deltaTime) { void Clay_UpdateScrollContainers(bool isPointerActive, Clay_Vector2 scrollDelta, float deltaTime) {
// Don't apply scroll events to ancestors of the inner element // Don't apply scroll events to ancestors of the inner element
int32_t highestPriorityElementIndex = -1; int32_t highestPriorityElementIndex = -1;
Clay__ScrollContainerData *highestPriorityScrollData = CLAY__NULL; Clay__ScrollContainerDataInternal *highestPriorityScrollData = CLAY__NULL;
for (int i = 0; i < Clay__scrollContainerOffsets.length; i++) { for (int i = 0; i < Clay__scrollContainerDatas.length; i++) {
Clay__ScrollContainerData *scrollData = Clay__ScrollContainerDataArray_Get(&Clay__scrollContainerOffsets, i); Clay__ScrollContainerDataInternal *scrollData = Clay__ScrollContainerDataInternalArray_Get(&Clay__scrollContainerDatas, i);
if (!scrollData->openThisFrame) { if (!scrollData->openThisFrame) {
Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i); Clay__ScrollContainerDataInternalArray_RemoveSwapback(&Clay__scrollContainerDatas, i);
continue; continue;
} }
scrollData->openThisFrame = false; scrollData->openThisFrame = false;
Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId); Clay_LayoutElementHashMapItem *hashMapItem = Clay__GetHashMapItem(scrollData->elementId);
// Element isn't rendered this frame but scroll offset has been retained // Element isn't rendered this frame but scroll offset has been retained
if (!hashMapItem) { if (!hashMapItem) {
Clay__ScrollContainerDataArray_RemoveSwapback(&Clay__scrollContainerOffsets, i); Clay__ScrollContainerDataInternalArray_RemoveSwapback(&Clay__scrollContainerDatas, i);
continue; continue;
} }
@ -2279,6 +2284,35 @@ bool Clay_PointerOver(uint32_t id) { // TODO return priority for separating mult
return false; 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 #endif //CLAY_IMPLEMENTATION
/* /*

View File

@ -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_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_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), { 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()); 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) void UpdateDrawFrame(void)
{ {
@ -122,8 +133,34 @@ void UpdateDrawFrame(void)
mouseWheelY = mouseWheelDelta.y; mouseWheelY = mouseWheelDelta.y;
//---------------------------------------------------------------------------------- //----------------------------------------------------------------------------------
// Handle scroll containers // Handle scroll containers
Clay_SetPointerPosition(RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition())); Clay_Vector2 mousePosition = RAYLIB_VECTOR2_TO_CLAY_VECTOR2(GetMousePosition());
Clay_UpdateScrollContainers(IsMouseButtonDown(0), (Clay_Vector2) {mouseWheelX, mouseWheelY}, GetFrameTime()); 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 // Generate the auto layout for rendering
double currentTime = GetTime(); double currentTime = GetTime();
Clay_RenderCommandArray renderCommands = CreateLayout(); Clay_RenderCommandArray renderCommands = CreateLayout();