/* Copyright (c) 2019 Alex Diener This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. Alex Diener alex@ludobloom.com */ #include "gamemath/Scalar.h" #include "uitoolkit/UIElement.h" #include "uitoolkit/UIToolkitAppearance.h" #include "uitoolkit/UIToolkitContext.h" #include "uitoolkit/UIToolkitDrawing.h" #include "utilities/IOUtilities.h" #include #include void UIToolkit_drawRectOutline(Rect4f rect, float inset, Color4f color, Rect4f textureBounds, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { call_virtual(drawQuad, drawingInterface, RECT4f(rect.xMin, rect.xMax, rect.yMin, rect.yMin + inset), textureBounds, color, vertexIO); call_virtual(drawQuad, drawingInterface, RECT4f(rect.xMin, rect.xMax, rect.yMax - inset, rect.yMax), textureBounds, color, vertexIO); call_virtual(drawQuad, drawingInterface, RECT4f(rect.xMin, rect.xMin + inset, rect.yMin + inset, rect.yMax - inset), textureBounds, color, vertexIO); call_virtual(drawQuad, drawingInterface, RECT4f(rect.xMax - inset, rect.xMax, rect.yMin + inset, rect.yMax - inset), textureBounds, color, vertexIO); } #define writeSliceVertices3x3() \ outSliceVertices[0].position.x = \ outSliceVertices[4].position.x = \ outSliceVertices[8].position.x = \ outSliceVertices[12].position.x = vertexBounds.xMin; \ outSliceVertices[0].texCoords.x = \ outSliceVertices[4].texCoords.x = \ outSliceVertices[8].texCoords.x = \ outSliceVertices[12].texCoords.x = textureBounds.xMin; \ \ outSliceVertices[1].position.x = \ outSliceVertices[5].position.x = \ outSliceVertices[9].position.x = \ outSliceVertices[13].position.x = vertexBounds.xMin + slices.leftColumn; \ outSliceVertices[1].texCoords.x = \ outSliceVertices[5].texCoords.x = \ outSliceVertices[9].texCoords.x = \ outSliceVertices[13].texCoords.x = textureBounds.xMin + slices.leftColumn * texCoordScaleX; \ \ outSliceVertices[2].position.x = \ outSliceVertices[6].position.x = \ outSliceVertices[10].position.x = \ outSliceVertices[14].position.x = vertexBounds.xMax - slices.rightColumn; \ outSliceVertices[2].texCoords.x = \ outSliceVertices[6].texCoords.x = \ outSliceVertices[10].texCoords.x = \ outSliceVertices[14].texCoords.x = textureBounds.xMax - slices.rightColumn * texCoordScaleX; \ \ outSliceVertices[3].position.x = \ outSliceVertices[7].position.x = \ outSliceVertices[11].position.x = \ outSliceVertices[15].position.x = vertexBounds.xMax; \ outSliceVertices[3].texCoords.x = \ outSliceVertices[7].texCoords.x = \ outSliceVertices[11].texCoords.x = \ outSliceVertices[15].texCoords.x = textureBounds.xMax; \ \ outSliceVertices[0].position.y = \ outSliceVertices[1].position.y = \ outSliceVertices[2].position.y = \ outSliceVertices[3].position.y = vertexBounds.yMin; \ outSliceVertices[0].texCoords.y = \ outSliceVertices[1].texCoords.y = \ outSliceVertices[2].texCoords.y = \ outSliceVertices[3].texCoords.y = textureBounds.yMin; \ \ outSliceVertices[4].position.y = \ outSliceVertices[5].position.y = \ outSliceVertices[6].position.y = \ outSliceVertices[7].position.y = vertexBounds.yMin + slices.bottomRow; \ outSliceVertices[4].texCoords.y = \ outSliceVertices[5].texCoords.y = \ outSliceVertices[6].texCoords.y = \ outSliceVertices[7].texCoords.y = textureBounds.yMin + slices.bottomRow * texCoordScaleY; \ \ outSliceVertices[8].position.y = \ outSliceVertices[9].position.y = \ outSliceVertices[10].position.y = \ outSliceVertices[11].position.y = vertexBounds.yMax - slices.topRow; \ outSliceVertices[8].texCoords.y = \ outSliceVertices[9].texCoords.y = \ outSliceVertices[10].texCoords.y = \ outSliceVertices[11].texCoords.y = textureBounds.yMax - slices.topRow * texCoordScaleY; \ \ outSliceVertices[12].position.y = \ outSliceVertices[13].position.y = \ outSliceVertices[14].position.y = \ outSliceVertices[15].position.y = vertexBounds.yMax; \ outSliceVertices[12].texCoords.y = \ outSliceVertices[13].texCoords.y = \ outSliceVertices[14].texCoords.y = \ outSliceVertices[15].texCoords.y = textureBounds.yMax; void UIToolkit_sliceQuad3x3(Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x3 slices, UIToolkit_sliceVertex * outSliceVertices, uint32_t * outSliceIndexes) { float texCoordScaleX = (textureBounds.xMax - textureBounds.xMin) / (slices.leftColumn + slices.centerColumn + slices.rightColumn); float texCoordScaleY = (textureBounds.yMax - textureBounds.yMin) / (slices.bottomRow + slices.centerRow + slices.topRow); writeSliceVertices3x3(); for (unsigned int rowIndex = 0; rowIndex < 3; rowIndex++) { for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 0] = rowIndex * 4 + columnIndex; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 1] = rowIndex * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 2] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 3] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 4] = (rowIndex + 1) * 4 + columnIndex; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 5] = rowIndex * 4 + columnIndex; } } } void UIToolkit_sliceQuad3x1(Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x1 slices, UIToolkit_sliceVertex * outSliceVertices, uint32_t * outSliceIndexes) { float texCoordScaleX = (textureBounds.xMax - textureBounds.xMin) / (slices.leftColumn + slices.centerColumn + slices.rightColumn); outSliceVertices[0].position.x = outSliceVertices[4].position.x = vertexBounds.xMin; outSliceVertices[0].texCoords.x = outSliceVertices[4].texCoords.x = textureBounds.xMin; outSliceVertices[1].position.x = outSliceVertices[5].position.x = vertexBounds.xMin + slices.leftColumn; outSliceVertices[1].texCoords.x = outSliceVertices[5].texCoords.x = textureBounds.xMin + slices.leftColumn * texCoordScaleX; outSliceVertices[2].position.x = outSliceVertices[6].position.x = vertexBounds.xMax - slices.rightColumn; outSliceVertices[2].texCoords.x = outSliceVertices[6].texCoords.x = textureBounds.xMax - slices.rightColumn * texCoordScaleX; outSliceVertices[3].position.x = outSliceVertices[7].position.x = vertexBounds.xMax; outSliceVertices[3].texCoords.x = outSliceVertices[7].texCoords.x = textureBounds.xMax; outSliceVertices[0].position.y = outSliceVertices[1].position.y = outSliceVertices[2].position.y = outSliceVertices[3].position.y = vertexBounds.yMin; outSliceVertices[0].texCoords.y = outSliceVertices[1].texCoords.y = outSliceVertices[2].texCoords.y = outSliceVertices[3].texCoords.y = textureBounds.yMin; outSliceVertices[4].position.y = outSliceVertices[5].position.y = outSliceVertices[6].position.y = outSliceVertices[7].position.y = vertexBounds.yMax; outSliceVertices[4].texCoords.y = outSliceVertices[5].texCoords.y = outSliceVertices[6].texCoords.y = outSliceVertices[7].texCoords.y = textureBounds.yMax; for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[columnIndex * 6 + 0] = columnIndex; outSliceIndexes[columnIndex * 6 + 1] = columnIndex + 1; outSliceIndexes[columnIndex * 6 + 2] = columnIndex + 1 + 4; outSliceIndexes[columnIndex * 6 + 3] = columnIndex + 1 + 4; outSliceIndexes[columnIndex * 6 + 4] = columnIndex + 4; outSliceIndexes[columnIndex * 6 + 5] = columnIndex; } } void UIToolkit_sliceQuad1x3(Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid1x3 slices, UIToolkit_sliceVertex * outSliceVertices, uint32_t * outSliceIndexes) { float texCoordScaleY = (textureBounds.yMax - textureBounds.yMin) / (slices.bottomRow + slices.centerRow + slices.topRow); outSliceVertices[0].position.x = outSliceVertices[1].position.x = outSliceVertices[2].position.x = outSliceVertices[3].position.x = vertexBounds.xMin; outSliceVertices[0].texCoords.x = outSliceVertices[1].texCoords.x = outSliceVertices[2].texCoords.x = outSliceVertices[3].texCoords.x = textureBounds.xMin; outSliceVertices[4].position.x = outSliceVertices[5].position.x = outSliceVertices[6].position.x = outSliceVertices[7].position.x = vertexBounds.xMax; outSliceVertices[4].texCoords.x = outSliceVertices[5].texCoords.x = outSliceVertices[6].texCoords.x = outSliceVertices[7].texCoords.x = textureBounds.xMax; outSliceVertices[0].position.y = outSliceVertices[4].position.y = vertexBounds.yMin; outSliceVertices[0].texCoords.y = outSliceVertices[4].texCoords.y = textureBounds.yMin; outSliceVertices[1].position.y = outSliceVertices[5].position.y = vertexBounds.yMin + slices.bottomRow; outSliceVertices[1].texCoords.y = outSliceVertices[5].texCoords.y = textureBounds.yMin + slices.bottomRow * texCoordScaleY; outSliceVertices[2].position.y = outSliceVertices[6].position.y = vertexBounds.yMax - slices.topRow; outSliceVertices[2].texCoords.y = outSliceVertices[6].texCoords.y = textureBounds.yMax - slices.topRow * texCoordScaleY; outSliceVertices[3].position.y = outSliceVertices[7].position.y = vertexBounds.yMax; outSliceVertices[3].texCoords.y = outSliceVertices[7].texCoords.y = textureBounds.yMax; for (unsigned int rowIndex = 0; rowIndex < 3; rowIndex++) { outSliceIndexes[rowIndex * 6 + 0] = rowIndex; outSliceIndexes[rowIndex * 6 + 1] = rowIndex + 4; outSliceIndexes[rowIndex * 6 + 2] = rowIndex + 1 + 4; outSliceIndexes[rowIndex * 6 + 3] = rowIndex + 1 + 4; outSliceIndexes[rowIndex * 6 + 4] = rowIndex + 1; outSliceIndexes[rowIndex * 6 + 5] = rowIndex; } } void UIToolkit_getCountsForSlicedQuad3x3WithRows(Rect4f vertexBounds, UISliceGrid3x3 slices, float inset, float rowHeight, unsigned int * outVertexCount, unsigned int * outIndexCount) { int rowCount = ceilf((vertexBounds.yMax - vertexBounds.yMin - slices.bottomRow - slices.topRow + inset) / rowHeight); if (rowCount < 1) { rowCount = 1; } *outVertexCount = 4 * 4 + (rowCount - 1) * 8; *outIndexCount = 6 * 3 * 2 + rowCount * 18; } void UIToolkit_sliceQuad3x3WithRows(Rect4f vertexBounds, Rect4f textureBounds, UISliceGrid3x3 slices, float inset, float rowHeight, UIToolkit_sliceVertexWithRow * outSliceVertices, uint32_t * outSliceIndexes) { float bottomRowHeight = roundpositivef(fmodf(vertexBounds.yMax - vertexBounds.yMin - slices.bottomRow - slices.topRow + inset, rowHeight)); if (bottomRowHeight == 0.0f) { bottomRowHeight = rowHeight; } float texCoordScaleX = (textureBounds.xMax - textureBounds.xMin) / (slices.leftColumn + slices.centerColumn + slices.rightColumn); float texCoordScaleY = (textureBounds.yMax - textureBounds.yMin) / (slices.bottomRow + slices.centerRow + slices.topRow); int rowCount = ceilf((vertexBounds.yMax - vertexBounds.yMin - slices.bottomRow - slices.topRow + inset) / rowHeight); if (rowCount <= 1) { writeSliceVertices3x3(); for (unsigned int vertexIndex = 0; vertexIndex < UI_SLICE_VERTEX_COUNT_3x3; vertexIndex++) { outSliceVertices[vertexIndex].rowIndex = 0; } for (unsigned int rowIndex = 0; rowIndex < 3; rowIndex++) { for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 0] = rowIndex * 4 + columnIndex; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 1] = rowIndex * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 2] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 3] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 4] = (rowIndex + 1) * 4 + columnIndex; outSliceIndexes[(rowIndex * 3 + columnIndex) * 6 + 5] = rowIndex * 4 + columnIndex; } } return; } writeSliceVertices3x3(); outSliceVertices[0].rowIndex = outSliceVertices[1].rowIndex = outSliceVertices[2].rowIndex = outSliceVertices[3].rowIndex = outSliceVertices[4].rowIndex = outSliceVertices[5].rowIndex = outSliceVertices[6].rowIndex = outSliceVertices[7].rowIndex = rowCount - 1; outSliceVertices[8].rowIndex = outSliceVertices[9].rowIndex = outSliceVertices[10].rowIndex = outSliceVertices[11].rowIndex = outSliceVertices[12].rowIndex = outSliceVertices[13].rowIndex = outSliceVertices[14].rowIndex = outSliceVertices[15].rowIndex = 0; for (int rowIndex = 0; rowIndex < rowCount - 1; rowIndex++) { outSliceVertices[16 + rowIndex * 8 + 0].position.x = vertexBounds.xMin; outSliceVertices[16 + rowIndex * 8 + 1].position.x = vertexBounds.xMin + slices.leftColumn; outSliceVertices[16 + rowIndex * 8 + 2].position.x = vertexBounds.xMax - slices.rightColumn; outSliceVertices[16 + rowIndex * 8 + 3].position.x = vertexBounds.xMax; outSliceVertices[16 + rowIndex * 8 + 0].position.y = outSliceVertices[16 + rowIndex * 8 + 1].position.y = outSliceVertices[16 + rowIndex * 8 + 2].position.y = outSliceVertices[16 + rowIndex * 8 + 3].position.y = vertexBounds.yMin + slices.bottomRow + bottomRowHeight + rowHeight * rowIndex; outSliceVertices[16 + rowIndex * 8 + 0].texCoords.x = textureBounds.xMin; outSliceVertices[16 + rowIndex * 8 + 1].texCoords.x = textureBounds.xMin + slices.leftColumn * texCoordScaleX; outSliceVertices[16 + rowIndex * 8 + 2].texCoords.x = textureBounds.xMax - slices.rightColumn * texCoordScaleX; outSliceVertices[16 + rowIndex * 8 + 3].texCoords.x = textureBounds.xMax; outSliceVertices[16 + rowIndex * 8 + 0].texCoords.y = outSliceVertices[16 + rowIndex * 8 + 1].texCoords.y = outSliceVertices[16 + rowIndex * 8 + 2].texCoords.y = outSliceVertices[16 + rowIndex * 8 + 3].texCoords.y = textureBounds.yMin + slices.bottomRow * texCoordScaleY; outSliceVertices[16 + rowIndex * 8 + 0].rowIndex = outSliceVertices[16 + rowIndex * 8 + 1].rowIndex = outSliceVertices[16 + rowIndex * 8 + 2].rowIndex = outSliceVertices[16 + rowIndex * 8 + 3].rowIndex = rowCount - rowIndex - 1; outSliceVertices[16 + rowIndex * 8 + 4] = outSliceVertices[16 + rowIndex * 8 + 0]; outSliceVertices[16 + rowIndex * 8 + 5] = outSliceVertices[16 + rowIndex * 8 + 1]; outSliceVertices[16 + rowIndex * 8 + 6] = outSliceVertices[16 + rowIndex * 8 + 2]; outSliceVertices[16 + rowIndex * 8 + 7] = outSliceVertices[16 + rowIndex * 8 + 3]; outSliceVertices[16 + rowIndex * 8 + 4].rowIndex = outSliceVertices[16 + rowIndex * 8 + 5].rowIndex = outSliceVertices[16 + rowIndex * 8 + 6].rowIndex = outSliceVertices[16 + rowIndex * 8 + 7].rowIndex = rowCount - rowIndex; } for (unsigned int rowIndex = 0; rowIndex < 3; rowIndex += 2) { for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 0] = rowIndex * 4 + columnIndex; outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 1] = rowIndex * 4 + columnIndex + 1; outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 2] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 3] = (rowIndex + 1) * 4 + columnIndex + 1; outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 4] = (rowIndex + 1) * 4 + columnIndex; outSliceIndexes[(rowIndex / 2 * 3 + columnIndex) * 6 + 5] = rowIndex * 4 + columnIndex; } } for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[(2 * 3 + columnIndex) * 6 + 0] = 4 + columnIndex; outSliceIndexes[(2 * 3 + columnIndex) * 6 + 1] = 4 + columnIndex + 1; outSliceIndexes[(2 * 3 + columnIndex) * 6 + 2] = 16 + columnIndex + 1; outSliceIndexes[(2 * 3 + columnIndex) * 6 + 3] = 16 + columnIndex + 1; outSliceIndexes[(2 * 3 + columnIndex) * 6 + 4] = 16 + columnIndex; outSliceIndexes[(2 * 3 + columnIndex) * 6 + 5] = 4 + columnIndex; } for (int rowIndex = 1; rowIndex < rowCount - 1; rowIndex++) { for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 0] = 16 + rowIndex * 8 - 4 + columnIndex; outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 1] = 16 + rowIndex * 8 - 4 + columnIndex + 1; outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 2] = 16 + rowIndex * 8 + columnIndex + 1; outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 3] = 16 + rowIndex * 8 + columnIndex + 1; outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 4] = 16 + rowIndex * 8 + columnIndex; outSliceIndexes[((rowIndex + 2) * 3 + columnIndex) * 6 + 5] = 16 + rowIndex * 8 - 4 + columnIndex; } } for (unsigned int columnIndex = 0; columnIndex < 3; columnIndex++) { outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 0] = 16 + (rowCount - 1) * 8 - 4 + columnIndex; outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 1] = 16 + (rowCount - 1) * 8 - 4 + columnIndex + 1; outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 2] = 8 + columnIndex + 1; outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 3] = 8 + columnIndex + 1; outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 4] = 8 + columnIndex; outSliceIndexes[((1 + rowCount) * 3 + columnIndex) * 6 + 5] = 16 + (rowCount - 1) * 8 - 4 + columnIndex; } } void UIToolkit_drawVerticalScrollbar(Rect4f containerBounds, float scrollPosition, float heightTotal, float displayedHeight, bool rounded, bool mini, bool dragging, bool mouseover, UIAppearance appearance, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { float scrollbarInset = getAppearanceFloat(appearance, UIToolkit_scrollbarInset); UIToolkit_drawVerticalScrollbarExtended(Rect4f_inset(containerBounds, VECTOR2f(scrollbarInset, scrollbarInset)), scrollPosition, heightTotal, displayedHeight, rounded, dragging ? getAppearanceColor4f(appearance, UIToolkit_scrollbarColorDragging) : mouseover ? getAppearanceColor4f(appearance, UIToolkit_scrollbarColorMouseover) : getAppearanceColor4f(appearance, UIToolkit_scrollbarColor), mini ? getAppearanceFloat(appearance, UIToolkit_scrollbarMiniWidth) : getAppearanceFloat(appearance, UIToolkit_scrollbarWidth), UIAtlasEntry_boundsForScale(rounded ? getAppearanceAtlasEntry(appearance, UIToolkit_scrollbarRounded) : getAppearanceAtlasEntry(appearance, UIToolkit_white), drawingInterface->scaleFactor), getAppearanceSliceGrid1x3(appearance, UIToolkit_scrollbarRoundedSlices), drawingInterface, vertexIO); } void UIToolkit_drawHorizontalScrollbar(Rect4f containerBounds, float scrollPosition, float widthTotal, float displayedWidth, bool rounded, bool mini, bool dragging, bool mouseover, UIAppearance appearance, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { float scrollbarInset = getAppearanceFloat(appearance, UIToolkit_scrollbarInset); UIToolkit_drawHorizontalScrollbarExtended(Rect4f_inset(containerBounds, VECTOR2f(scrollbarInset, scrollbarInset)), scrollPosition, widthTotal, displayedWidth, rounded, dragging ? getAppearanceColor4f(appearance, UIToolkit_scrollbarColorDragging) : mouseover ? getAppearanceColor4f(appearance, UIToolkit_scrollbarColorMouseover) : getAppearanceColor4f(appearance, UIToolkit_scrollbarColor), mini ? getAppearanceFloat(appearance, UIToolkit_scrollbarMiniWidth) : getAppearanceFloat(appearance, UIToolkit_scrollbarWidth), UIAtlasEntry_boundsForScale(rounded ? getAppearanceAtlasEntry(appearance, UIToolkit_scrollbarRounded) : getAppearanceAtlasEntry(appearance, UIToolkit_white), drawingInterface->scaleFactor), getAppearanceSliceGrid3x1(appearance, UIToolkit_scrollbarRoundedSlices), drawingInterface, vertexIO); } void UIToolkit_drawVerticalScrollbarExtended(Rect4f containerBounds, float scrollPosition, float heightTotal, float displayedHeight, bool rounded, Color4f color, float width, Rect4f atlasEntry, UISliceGrid1x3 slices, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { Rect4f scrollbarBounds; scrollbarBounds.xMax = containerBounds.xMax; scrollbarBounds.xMin = scrollbarBounds.xMax - width; scrollbarBounds.yMax = roundpositivef(containerBounds.yMax - (containerBounds.yMax - containerBounds.yMin) * scrollPosition / heightTotal); scrollbarBounds.yMin = scrollbarBounds.yMax - roundpositivef((containerBounds.yMax - containerBounds.yMin) * displayedHeight / heightTotal); if (rounded) { call_virtual(drawSlicedQuad1x3, drawingInterface, scrollbarBounds, atlasEntry, slices, color, vertexIO); } else { call_virtual(drawQuad, drawingInterface, scrollbarBounds, atlasEntry, color, vertexIO); } } void UIToolkit_drawHorizontalScrollbarExtended(Rect4f containerBounds, float scrollPosition, float widthTotal, float displayedWidth, bool rounded, Color4f color, float height, Rect4f atlasEntry, UISliceGrid3x1 slices, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { Rect4f scrollbarBounds; scrollbarBounds.yMin = containerBounds.yMin; scrollbarBounds.yMax = scrollbarBounds.yMin + height; scrollbarBounds.xMin = roundpositivef(containerBounds.xMin + (containerBounds.xMax - containerBounds.xMin) * scrollPosition / widthTotal); scrollbarBounds.xMax = scrollbarBounds.xMin + roundpositivef((containerBounds.xMax - containerBounds.xMin) * displayedWidth / widthTotal); if (rounded) { call_virtual(drawSlicedQuad3x1, drawingInterface, scrollbarBounds, atlasEntry, slices, color, vertexIO); } else { call_virtual(drawQuad, drawingInterface, scrollbarBounds, atlasEntry, color, vertexIO); } } bool UIToolkit_hitTestVerticalScrollbar(float x, float y, Rect4f bounds, float scrollPosition, float heightTotal, float displayedHeight, UIAppearance appearance) { float scrollbarInset = getAppearanceFloat(appearance, UIToolkit_scrollbarInset); bounds = Rect4f_inset(bounds, VECTOR2f(scrollbarInset, scrollbarInset)); float scrollbarWidth = getAppearanceFloat(appearance, UIToolkit_scrollbarWidth); if (x < bounds.xMax && x > bounds.xMax - scrollbarWidth) { float scrollTop = bounds.yMax - (bounds.yMax - bounds.yMin) * scrollPosition / heightTotal; float scrollBottom = scrollTop - roundpositivef((bounds.yMax - bounds.yMin) * displayedHeight / heightTotal); if (y >= scrollBottom && y <= scrollTop) { return true; } } return false; } bool UIToolkit_hitTestHorizontalScrollbar(float x, float y, Rect4f bounds, float scrollPosition, float widthTotal, float displayedWidth, UIAppearance appearance) { float scrollbarInset = getAppearanceFloat(appearance, UIToolkit_scrollbarInset); bounds = Rect4f_inset(bounds, VECTOR2f(scrollbarInset, scrollbarInset)); float scrollbarWidth = getAppearanceFloat(appearance, UIToolkit_scrollbarWidth); if (y > bounds.yMin && y < bounds.yMin + scrollbarWidth) { float scrollLeft = bounds.xMin + (bounds.xMax - bounds.xMin) * scrollPosition / widthTotal; float scrollRight = scrollLeft + roundpositivef((bounds.xMax - bounds.xMin) * displayedWidth / widthTotal); if (x >= scrollLeft && x <= scrollRight) { return true; } } return false; } void UIToolkit_drawPopUpFrameWithShadow(Rect4f bounds, Color4f color, float shadowStrength, UIAppearance appearance, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { UIToolkit_drawPopUpFrameShadowOnly(bounds, shadowStrength, appearance, drawingInterface, vertexIO); call_virtual(drawSlicedQuad3x3, drawingInterface, bounds, UIAtlasEntry_boundsForScale(getAppearanceAtlasEntry(appearance, UIPopUpView_popUpFrame), drawingInterface->scaleFactor), getAppearanceSliceGrid3x3(appearance, UIPopUpView_frameSlices), color, vertexIO); } void UIToolkit_drawPopUpFrameShadowOnly(Rect4f bounds, float shadowStrength, UIAppearance appearance, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { float frameShadowOutset = getAppearanceFloat(appearance, UIPopUpView_frameShadowOutset); call_virtual(drawSlicedQuad3x3, drawingInterface, Rect4f_offset(Rect4f_inset(bounds, VECTOR2f(-frameShadowOutset, -frameShadowOutset)), VECTOR2f(0.0f, -getAppearanceFloat(appearance, UIPopUpView_frameShadowOffset))), UIAtlasEntry_boundsForScale(getAppearanceAtlasEntry(appearance, UIPopUpView_popUpShadow), drawingInterface->scaleFactor), getAppearanceSliceGrid3x3(appearance, UIPopUpView_frameShadowSlices), COLOR4f(0.0f, 0.0f, 0.0f, shadowStrength), vertexIO); } void UIToolkit_drawPopUpTail(Rect4f popUpBounds, Vector2f tailPosition, Color4f tailColor, UIAppearance appearance, UIDrawingInterface * drawingInterface, VertexIO * vertexIO) { quadTransformBits transform; Vector2f tailPositionWithOffset = tailPosition; UIAtlasEntry atlasEntry; Vector2f origin; if (tailPosition.y <= popUpBounds.yMin) { transform = 0; origin = VECTOR2f(0.5f, 1.0f); tailPositionWithOffset.y += 2; atlasEntry = getAppearanceAtlasEntry(appearance, UIPopUpView_popUpTailDark); } else if (tailPosition.y >= popUpBounds.yMax) { transform = QUAD_TRANSFORM_ROTATE_180; origin = VECTOR2f(0.5f, 0.0f); tailPositionWithOffset.y -= 2; atlasEntry = getAppearanceAtlasEntry(appearance, UIPopUpView_popUpTailLight); } else if (tailPosition.x <= popUpBounds.xMin) { transform = QUAD_TRANSFORM_ROTATE_CW; origin = VECTOR2f(1.0f, 0.5f); tailPositionWithOffset.x += 2; atlasEntry = getAppearanceAtlasEntry(appearance, UIPopUpView_popUpTailLight); atlasEntry.size = Vector2f_transpose(atlasEntry.size); } else if (tailPosition.x >= popUpBounds.xMax) { transform = QUAD_TRANSFORM_ROTATE_CCW; origin = VECTOR2f(0.0f, 0.5f); tailPositionWithOffset.x -= 2; atlasEntry = getAppearanceAtlasEntry(appearance, UIPopUpView_popUpTailDark); atlasEntry.size = Vector2f_transpose(atlasEntry.size); } else { return; } call_virtual(drawQuadWithTransform, drawingInterface, Rect4f_round(Rect4f_fromPositionSizeOrigin(tailPositionWithOffset, atlasEntry.size, origin)), UIAtlasEntry_boundsForScale(atlasEntry, drawingInterface->scaleFactor), transform, tailColor, vertexIO); } UITypeface * UIToolkit_getUITypeface(UIAppearance appearance, UIDrawingInterface * drawingInterface) { if (drawingInterface->scaleFactor >= 2.0f) { UITypeface * typeface = getAppearanceTypeface(appearance, UIToolkit_typeface_2x); if (typeface != NULL) { return typeface; } } return getAppearanceTypeface(appearance, UIToolkit_typeface); } void UIToolkit_getBestInvokerAttachPoint(Rect4f invokerBounds, Vector2f size, UISidePreference sidePreference, float perpendicularMin, float offset, Rect4f safeUIBounds, Vector2f * outRawPosition, Vector2f * outAdjustedPosition, Vector2f * outRelativeOrigin) { Vector2f origins[4] = {VECTOR2f(1.0f, 0.5f), VECTOR2f(0.0f, 0.5f), VECTOR2f(0.5f, 1.0f), VECTOR2f(0.5f, 0.0f)}; switch (sidePreference) { case UISidePreference_left: break; case UISidePreference_right: origins[0] = VECTOR2f(0.0f, 0.5f); origins[1] = VECTOR2f(1.0f, 0.5f); break; case UISidePreference_bottom: origins[0] = VECTOR2f(0.5f, 1.0f); origins[1] = VECTOR2f(0.5f, 0.0f); origins[2] = VECTOR2f(1.0f, 0.5f); origins[3] = VECTOR2f(0.0f, 0.5f); break; case UISidePreference_top: origins[0] = VECTOR2f(0.5f, 0.0f); origins[1] = VECTOR2f(0.5f, 1.0f); origins[2] = VECTOR2f(1.0f, 0.5f); origins[3] = VECTOR2f(0.0f, 0.5f); break; } Vector2f invokerCenter = Rect4f_getCenter(invokerBounds); Vector2f bestProposedPosition = invokerCenter; Vector2f bestProposedOrigin = VECTOR2f(0.5f, 0.5f); for (unsigned int originIndex = 0; originIndex < 4; originIndex++) { Vector2f proposedPosition = VECTOR2f(invokerBounds.xMax - (invokerBounds.xMax - invokerBounds.xMin) * origins[originIndex].x, invokerBounds.yMax - (invokerBounds.yMax - invokerBounds.yMin) * origins[originIndex].y); if (origins[originIndex].x != 0.5f) { proposedPosition.x -= offset * (origins[originIndex].x * 2.0f - 1.0f); } else { proposedPosition.y -= offset * (origins[originIndex].y * 2.0f - 1.0f); } Rect4f proposedBounds = UIElement_boundsRectWithOrigin(proposedPosition, origins[originIndex], size); bool fit; if (origins[originIndex].x != 0.5f) { fit = proposedBounds.xMin >= safeUIBounds.xMin && proposedBounds.xMax <= safeUIBounds.xMax; if (invokerCenter.y - perpendicularMin - offset < safeUIBounds.yMin || invokerCenter.y + perpendicularMin + offset > safeUIBounds.yMax) { fit = false; } } else { fit = proposedBounds.yMin >= safeUIBounds.yMin && proposedBounds.yMax <= safeUIBounds.yMax; if (invokerCenter.x - perpendicularMin - offset < safeUIBounds.xMin || invokerCenter.x + perpendicularMin + offset > safeUIBounds.xMax) { fit = false; } } if (fit) { bestProposedPosition = proposedPosition; bestProposedOrigin = origins[originIndex]; break; } } bestProposedPosition = Vector2f_round(bestProposedPosition); Rect4f proposedBounds = UIElement_boundsRectWithOrigin(bestProposedPosition, bestProposedOrigin, size); if (outRawPosition != NULL) { *outRawPosition = bestProposedPosition; } if (proposedBounds.xMin < safeUIBounds.xMin) { bestProposedPosition.x += safeUIBounds.xMin - proposedBounds.xMin; } else if (proposedBounds.xMax > safeUIBounds.xMax) { bestProposedPosition.x += safeUIBounds.xMax - proposedBounds.xMax; } if (proposedBounds.yMin < safeUIBounds.yMin) { bestProposedPosition.y += safeUIBounds.yMin - proposedBounds.yMin; } else if (proposedBounds.yMax > safeUIBounds.yMax) { bestProposedPosition.y += safeUIBounds.yMax - proposedBounds.yMax; } if (outAdjustedPosition != NULL) { *outAdjustedPosition = bestProposedPosition; } if (outRelativeOrigin != NULL) { *outRelativeOrigin = bestProposedOrigin; } }