diff --git a/clay.h b/clay.h index c627d194..4fbdf911 100644 --- a/clay.h +++ b/clay.h @@ -60,7 +60,7 @@ #define CLAY_SIZING_GROW(...) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = CLAY__INIT(Clay_SizingMinMax) {__VA_ARGS__}, .type = CLAY__SIZING_TYPE_GROW } -#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = { fixedSize, fixedSize }, .type = CLAY__SIZING_TYPE_GROW } +#define CLAY_SIZING_FIXED(fixedSize) CLAY__INIT(Clay_SizingAxis) { .sizeMinMax = { fixedSize, fixedSize }, .type = CLAY__SIZING_TYPE_FIXED } #define CLAY_SIZING_PERCENT(percentOfParent) CLAY__INIT(Clay_SizingAxis) { .sizePercent = (percentOfParent), .type = CLAY__SIZING_TYPE_PERCENT } @@ -194,6 +194,7 @@ typedef enum CLAY_PACKED_ENUM { CLAY__SIZING_TYPE_FIT, CLAY__SIZING_TYPE_GROW, CLAY__SIZING_TYPE_PERCENT, + CLAY__SIZING_TYPE_FIXED, } Clay__SizingType; typedef struct { @@ -1318,8 +1319,7 @@ Clay__int32_tArray Clay__layoutElementChildren; Clay__int32_tArray Clay__layoutElementChildrenBuffer; Clay__TextElementDataArray Clay__textElementData; Clay__LayoutElementPointerArray Clay__imageElementPointers; -Clay__LayoutElementPointerArray Clay__layoutElementReusableBuffer; -Clay__LayoutElementPointerArray Clay__layoutElementReusableBuffer2; +Clay__int32_tArray Clay__reusableElementIndexBuffer; // Configs Clay__LayoutConfigArray Clay__layoutConfigs; Clay__ElementConfigArray Clay__elementConfigBuffer; @@ -1341,7 +1341,6 @@ Clay__MeasureTextCacheItemArray Clay__measureTextHashMapInternal; Clay__int32_tArray Clay__measureTextHashMap; Clay__int32_tArray Clay__openClipElementStack; Clay__ElementIdArray Clay__pointerOverIds; -Clay__int32_tArray Clay__reusableElementIndexBuffer; Clay__ScrollContainerDataInternalArray Clay__scrollContainerDatas; Clay__BoolArray Clay__treeNodeVisited; Clay__CharArray Clay__dynamicStringData; @@ -1758,8 +1757,6 @@ void Clay__InitializeEphemeralMemory(Clay_Arena *arena) { Clay__openLayoutElementStack = Clay__int32_tArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__textElementData = Clay__TextElementDataArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__imageElementPointers = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); - Clay__layoutElementReusableBuffer = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); // TODO convert this to indexes instead of pointers - Clay__layoutElementReusableBuffer2 = Clay__LayoutElementPointerArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); // TODO convert this to indexes instead of pointers Clay__renderCommands = Clay_RenderCommandArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__treeNodeVisited = Clay__BoolArray_Allocate_Arena(CLAY_MAX_ELEMENT_COUNT, arena); Clay__treeNodeVisited.length = Clay__treeNodeVisited.capacity; // This array is accessed directly rather than behaving as a list @@ -1788,69 +1785,68 @@ typedef enum CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER, } Clay__SizeDistributionType; -// Because of the max and min sizing properties, we can't predict ahead of time how (or if) all the excess width -// will actually be distributed. So we keep looping until either all the excess width is distributed or -// we have exhausted all our containers that can change size along this axis -float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__LayoutElementPointerArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) { - Clay__LayoutElementPointerArray backBuffer = Clay__layoutElementReusableBuffer2; - backBuffer.length = 0; +float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__int32_tArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) { + Clay__int32_tArray remainingElements = Clay__openClipElementStack; + remainingElements.length = 0; + + for (int i = 0; i < resizableContainerBuffer.length; ++i) { + Clay__int32_tArray_Add(&remainingElements, Clay__int32_tArray_Get(&resizableContainerBuffer, i)); + } - Clay__LayoutElementPointerArray remainingElements = resizableContainerBuffer; - float totalDistributedSize; while (sizeToDistribute != 0 && remainingElements.length > 0) { - totalDistributedSize = 0; + float dividedSize = sizeToDistribute / (float)remainingElements.length; for (int childOffset = 0; childOffset < remainingElements.length; childOffset++) { - Clay_LayoutElement *childElement = Clay__LayoutElementPointerArray_Get(&remainingElements, childOffset); + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&remainingElements, childOffset)); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; + bool canDistribute = true; if ((sizeToDistribute < 0 && *childSize == childSizing.sizeMinMax.min) || (sizeToDistribute > 0 && *childSize == childSizing.sizeMinMax.max)) { - continue; + canDistribute = false; } - - if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) { - continue; // Currently, we don't support squishing aspect ratio images on their Y axis as it would break ratio + // Currently, we don't support squishing aspect ratio images on their Y axis as it would break ratio + else if (!xAxis && Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_IMAGE)) { + canDistribute = false; } - - switch (distributionType) { - case CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER: break; - case CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER: if (childSizing.type != CLAY__SIZING_TYPE_GROW) { continue; } break; - case CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER: { - if (Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { - Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; - if ((xAxis && !scrollConfig->horizontal) || (!xAxis && !scrollConfig->vertical)) { - continue; + else { + switch (distributionType) { + case CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER: break; + case CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER: if (childSizing.type != CLAY__SIZING_TYPE_GROW) canDistribute = false; break; + case CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER: { + if (Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { + Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if ((xAxis && !scrollConfig->horizontal) || (!xAxis && !scrollConfig->vertical)) { + continue; + } } } - break; } } - float dividedSize = sizeToDistribute / (float)(remainingElements.length - childOffset); + if (!canDistribute) { + Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); + childOffset--; + continue; + } + float oldChildSize = *childSize; *childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.sizeMinMax.max, *childSize + dividedSize), childSizing.sizeMinMax.min), childMinSize); float diff = *childSize - oldChildSize; - if (diff != 0) { - Clay__LayoutElementPointerArray_Add(&backBuffer, childElement); + if (diff > -0.01 && diff < 0.01) { + Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); + childOffset--; + continue; } sizeToDistribute -= diff; - totalDistributedSize += diff; } - if (totalDistributedSize == 0) { - break; - } - // Flip the buffers - Clay__LayoutElementPointerArray temp = remainingElements; - remainingElements = backBuffer; - backBuffer = temp; } - return sizeToDistribute; + return (sizeToDistribute > -0.01 && sizeToDistribute < 0.01) ? 0 : sizeToDistribute; } void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__int32_tArray bfsBuffer = Clay__layoutElementChildrenBuffer; - Clay__LayoutElementPointerArray resizableContainerBuffer = Clay__layoutElementReusableBuffer; + Clay__int32_tArray resizableContainerBuffer = Clay__openLayoutElementStack; for (int rootIndex = 0; rootIndex < Clay__layoutElementTreeRoots.length; ++rootIndex) { bfsBuffer.length = 0; Clay__LayoutElementTreeRoot *root = Clay__LayoutElementTreeRootArray_Get(&Clay__layoutElementTreeRoots, rootIndex); @@ -1859,8 +1855,8 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { // Size floating containers to their parents if (Clay__ElementHasConfig(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER)) { - Clay_FloatingElementConfig *floatingConfig = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig; - Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingConfig->parentId); + Clay_FloatingElementConfig *floatingElementConfig = Clay__FindElementConfigWithType(rootElement, CLAY__ELEMENT_CONFIG_TYPE_FLOATING_CONTAINER).floatingElementConfig; + Clay_LayoutElementHashMapItem *parentItem = Clay__GetHashMapItem(floatingElementConfig->parentId); if (parentItem) { Clay_LayoutElement *parentLayoutElement = parentItem->layoutElement; if (rootElement->layoutConfig->sizing.width.type == CLAY__SIZING_TYPE_GROW) { @@ -1879,9 +1875,10 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { int32_t parentIndex = Clay__int32_tArray_Get(&bfsBuffer, i); Clay_LayoutElement *parent = Clay_LayoutElementArray_Get(&Clay__layoutElements, parentIndex); Clay_LayoutConfig *parentStyleConfig = parent->layoutConfig; + int growContainerCount = 0; float parentSize = xAxis ? parent->dimensions.width : parent->dimensions.height; float parentPadding = (float)(xAxis ? parent->layoutConfig->padding.x : parent->layoutConfig->padding.y); - float innerContentSize = 0, totalPaddingAndChildGaps = parentPadding * 2; + float innerContentSize = 0, growContainerContentSize = 0, totalPaddingAndChildGaps = parentPadding * 2; bool sizingAlongAxis = (xAxis && parentStyleConfig->layoutDirection == CLAY_LEFT_TO_RIGHT) || (!xAxis && parentStyleConfig->layoutDirection == CLAY_TOP_TO_BOTTOM); resizableContainerBuffer.length = 0; float parentChildGap = parentStyleConfig->childGap; @@ -1896,12 +1893,16 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__int32_tArray_Add(&bfsBuffer, childElementIndex); } - if (childSizing.type != CLAY__SIZING_TYPE_PERCENT && (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS))) { - Clay__LayoutElementPointerArray_Add(&resizableContainerBuffer, childElement); + if (childSizing.type != CLAY__SIZING_TYPE_PERCENT && childSizing.type != CLAY__SIZING_TYPE_FIXED && (!Clay__ElementHasConfig(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT) || (Clay__FindElementConfigWithType(childElement, CLAY__ELEMENT_CONFIG_TYPE_TEXT).textElementConfig->wrapMode == CLAY_TEXT_WRAP_WORDS))) { + Clay__int32_tArray_Add(&resizableContainerBuffer, childElementIndex); } if (sizingAlongAxis) { innerContentSize += (childSizing.type == CLAY__SIZING_TYPE_PERCENT ? 0 : childSize); + if (childSizing.type == CLAY__SIZING_TYPE_GROW) { + growContainerContentSize += childSize; + growContainerCount++; + } if (childOffset > 0) { innerContentSize += parentChildGap; // For children after index 0, the childAxisOffset is the gap from the previous child totalPaddingAndChildGaps += parentChildGap; @@ -1935,10 +1936,10 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { float sizeToDistribute = parentSize - parentPadding * 2 - innerContentSize; // If the content is too large, compress the children as much as possible if (sizeToDistribute < 0) { - // If the parent can scroll in the axis direction in this direction, just leave the children alone + // If the parent can scroll in the axis direction in this direction, don't compress children, just leave them alone if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { - Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; - if (((xAxis && scrollConfig->horizontal) || (!xAxis && scrollConfig->vertical))) { + Clay_ScrollElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) { continue; } } @@ -1950,13 +1951,30 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER); } // The content is too small, allow SIZING_GROW containers to expand - } else { - Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER); + } else if (sizeToDistribute > 0 && growContainerCount > 0) { + float targetSize = (sizeToDistribute + growContainerContentSize) / growContainerCount; + for (int childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, childOffset)); + Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; + if (childSizing.type == CLAY__SIZING_TYPE_GROW) { + float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; + float *minSize = xAxis ? &childElement->minDimensions.width : &childElement->minDimensions.height; + if (targetSize < *minSize) { + growContainerContentSize -= *minSize; + Clay__int32_tArray_RemoveSwapback(&resizableContainerBuffer, childOffset); + growContainerCount--; + targetSize = (sizeToDistribute + growContainerContentSize) / growContainerCount; + childOffset = -1; + continue; + } + *childSize = targetSize; + } + } } // Sizing along the non layout axis ("off axis") } else { for (int childOffset = 0; childOffset < resizableContainerBuffer.length; childOffset++) { - Clay_LayoutElement *childElement = Clay__LayoutElementPointerArray_Get(&resizableContainerBuffer, childOffset); + Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, childOffset)); Clay_SizingAxis childSizing = xAxis ? childElement->layoutConfig->sizing.width : childElement->layoutConfig->sizing.height; float *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height; @@ -1964,11 +1982,11 @@ void Clay__SizeContainersAlongAxis(bool xAxis) { continue; // Currently we don't support resizing aspect ratio images on the Y axis because it would break the ratio } - float maxSize = parentSize - parentPadding * 2; // If we're laying out the children of a scroll panel, grow containers expand to the height of the inner content, not the outer container + float maxSize = parentSize - parentPadding * 2; if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) { - Clay_ScrollElementConfig *scrollConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; - if ((xAxis && scrollConfig->horizontal) || (!xAxis && scrollConfig->vertical)) { + Clay_ScrollElementConfig *scrollElementConfig = Clay__FindElementConfigWithType(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER).scrollElementConfig; + if (((xAxis && scrollElementConfig->horizontal) || (!xAxis && scrollElementConfig->vertical))) { maxSize = CLAY__MAX(maxSize, innerContentSize); } } diff --git a/examples/raylib-sidebar-scrolling-container/main.c b/examples/raylib-sidebar-scrolling-container/main.c index 7aceae0d..a8a36d78 100644 --- a/examples/raylib-sidebar-scrolling-container/main.c +++ b/examples/raylib-sidebar-scrolling-container/main.c @@ -83,7 +83,7 @@ Clay_RenderCommandArray CreateLayout() { } CLAY_TEXT(CLAY_STRING("Faucibus purus in massa tempor nec. Nec ullamcorper sit amet risus nullam eget felis eget nunc. Diam vulputate ut pharetra sit amet aliquam id diam. Lacus suspendisse faucibus interdum posuere lorem. A diam sollicitudin tempor id. Amet massa vitae tortor condimentum lacinia. Aliquet nibh praesent tristique magna."), - CLAY_TEXT_CONFIG(.fontSize = 24, .lineSpacing = 20, .textColor = {0,0,0,255})); + CLAY_TEXT_CONFIG(.fontSize = 24, .lineHeight = 20, .textColor = {0,0,0,255})); CLAY_TEXT(CLAY_STRING("Suspendisse in est ante in nibh. Amet venenatis urna cursus eget nunc scelerisque viverra. Elementum sagittis vitae et leo duis ut diam quam nulla. Enim nulla aliquet porttitor lacus. Pellentesque habitant morbi tristique senectus et. Facilisi nullam vehicula ipsum a arcu cursus vitae.\nSem fringilla ut morbi tincidunt. Euismod quis viverra nibh cras pulvinar mattis nunc sed. Velit sed ullamcorper morbi tincidunt ornare massa. Varius quam quisque id diam vel quam. Nulla pellentesque dignissim enim sit amet venenatis. Enim lobortis scelerisque fermentum dui faucibus in. Pretium viverra suspendisse potenti nullam ac tortor vitae. Lectus vestibulum mattis ullamcorper velit sed. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Habitant morbi tristique senectus et. Integer vitae justo eget magna fermentum iaculis eu. Semper quis lectus nulla at volutpat diam. Enim praesent elementum facilisis leo. Massa vitae tortor condimentum lacinia quis vel."), CLAY_TEXT_CONFIG(.fontSize = 24, .textColor = {0,0,0,255}));