File CanvasWidget.c
File List > engine > src > gameframework > layers > UI > widgets > CanvasWidget.c
Go to the documentation of this file
#include "CanvasWidget.h"
#include "Widget.h"
#include "DataNode.h"
#include "XMLUIGameLayer.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "Atlas.h"
#include "AssertLib.h"
#include "RootWidget.h"
#include "Geometry.h"
#include "WidgetVertexOutputHelpers.h"
#include "FloatingPointLib.h"
static struct WidgetPadding zeroPadding =
{
0,0,0,0
};
static float BBWidth(GeomRect r)
{
return r[2] - r[0];
}
static float BBHeight(GeomRect r)
{
return r[3] - r[1];
}
static float GetWidth(struct UIWidget* pWidget, struct UIWidget* pParent)
{
return UI_ResolveWidthDimPxls(pWidget, &pWidget->width);
}
static float GetHeight(struct UIWidget* pWidget, struct UIWidget* pParent)
{
return UI_ResolveHeightDimPxls(pWidget, &pWidget->height);
}
static void UnionRects(vec2 tl1, vec2 br1, vec2 tl2, vec2 br2)
{
if (tl2[0] < tl1[0])
{
tl1[0] = tl2[0];
}
if (tl2[1] < tl1[1])
{
tl1[1] = tl2[1];
}
if (br2[0] > br1[0])
{
br1[0] = br2[0];
}
if (br2[1] > br1[1])
{
br1[1] = br2[1];
}
}
static void GetChildrenBoundingBox(struct UIWidget* pWidget, vec2 tl, vec2 br)
{
bool bSet = false;
HWidget hChild = pWidget->hFirstChild;
while (hChild != NULL_HWIDGET)
{
vec2 tl0, br0;
struct UIWidget* pChild = UI_GetWidget(hChild);
tl0[0] = pChild->left;
tl0[1] = pChild->top;
vec2 dims;
UI_GetWidgetSize(hChild, &dims[0], &dims[1]);
glm_vec2_add(tl0, dims, br0);
if (!bSet)
{
memcpy(tl, tl0, sizeof(vec2));
memcpy(br, br0, sizeof(vec2));
bSet = true;
hChild = pChild->hNext;
continue; // nothing to see here
}
UnionRects(tl, br, tl0, br0);
}
}
static void SetScroll(struct CanvasData* pData)
{
if (pData->bVSliderActive)
{
pData->scrollY = pData->sliderV.fVal;
}
else
{
pData->scrollY = 0;
}
if (pData->bHSliderActive)
{
pData->scrollX = pData->sliderH.fVal;
}
else
{
pData->scrollX = 0;
}
}
static void SetSliderMinAndMax(struct UIWidget* pWidget, struct CanvasData* pCanvasData)
{
float w = pWidget->fnGetWidth(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingRight + pWidget->padding.paddingLeft);
float h = pWidget->fnGetHeight(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingTop + pWidget->padding.paddingBottom);
float left = pWidget->left + pWidget->padding.paddingLeft;
float top = pWidget->top + pWidget->padding.paddingTop;
float amountAbove = top - pCanvasData->contentBB[1];
float amountBelow = pCanvasData->contentBB[3] - (top + h);
float amoutLeft = left - pCanvasData->contentBB[0];
float amountRight = pCanvasData->contentBB[2] - (left + w);
pCanvasData->sliderH.fMinVal = fabs(amoutLeft);
pCanvasData->sliderH.fMaxVal = -fabs(amountRight);
pCanvasData->sliderV.fMinVal = fabs(amountAbove);
pCanvasData->sliderV.fMaxVal = -fabs(amountBelow);
pCanvasData->sliderV.fVal = pCanvasData->sliderV.fMaxVal;
pCanvasData->sliderH.fVal = pCanvasData->sliderH.fMaxVal;
}
static void LayoutChildren(struct UIWidget* pWidget, struct UIWidget* pParent)
{
struct CanvasData* pData = pWidget->pImplementationData;
SetScroll(pData);
RootWidget_LayoutChildren(pWidget, pParent, pData->scrollX, pData->scrollY);
GetChildrenBoundingBox(pWidget, pData->contentBB, &pData->contentBB[2]);
}
static void OnDestroy(struct UIWidget* pWidget)
{
free(pWidget->pImplementationData);
}
static bool ContentExceedsSize(struct CanvasData* pCanvasData, struct UIWidget* pWidget, bool* pOutExceedsWidth, bool* pOutExceedsHeight)
{
struct CanvasData* pData = pWidget->pImplementationData;
float w = pWidget->fnGetWidth(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingRight + pWidget->padding.paddingLeft);
float h = pWidget->fnGetHeight(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingTop + pWidget->padding.paddingBottom);
float contentW = BBWidth(pCanvasData->contentBB);
float contentH = BBHeight(pCanvasData->contentBB);
*pOutExceedsWidth = !CompareFloat(contentW, w) && contentW > w;
*pOutExceedsHeight = !CompareFloat(contentH, h) && contentH > h;
return *pOutExceedsWidth || *pOutExceedsHeight;
}
void GetClipRegion(GeomRect rect, struct UIWidget* pWidget)
{
float w = pWidget->fnGetWidth(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingRight + pWidget->padding.paddingLeft);
float h = pWidget->fnGetHeight(pWidget, UI_GetWidget(pWidget->hParent)) - (pWidget->padding.paddingTop + pWidget->padding.paddingBottom);
rect[0] = pWidget->left + pWidget->padding.paddingLeft;
rect[1] = pWidget->top + pWidget->padding.paddingTop;
rect[2] = rect[0] + w;
rect[3] = rect[1] + h;
}
static void SetSliderPositionAndDims(struct UIWidget* pWidget, struct CanvasData* pData)
{
if(pData->bUseHSlider || pData->bUseVSlider)
{
float canvasH = pWidget->fnGetHeight(pWidget, UI_GetWidget(pWidget->hParent));
float canvasW = pWidget->fnGetWidth(pWidget, UI_GetWidget(pWidget->hParent));
pData->sliderV.lengthPx = canvasH - (pWidget->padding.paddingTop + pWidget->padding.paddingBottom);
pData->sliderH.lengthPx = canvasW - (pWidget->padding.paddingLeft + pWidget->padding.paddingRight);
vec2 canvasTL = {
pWidget->left,
pWidget->top
};
if(pData->bUseHSlider)
{
pData->sliderHTopLeft[0] = canvasTL[0] + pWidget->padding.paddingLeft;
pData->sliderHTopLeft[1] = canvasTL[1] + (canvasH - SliderWidget_GetHeight(&pData->sliderH, &zeroPadding));
}
if(pData->bUseVSlider)
{
pData->sliderVTopLeft[0] = canvasTL[0] + (canvasW - SliderWidget_GetWidth(&pData->sliderV, &zeroPadding));
pData->sliderVTopLeft[1] = canvasTL[1] + pWidget->padding.paddingTop;
}
}
}
void* CanvasWidget_OnOutputVerts(struct UIWidget* pWidget, VECTOR(WidgetVertex) pOutVerts)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
bool bExceedsW, bExceedsH;
if (ContentExceedsSize(pCanvasData, pWidget, &bExceedsW, &bExceedsH))
{
GeomRect region;
GetClipRegion(region, pWidget);
SetClipRect(region);
if(pCanvasData->bUseVSlider || pCanvasData->bUseHSlider)
{
SetSliderPositionAndDims(pWidget, pCanvasData);
}
if(pCanvasData->bUseHSlider) pCanvasData->bHSliderActive = bExceedsW;
if(pCanvasData->bUseVSlider) pCanvasData->bVSliderActive = bExceedsH;
}
pOutVerts = UI_Helper_OnOutputVerts(pWidget, pOutVerts);
UnsetClipRect();
if (pCanvasData->bUseVSlider && bExceedsH)
{
pOutVerts = SliderWidget_OnOutputVerts(pOutVerts, &pCanvasData->sliderV, pCanvasData->sliderVTopLeft[1], pCanvasData->sliderVTopLeft[0], &zeroPadding);
}
if (pCanvasData->bUseHSlider && bExceedsW)
{
pOutVerts = SliderWidget_OnOutputVerts(pOutVerts, &pCanvasData->sliderH, pCanvasData->sliderHTopLeft[1], pCanvasData->sliderHTopLeft[0], &zeroPadding);
}
return pOutVerts;
}
static void OnWidgetInit(struct UIWidget* pWidget)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
LayoutChildren(pWidget, UI_GetWidget(pWidget->hParent));
GetChildrenBoundingBox(pWidget, pCanvasData->contentBB, &pCanvasData->contentBB[2]);
bool bExceedsW, bExceedsH;
if (ContentExceedsSize(pCanvasData, pWidget, &bExceedsW, &bExceedsH))
{
// need to call this when the contents of the canvas change
if(pCanvasData->bUseHSlider || pCanvasData->bUseVSlider)
{
SetSliderMinAndMax(pWidget, pCanvasData);
}
}
}
static void OnPropertyChanged(struct UIWidget* pThisWidget, struct WidgetPropertyBinding* pBinding)
{
}
static void MouseButtonDownCallback(struct UIWidget* pWidget, float x, float y, int btn)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
if (pCanvasData->bUseHSlider && pCanvasData->bHSliderActive)
{
GeomRect hsliderRect =
{
pCanvasData->sliderHTopLeft[0],
pCanvasData->sliderHTopLeft[1],
pCanvasData->sliderHTopLeft[0] + SliderWidget_GetWidth(&pCanvasData->sliderH, &zeroPadding),
pCanvasData->sliderHTopLeft[1] + SliderWidget_GetHeight(&pCanvasData->sliderH, &zeroPadding) ,
};
if (Ge_PointInAABB(x, y, hsliderRect))
{
SliderWudget_SetSliderPositionFromMouse(pWidget, &pCanvasData->sliderH, x, y, pCanvasData->sliderHTopLeft[1], pCanvasData->sliderHTopLeft[1], zeroPadding);
pCanvasData->sliderH.bMouseDown = true;
}
}
if (pCanvasData->bUseVSlider && pCanvasData->bVSliderActive)
{
GeomRect vsliderRect =
{
pCanvasData->sliderVTopLeft[0],
pCanvasData->sliderVTopLeft[1],
pCanvasData->sliderVTopLeft[0] + SliderWidget_GetWidth(&pCanvasData->sliderV, &zeroPadding),
pCanvasData->sliderVTopLeft[1] + SliderWidget_GetHeight(&pCanvasData->sliderV, &zeroPadding) ,
};
if (Ge_PointInAABB(x, y, vsliderRect))
{
SliderWudget_SetSliderPositionFromMouse(pWidget, &pCanvasData->sliderV, x, y, pCanvasData->sliderVTopLeft[1], pCanvasData->sliderVTopLeft[1], zeroPadding);
pCanvasData->sliderV.bMouseDown = true;
}
}
}
static void MouseButtonUpCallback(struct UIWidget* pWidget, float x, float y, int btn)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
pCanvasData->sliderV.bMouseDown = false;
pCanvasData->sliderH.bMouseDown = false;
}
static void MouseLeaveCallback(struct UIWidget* pWidget, float x, float y)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
pCanvasData->sliderV.bMouseDown = false;
pCanvasData->sliderH.bMouseDown = false;
}
static void MouseMoveCallback(struct UIWidget* pWidget, float x, float y)
{
struct CanvasData* pCanvasData = pWidget->pImplementationData;
if (pCanvasData->bUseHSlider && pCanvasData->bHSliderActive)
{
if (pCanvasData->sliderH.bMouseDown)
{
SliderWudget_SetSliderPositionFromMouse(pWidget, &pCanvasData->sliderH, x, y, pCanvasData->sliderHTopLeft[1], pCanvasData->sliderHTopLeft[1], zeroPadding);
}
}
if (pCanvasData->bUseVSlider && pCanvasData->bVSliderActive)
{
if (pCanvasData->sliderV.bMouseDown)
{
SliderWudget_SetSliderPositionFromMouse(pWidget, &pCanvasData->sliderV, x, y, pCanvasData->sliderVTopLeft[1], pCanvasData->sliderVTopLeft[1], zeroPadding);
}
}
}
static void OnChildrenChanged(struct UIWidget* pWidget)
{
struct CanvasData* pData = pWidget->pImplementationData;
GetChildrenBoundingBox(pWidget, pData->contentBB, &pData->contentBB[2]);
SetSliderMinAndMax(pWidget, pData);
SetScroll(pData);
UI_DefaultOnChildrenChanged(pWidget);
}
static void MakeWidgetIntoCanvasWidget(HWidget hWidget, struct DataNode* pDataNode, struct XMLUIData* pUILayerData)
{
struct UIWidget* pWidget = UI_GetWidget(hWidget);
pWidget->hNext = -1;
pWidget->hPrev = -1;
pWidget->hParent = -1;
pWidget->hFirstChild = -1;
pWidget->fnGetHeight = &GetHeight;
pWidget->fnGetWidth = &GetWidth;
pWidget->fnLayoutChildren = &LayoutChildren;
pWidget->fnOnDestroy = &OnDestroy;
pWidget->fnOutputVertices = &CanvasWidget_OnOutputVerts;
pWidget->fnOnBoundPropertyChanged = &OnPropertyChanged;
pWidget->fnOnWidgetInit = &OnWidgetInit;
pWidget->fnOnWidgetChildrenChangedFn = &OnChildrenChanged;
pWidget->cCallbacks.Callbacks[WC_OnMouseDown].type = WC_OnMouseDown;
pWidget->cCallbacks.Callbacks[WC_OnMouseDown].callback.mouseBtnFn = &MouseButtonDownCallback;
pWidget->cCallbacks.Callbacks[WC_OnMouseUp].type = WC_OnMouseUp;
pWidget->cCallbacks.Callbacks[WC_OnMouseUp].callback.mouseBtnFn = &MouseButtonUpCallback;
pWidget->cCallbacks.Callbacks[WC_OnMouseLeave].type = WC_OnMouseLeave;
pWidget->cCallbacks.Callbacks[WC_OnMouseLeave].callback.mousePosFn = &MouseLeaveCallback;
pWidget->cCallbacks.Callbacks[WC_OnMouseMove].type = WC_OnMouseMove;
pWidget->cCallbacks.Callbacks[WC_OnMouseMove].callback.mousePosFn = &MouseMoveCallback;
pWidget->pImplementationData = malloc(sizeof(struct CanvasData));
memset(pWidget->pImplementationData, 0, sizeof(struct CanvasData));
struct CanvasData* pData = pWidget->pImplementationData;
pData->bUseHSlider = true; // todo: move to xml
pData->bUseVSlider = true;
if(pData->bUseHSlider)
{
SliderWidget_MakeDefaultSliderWidget(&pData->sliderH, pUILayerData, SO_Horizontal);
}
if(pData->bUseVSlider)
{
SliderWidget_MakeDefaultSliderWidget(&pData->sliderV, pUILayerData, SO_Vertical);
}
}
HWidget CanvasWidgetNew(HWidget hParent, struct DataNode* pDataNode, struct XMLUIData* pUILayerData)
{
HWidget hWidget = UI_NewBlankWidget();
MakeWidgetIntoCanvasWidget(hWidget, pDataNode, pUILayerData);
return hWidget;
}