[Layout] Improve shrink size distribution (#173)
Some checks failed
CMake on multiple platforms / build (Release, cl, cl, windows-latest) (push) Waiting to run
CMake on multiple platforms / build (Release, clang, clang++, ubuntu-latest) (push) Failing after 15s
CMake on multiple platforms / build (Release, gcc, g++, ubuntu-latest) (push) Failing after 11s

This commit is contained in:
Nic Barker 2025-01-08 19:39:20 +13:00 committed by GitHub
parent fd45553aff
commit c12cefeaf4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 34 additions and 62 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

96
clay.h
View File

@ -2097,71 +2097,48 @@ void Clay__InitializePersistentMemory(Clay_Arena *arena) {
} }
CLAY__TYPEDEF(Clay__SizeDistributionType, enum { void Clay__CompressChildrenAlongAxis(bool xAxis, float totalSizeToDistribute, Clay__int32_tArray resizableContainerBuffer) {
CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER, Clay__int32_tArray largestContainers = Clay__openClipElementStack;
CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER, largestContainers.length = 0;
CLAY__SIZE_DISTRIBUTION_TYPE_GROW_CONTAINER,
});
float Clay__DistributeSizeAmongChildren(bool xAxis, float sizeToDistribute, Clay__int32_tArray resizableContainerBuffer, Clay__SizeDistributionType distributionType) { while (totalSizeToDistribute > 0) {
Clay__int32_tArray remainingElements = Clay__openClipElementStack; float largestSize = 0;
remainingElements.length = 0; float targetSize = 0;
for (int32_t i = 0; i < resizableContainerBuffer.length; ++i) {
Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&resizableContainerBuffer, i));
float childSize = xAxis ? childElement->dimensions.width : childElement->dimensions.height;
if (childSize == largestSize) {
Clay__int32_tArray_Add(&largestContainers, Clay__int32_tArray_Get(&resizableContainerBuffer, i));
} else if (childSize > largestSize) {
targetSize = largestSize;
largestSize = childSize;
largestContainers.length = 0;
Clay__int32_tArray_Add(&largestContainers, Clay__int32_tArray_Get(&resizableContainerBuffer, i));
}
else if (childSize > targetSize) {
targetSize = childSize;
}
}
for (int32_t i = 0; i < resizableContainerBuffer.length; ++i) { targetSize = CLAY__MAX(targetSize, (largestSize * largestContainers.length) - totalSizeToDistribute) / largestContainers.length;
Clay__int32_tArray_Add(&remainingElements, Clay__int32_tArray_Get(&resizableContainerBuffer, i));
}
while (sizeToDistribute != 0 && remainingElements.length > 0) { for (int32_t childOffset = 0; childOffset < largestContainers.length; childOffset++) {
float dividedSize = sizeToDistribute / (float)remainingElements.length; Clay_LayoutElement *childElement = Clay_LayoutElementArray_Get(&Clay__layoutElements, Clay__int32_tArray_Get(&largestContainers, childOffset));
for (int32_t childOffset = 0; childOffset < remainingElements.length; 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 *childSize = xAxis ? &childElement->dimensions.width : &childElement->dimensions.height;
float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height; float childMinSize = xAxis ? childElement->minDimensions.width : childElement->minDimensions.height;
bool canDistribute = true;
if ((sizeToDistribute < 0 && *childSize == childSizing.size.minMax.min) || (sizeToDistribute > 0 && *childSize == childSizing.size.minMax.max)) {
canDistribute = false;
}
// 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;
}
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)) {
Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset);
childOffset--;
continue;
}
}
}
}
}
if (!canDistribute) {
Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset);
childOffset--;
continue;
}
float oldChildSize = *childSize; float oldChildSize = *childSize;
*childSize = CLAY__MAX(CLAY__MAX(CLAY__MIN(childSizing.size.minMax.max, *childSize + dividedSize), childSizing.size.minMax.min), childMinSize); *childSize = CLAY__MAX(childMinSize, targetSize);
float diff = *childSize - oldChildSize; totalSizeToDistribute -= (oldChildSize - *childSize);
if (diff > -0.01 && diff < 0.01) { if (*childSize == childMinSize) {
Clay__int32_tArray_RemoveSwapback(&remainingElements, childOffset); Clay__int32_tArray_RemoveSwapback(&largestContainers, childOffset);
childOffset--; childOffset--;
continue;
} }
sizeToDistribute -= diff; }
if (largestContainers.length == 0) {
break;
} }
} }
return (sizeToDistribute > -0.01 && sizeToDistribute < 0.01) ? 0 : sizeToDistribute;
} }
void Clay__SizeContainersAlongAxis(bool xAxis) { void Clay__SizeContainersAlongAxis(bool xAxis) {
@ -2254,7 +2231,7 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
if (sizingAlongAxis) { if (sizingAlongAxis) {
float sizeToDistribute = parentSize - parentPadding * 2 - innerContentSize; float sizeToDistribute = parentSize - parentPadding * 2 - innerContentSize;
// If the content is too large, compress the children as much as possible // The content is too large, compress the children as much as possible
if (sizeToDistribute < 0) { if (sizeToDistribute < 0) {
// If the parent can scroll in the axis direction in this direction, don't compress children, just leave them 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)) { if (Clay__ElementHasConfig(parent, CLAY__ELEMENT_CONFIG_TYPE_SCROLL_CONTAINER)) {
@ -2264,12 +2241,7 @@ void Clay__SizeContainersAlongAxis(bool xAxis) {
} }
} }
// Scrolling containers preferentially compress before others // Scrolling containers preferentially compress before others
sizeToDistribute = Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_SCROLL_CONTAINER); Clay__CompressChildrenAlongAxis(xAxis, -sizeToDistribute, resizableContainerBuffer);
// If there is still height to make up, remove it from all containers that haven't hit their minimum size
if (sizeToDistribute < 0) {
Clay__DistributeSizeAmongChildren(xAxis, sizeToDistribute, resizableContainerBuffer, CLAY__SIZE_DISTRIBUTION_TYPE_RESIZEABLE_CONTAINER);
}
// The content is too small, allow SIZING_GROW containers to expand // The content is too small, allow SIZING_GROW containers to expand
} else if (sizeToDistribute > 0 && growContainerCount > 0) { } else if (sizeToDistribute > 0 && growContainerCount > 0) {
float targetSize = (sizeToDistribute + growContainerContentSize) / (float)growContainerCount; float targetSize = (sizeToDistribute + growContainerContentSize) / (float)growContainerCount;