File Widget.c
File List > engine > src > gameframework > layers > UI > widgets > Widget.c
Go to the documentation of this file
#include "Widget.h"
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include "Scripting.h"
#include "AssertLib.h"
#include "DataNode.h"
#include "DrawContext.h"
#include "Log.h"
OBJECT_POOL(struct UIWidget) gWidgetPool = NULL;
HWidget gScratchWidget = NULL_HANDLE;
#define WIDGET_POOL_BOUNDS_CHECK(handle, rVal) OBJ_POOL_BOUNDS_CHECK(handle, rVal, gWidgetPool)
#define WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(handle) OBJ_POOL_BOUNDS_CHECK_NO_RETURN(handle, gWidgetPool)
static void MakeBlankScriptingCallbacks(struct LuaWidgetCallbacks* pCallbacks)
{
memset(pCallbacks, 0, sizeof(struct LuaWidgetCallbacks));
}
HWidget UI_NewBlankWidget()
{
HWidget widget = 0;
gWidgetPool = GetObjectPoolIndex(gWidgetPool, &widget);
struct UIWidget* pWidget = &gWidgetPool[widget];
memset(pWidget, 0, sizeof(struct UIWidget));
pWidget->hFirstChild = NULL_HWIDGET;
pWidget->hNext = NULL_HWIDGET;
pWidget->hPrev = NULL_HWIDGET;
pWidget->hParent = NULL_HWIDGET;
pWidget->numBindings = 0;
pWidget->hThis = widget;
pWidget->width.type = WD_Auto;
pWidget->height.type = WD_Auto;
MakeBlankScriptingCallbacks(&pWidget->scriptCallbacks);
return widget;
}
size_t UI_CountWidgetChildrenPtr(struct UIWidget* pWidget)
{
size_t count = 0;
HWidget h = pWidget->hFirstChild;
while (h != NULL_HWIDGET)
{
struct UIWidget* pChild = UI_GetWidget(h);
count++;
h = pChild->hNext;
}
return count;
}
size_t UI_CountWidgetChildren(HWidget hWidget)
{
WIDGET_POOL_BOUNDS_CHECK(hWidget, 0);
struct UIWidget* pWidget = UI_GetWidget(hWidget);
return UI_CountWidgetChildrenPtr(pWidget);
}
struct UIWidget* UI_GetWidget(HWidget hWidget)
{
if (hWidget < 0)
{
return NULL;
}
return &gWidgetPool[hWidget];
}
struct UIWidget* UI_FirstChild(HWidget hWidget)
{
struct UIWidget* pWidget = UI_GetWidget(hWidget);
if(!pWidget)
{
return NULL;
}
return UI_GetWidget(pWidget->hFirstChild);
}
void UI_AddChild(HWidget hParent, HWidget hChild)
{
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(hParent)
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(hChild)
struct UIWidget* pParent = UI_GetWidget(hParent);
struct UIWidget* pChild = UI_GetWidget(hChild);
pChild->hParent = hParent;
if (pParent->hFirstChild == NULL_HWIDGET)
{
pParent->hFirstChild = hChild;
return;
}
struct UIWidget* pLastChild = UI_GetWidget(pParent->hFirstChild);
HWidget hLastWidget = hChild;
while (pLastChild->hNext != NULL_HWIDGET)
{
pLastChild = UI_GetWidget(pLastChild->hNext);
hLastWidget = pLastChild->hNext;
}
pLastChild->hNext = hChild;
pChild->hPrev = hLastWidget;
pChild->hNext = NULL_HWIDGET;
}
void UI_Init()
{
gWidgetPool = NEW_OBJECT_POOL(struct UIWidget, 256);
GetObjectPoolIndex(gWidgetPool, &gScratchWidget);
}
HWidget UI_GetScratchWiget()
{
EASSERT(gScratchWidget != NULL_HANDLE);
return gScratchWidget;
}
void UI_DestroyWidget(HWidget widget)
{
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(widget)
struct UIWidget* pWidget = UI_GetWidget(widget);
if (pWidget->hNext != NULL_HWIDGET)
{
UI_GetWidget(pWidget->hNext)->hPrev = pWidget->hPrev;
}
if (pWidget->hPrev != NULL_HWIDGET)
{
UI_GetWidget(pWidget->hPrev)->hNext = pWidget->hNext;
}
if (widget >= 0 && widget < ObjectPoolCapacity(gWidgetPool))
{
gWidgetPool[widget].fnOnDestroy(&gWidgetPool[widget]);
HWidget child = gWidgetPool[widget].hFirstChild;
while (child != NULL_HWIDGET)
{
UI_DestroyWidget(child);
child = gWidgetPool[child].hNext;
}
FreeObjectPoolIndex(gWidgetPool, widget);
}
else
{
Log_Error("DestroyWidget: widget %i out of range", widget);
}
}
void UI_ParseWidgetDimsAttribute(const char* attributeContent, struct WidgetDim* outWidgetDims)
{
if (strcmp(attributeContent, "auto") == 0)
{
outWidgetDims->type = WD_Auto;
return;
}
if (strcmp(attributeContent, "*") == 0)
{
outWidgetDims->type = WD_Stretch;
return;
}
char dimType[16];
float dimVal = 0.0f;
int numMatched = sscanf(attributeContent, "%f%s", &dimVal, dimType);
switch (numMatched)
{
case 1:
outWidgetDims->type = WD_Pixels;
break;
case 2:
if (strcmp(dimType, "px") == 0)
{
outWidgetDims->type = WD_Pixels;
}
else if (strcmp(dimType, "*") == 0)
{
outWidgetDims->type = WD_StretchFraction;
}
else if (strcmp(dimType, "%") == 0)
{
outWidgetDims->type = WD_Percentage;
}
break;
default:
Log_Error("invalid widget dim value %s", attributeContent);
EASSERT(false);
break;
}
outWidgetDims->data = dimVal;
}
void UI_ParseWidgetPaddingAttributes(struct DataNode* pInNode, struct WidgetPadding* outWidgetPadding)
{
if(pInNode->fnGetPropType(pInNode, "paddingTop") == DN_Float || pInNode->fnGetPropType(pInNode, "paddingTop") == DN_Int)
{
outWidgetPadding->paddingTop = pInNode->fnGetFloat(pInNode, "paddingTop");
}
if(pInNode->fnGetPropType(pInNode, "paddingBottom") == DN_Float || pInNode->fnGetPropType(pInNode, "paddingBottom") == DN_Int)
{
outWidgetPadding->paddingBottom = pInNode->fnGetFloat(pInNode, "paddingBottom");
}
if(pInNode->fnGetPropType(pInNode, "paddingLeft") == DN_Float || pInNode->fnGetPropType(pInNode, "paddingLeft") == DN_Int)
{
outWidgetPadding->paddingLeft = pInNode->fnGetFloat(pInNode, "paddingLeft");
}
if(pInNode->fnGetPropType(pInNode, "paddingRight") == DN_Float || pInNode->fnGetPropType(pInNode, "paddingRight") == DN_Int)
{
outWidgetPadding->paddingRight = pInNode->fnGetFloat(pInNode, "paddingRight");
}
}
void UI_ParseHorizontalAlignementAttribute(const char* contents, enum WidgetHorizontalAlignment* outAlignment)
{
if (strcmp(contents, "left") == 0)
{
*outAlignment = WHA_Left;
}
else if (strcmp(contents, "middle") == 0)
{
*outAlignment = WHA_Middle;
}
else if (strcmp(contents, "right") == 0)
{
*outAlignment = WHA_Right;
}
else
{
*outAlignment = WHA_Middle;
}
}
void UI_ParseVerticalAlignementAttribute(const char* contents, enum WidgetVerticalAlignment* outAlignment)
{
if (strcmp(contents, "top") == 0)
{
*outAlignment = WVA_Top;
}
else if (strcmp(contents, "middle") == 0)
{
*outAlignment = WVA_Middle;
}
else if (strcmp(contents, "bottom") == 0)
{
*outAlignment = WVA_Bottom;
}
else
{
*outAlignment = WVA_Middle;
}
}
struct WidgetDim* GetWidgetWidthDim(struct UIWidget* pWidget) { return &pWidget->width; }
struct WidgetDim* GetWidgetHeightDim(struct UIWidget* pWidget) { return &pWidget->height; }
static void GetTotalFractionAmongChildren(struct UIWidget* pWidgetParent, float* outV, WidgetDimGetterFn getter)
{
*outV = 0.0f;
HWidget hChild = pWidgetParent->hFirstChild;
while (hChild != NULL_HWIDGET)
{
struct UIWidget* pChild = UI_GetWidget(hChild);
const struct WidgetDim* dim = getter(pChild);
EASSERT(dim->type == WD_StretchFraction);
*outV += dim->data;
hChild = pChild->hNext;
}
}
typedef float(*GetWorHFn)(struct UIWidget* pW);
float GetWidgetW(struct UIWidget* pW) { return pW->fnGetWidth(pW, UI_GetWidget(pW->hParent)); }
float GetWidgetH(struct UIWidget* pW) { return pW->fnGetHeight(pW, UI_GetWidget(pW->hParent)); }
static void GetTotalAmountAmongChildren(struct UIWidget* pWidgetParent, float* outV, GetWorHFn getWorH, WidgetDimGetterFn dimgetter)
{
*outV = 0.0f;
HWidget hChild = pWidgetParent->hFirstChild;
int stretchCount = 0;
while (hChild != NULL_HWIDGET)
{
struct UIWidget* pChild = UI_GetWidget(hChild);
const struct WidgetDim* dim = dimgetter(pChild);
switch (dim->type)
{
case WD_Stretch:
stretchCount++;
if (stretchCount > 1)
{
EASSERT(false);// only one child can have a width or height of *
}
break;
case WD_Auto:
case WD_Pixels:
*outV += getWorH(pChild);
break;
default:
EASSERT(false); // only one child can have a width or height of *
break;
}
*outV += dim->data;
hChild = pChild->hNext;
}
}
struct UIWidget* FindResolveableDimensionAncestor(struct UIWidget* pWidgetParent, WidgetDimGetterFn getter)
{
HWidget hParent = pWidgetParent->hParent;
while (hParent != NULL_HWIDGET)
{
struct UIWidget* pParent = UI_GetWidget(hParent);
EASSERT(pParent);
const struct WidgetDim* pDim = getter(pParent);
if (pDim->type == WD_Percentage || pDim->type == WD_StretchFraction || pDim->type == WD_Pixels)
{
return pParent;
}
hParent = pParent->hParent;
}
EASSERT(false); // root widget must be of of fixed size
return NULL;
}
float UI_ResolveWidthDimPxls(struct UIWidget* pWidget, const struct WidgetDim* dim)
{
switch (dim->type)
{
case WD_Stretch:
{
struct UIWidget* pResolvableWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetWidthDim);
struct UIWidget* pActualWidgetParent = UI_GetWidget(pWidget->hParent);
float amount = 0;
GetTotalAmountAmongChildren(pActualWidgetParent, &amount, &GetWidgetW, &GetWidgetWidthDim);
float parentW = UI_ResolveWidthDimPxls(pResolvableWidgetParent, &pResolvableWidgetParent->height);
return parentW - amount;
}
case WD_Auto:
{
if(pWidget->hFirstChild != NULL_HANDLE)
{
// TODO: This should really return the size of pWidget not its first child...
struct UIWidget* pChild = UI_GetWidget(pWidget->hThis); // REMEMBER: This used to pass pWidget->hFirstChild to UI_GetWidget!!
return pChild->fnGetWidth(pChild, pWidget);
}
}
case WD_Pixels: return dim->data;
case WD_StretchFraction:
{
float wFraction;
//EASSERT(pWidgetParent->width.type != WD_Auto);
struct UIWidget* pResolvableWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetWidthDim);
struct UIWidget* pActualWidgetParent = UI_GetWidget(pWidget->hParent);
GetTotalFractionAmongChildren(pActualWidgetParent, &wFraction, &GetWidgetWidthDim);
float parentW = UI_ResolveWidthDimPxls(pResolvableWidgetParent, &pResolvableWidgetParent->width);
return (dim->data / wFraction) * parentW;
}
case WD_Percentage:
{
struct UIWidget* pWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetWidthDim);
float parentW = UI_ResolveWidthDimPxls(pWidgetParent, &pWidgetParent->width);
return dim->data * parentW;
}
default:
break;
}
return dim->data;
}
float UI_ResolveHeightDimPxls(struct UIWidget* pWidget, const struct WidgetDim* dim)
{
switch (dim->type)
{
case WD_Stretch:
{
struct UIWidget* pResolvableWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetHeightDim);
struct UIWidget* pActualWidgetParent = UI_GetWidget(pWidget->hParent);
float amount = 0;
GetTotalAmountAmongChildren(pActualWidgetParent, &amount, &GetWidgetH, &GetWidgetHeightDim);
float parentH = UI_ResolveHeightDimPxls(pResolvableWidgetParent, &pResolvableWidgetParent->height);
return parentH - amount;
}
case WD_Auto:
{
if(pWidget->hFirstChild != NULL_HANDLE)
{
struct UIWidget* pChild = UI_GetWidget(pWidget->hThis); // REMEMBER: This used to pass pWidget->hFirstChild to UI_GetWidget!!
return pChild->fnGetHeight(pChild, pWidget);
}
}
case WD_Pixels: return dim->data;
case WD_StretchFraction:
{
float wFraction;
//EASSERT(pWidgetParent->width.type != WD_Auto);
struct UIWidget* pResolvableWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetHeightDim);
struct UIWidget* pActualWidgetParent = UI_GetWidget(pWidget->hParent);
GetTotalFractionAmongChildren(pActualWidgetParent, &wFraction, &GetWidgetHeightDim);
float parentW = UI_ResolveHeightDimPxls(pResolvableWidgetParent, &pResolvableWidgetParent->height);
return (dim->data / wFraction) * parentW;
}
case WD_Percentage:
{
struct UIWidget* pWidgetParent = FindResolveableDimensionAncestor(pWidget, &GetWidgetHeightDim);
float parentW = UI_ResolveHeightDimPxls(pWidgetParent, &pWidgetParent->height);
return dim->data * parentW;
}
default:
break;
}
return dim->data;
}
bool UI_ParseWidgetDockPoint(struct DataNode* pInNode, struct UIWidget* outWidget)
{
if(pInNode->fnGetPropType(pInNode, "dockPoint") == DN_String)
{
if (pInNode->fnStrCmp(pInNode, "dockPoint", "topLeft"))
{
outWidget->dockPoint = WDP_TopLeft;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "topMiddle"))
{
outWidget->dockPoint = WDP_TopMiddle;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "topRight"))
{
outWidget->dockPoint = WDP_TopRight;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "middleRight"))
{
outWidget->dockPoint = WDP_MiddleRight;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "bottomRight"))
{
outWidget->dockPoint = WDP_BottomRight;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "bottomMiddle"))
{
outWidget->dockPoint = WDP_BottomMiddle;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "bottomLeft"))
{
outWidget->dockPoint = WDP_BottomLeft;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "middleLeft"))
{
outWidget->dockPoint = WDP_MiddleLeft;
return true;
}
else if (pInNode->fnStrCmp(pInNode, "dockPoint", "centre"))
{
outWidget->dockPoint = WDP_Centre;
return true;
}
}
return false;
}
static void ParseLuaCallbacks(struct DataNode* pInNode, struct UIWidget* outWidget)
{
if(pInNode->fnGetPropType(pInNode, "onMouseEnter") == DN_String)
{
pInNode->fnGetStrcpy(pInNode, "onMouseEnter", outWidget->scriptCallbacks.Callbacks[WC_OnMouseEnter].name);
outWidget->scriptCallbacks.Callbacks[WC_OnMouseEnter].bActive = true;
}
if(pInNode->fnGetPropType(pInNode, "onMouseLeave") == DN_String)
{
pInNode->fnGetStrcpy(pInNode, "onMouseLeave", outWidget->scriptCallbacks.Callbacks[WC_OnMouseLeave].name);
outWidget->scriptCallbacks.Callbacks[WC_OnMouseLeave].bActive = true;
}
if(pInNode->fnGetPropType(pInNode, "onMouseMove") == DN_String)
{
pInNode->fnGetStrcpy(pInNode, "onMouseMove", outWidget->scriptCallbacks.Callbacks[WC_OnMouseMove].name);
outWidget->scriptCallbacks.Callbacks[WC_OnMouseMove].bActive = true;
}
if(pInNode->fnGetPropType(pInNode, "onMouseDown") == DN_String)
{
pInNode->fnGetStrcpy(pInNode, "onMouseDown", outWidget->scriptCallbacks.Callbacks[WC_OnMouseDown].name);
outWidget->scriptCallbacks.Callbacks[WC_OnMouseDown].bActive = true;
}
if(pInNode->fnGetPropType(pInNode, "onMouseUp") == DN_String)
{
pInNode->fnGetStrcpy(pInNode, "onMouseUp", outWidget->scriptCallbacks.Callbacks[WC_OnMouseUp].name);
outWidget->scriptCallbacks.Callbacks[WC_OnMouseUp].bActive = true;
}
}
static void ParseWidgetAlignments(struct DataNode* pInNode, struct UIWidget* outWidget)
{
outWidget->horizontalAlignment = WHA_Middle;
outWidget->verticalAlignment = WVA_Middle;
if(pInNode->fnGetPropType(pInNode, "horizontalAlignment") == DN_String)
{
char buffer[64];
pInNode->fnGetStrcpy(pInNode, "horizontalAlignment", buffer);
UI_ParseHorizontalAlignementAttribute(buffer, &outWidget->horizontalAlignment);
}
if(pInNode->fnGetPropType(pInNode, "verticalAlignment") == DN_String)
{
char buffer[64];
pInNode->fnGetStrcpy(pInNode, "verticalAlignment", buffer);
UI_ParseVerticalAlignementAttribute(buffer, &outWidget->verticalAlignment);
}
}
static void ParseWidgetDims(struct DataNode* pInNode, struct UIWidget* outWidget)
{
if(pInNode->fnGetPropType(pInNode, "width") == DN_String)
{
char buffer[64];
pInNode->fnGetStrcpy(pInNode, "width", buffer);
UI_ParseWidgetDimsAttribute(buffer, &outWidget->width);
}
if(pInNode->fnGetPropType(pInNode, "height") == DN_String)
{
char buffer[64];
pInNode->fnGetStrcpy(pInNode, "height", buffer);
UI_ParseWidgetDimsAttribute(buffer, &outWidget->height);
}
}
static void ParseWidgetOffsets(struct DataNode* pInNode, struct UIWidget* outWidget)
{
if(pInNode->fnGetPropType(pInNode, "x") == DN_Float)
{
outWidget->offsetX = pInNode->fnGetFloat(pInNode, "x");
}
if(pInNode->fnGetPropType(pInNode, "y") == DN_Float)
{
outWidget->offsetY = pInNode->fnGetFloat(pInNode, "y");
}
}
void UI_DefaultOnChildrenChanged(struct UIWidget* outWidget)
{
if (outWidget->hParent)
{
struct UIWidget* pWidget = UI_GetWidget(outWidget->hParent);
if (pWidget->fnOnWidgetChildrenChangedFn)
{
pWidget->fnOnWidgetChildrenChangedFn(pWidget);
}
}
}
void UI_WidgetCommonInit(struct DataNode* pInNode, struct UIWidget* outWidget)
{
if(!outWidget->fnOnWidgetChildrenChangedFn)
outWidget->fnOnWidgetChildrenChangedFn = UI_DefaultOnChildrenChanged;
UI_ParseWidgetPaddingAttributes(pInNode, &outWidget->padding);
UI_ParseWidgetDockPoint(pInNode, outWidget);
ParseLuaCallbacks(pInNode, outWidget);
ParseWidgetDims(pInNode, outWidget);
ParseWidgetOffsets(pInNode, outWidget);
ParseWidgetAlignments(pInNode, outWidget);
if(pInNode->fnGetPropType(pInNode, "childrenBinding") == DN_String)
{
struct WidgetPropertyBinding* pBinding = &outWidget->bindings[outWidget->numBindings++];
pBinding->type = WBT_WidgetTree;
pInNode->fnGetStrcpy(pInNode, "childrenBinding", pBinding->name);
strcpy(pBinding->boundPropertyName, "childrenBinding");
}
}
static const char* GetDockingPointName(WidgetDockPoint dockingPoint)
{
switch (dockingPoint)
{
case WDP_TopLeft: return "TopLeft";
case WDP_TopMiddle: return "TopMiddle";
case WDP_TopRight: return "TopRight";
case WDP_MiddleRight: return "MiddleRight";
case WDP_BottomRight: return "BottomRight";
case WDP_BottomMiddle: return "BottomMiddle";
case WDP_BottomLeft: return "BottomLeft";
case WDP_MiddleLeft: return "MiddleLeft";
default: return "Unknown";
}
}
void UI_DebugPrintCommonWidgetInfo(const struct UIWidget* inWidget, PrintfFn pPrintfFn)
{
pPrintfFn("paddingTop = %.1f, paddingBottom = %.1f, paddingLeft = %.1f, paddingRight = %.1f, left = %.1f, top = %.1f, dockPoint = %s",
inWidget->padding.paddingTop,
inWidget->padding.paddingBottom,
inWidget->padding.paddingLeft,
inWidget->padding.paddingRight,
inWidget->left,
inWidget->top,
GetDockingPointName(inWidget->dockPoint));
}
void* UI_Helper_OnOutputVerts(struct UIWidget* pWidget, VECTOR(WidgetVertex) pOutVerts)
{
HWidget child = pWidget->hFirstChild;
while (child != NULL_HWIDGET)
{
struct UIWidget* pChildWidget = UI_GetWidget(child);
if (pChildWidget->fnOutputVertices)
{
pOutVerts = pChildWidget->fnOutputVertices(pChildWidget, pOutVerts);
}
child = pChildWidget->hNext;
}
return pOutVerts;
}
void UI_Helper_OnLayoutChildren(struct UIWidget* pWidget, struct UIWidget* pParent)
{
if (pWidget->hFirstChild == NULL_HWIDGET)
{
return;
}
struct UIWidget* pChild = UI_GetWidget(pWidget->hFirstChild);
while (pChild)
{
pChild->fnLayoutChildren(pChild, pWidget);
pChild = UI_GetWidget(pChild->hNext);
}
}
void UI_SendWidgetMouseEvent(struct UIWidget* pWidget, enum WidgetCallbackTypes type, struct WidgetMouseInfo* pMouseInfo)
{
if (pWidget->cCallbacks.Callbacks[type].callback.bActive)
{
switch (type)
{
case WC_OnMouseLeave:
case WC_OnMouseEnter:
case WC_OnMouseMove:
pWidget->cCallbacks.Callbacks[type].callback.mousePosFn(pWidget, pMouseInfo->x, pMouseInfo->y);
break;
case WC_OnMouseDown:
case WC_OnMouseUp:
pWidget->cCallbacks.Callbacks[type].callback.mouseBtnFn(pWidget, pMouseInfo->x, pMouseInfo->y, pMouseInfo->button);
break;
}
}
if (!pWidget->scriptCallbacks.Callbacks[type].bActive)
{
return;
}
int numArguments = 0;
struct ScriptCallArgument arguments[8];
switch (type)
{
case WC_OnMouseLeave:
case WC_OnMouseEnter:
case WC_OnMouseMove:
{
struct ScriptCallArgument* pX = &arguments[numArguments++];
struct ScriptCallArgument* pY = &arguments[numArguments++];
pX->type = SCA_number;
pX->val.number = pMouseInfo->x;
pY->type = SCA_number;
pY->val.number = pMouseInfo->y;
}
break;
case WC_OnMouseDown:
case WC_OnMouseUp:
{
struct ScriptCallArgument* pX = &arguments[numArguments++];
struct ScriptCallArgument* pY = &arguments[numArguments++];
struct ScriptCallArgument* pMouseButton = &arguments[numArguments++];
pX->type = SCA_number;
pX->val.number = pMouseInfo->x;
pY->type = SCA_number;
pY->val.number = pMouseInfo->y;
pMouseButton->type = SCA_number;
pMouseButton->val.number = pMouseInfo->button;
}
break;
}
Sc_CallFuncInRegTableEntryTable(
pWidget->scriptCallbacks.viewmodelTable,
pWidget->scriptCallbacks.Callbacks[type].name,
&arguments[0],
numArguments,
0
);
}
void UI_GetWidgetSize(HWidget hWidget, float* pOutW, float* pOutH)
{
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(hWidget);
struct UIWidget* pWidget = UI_GetWidget(hWidget);
*pOutW = pWidget->fnGetWidth(pWidget, NULL);
*pOutH = pWidget->fnGetHeight(pWidget, NULL);
}
void UI_GetWidgetPadding(HWidget hWidget, float* pOutPaddingTop, float* pOutPaddingBottom, float* pOutPaddingLeft, float* pOutPaddingRight)
{
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(hWidget);
struct UIWidget* pWidget = UI_GetWidget(hWidget);
*pOutPaddingTop = pWidget->padding.paddingTop;
*pOutPaddingBottom = pWidget->padding.paddingBottom;
*pOutPaddingLeft = pWidget->padding.paddingLeft;
*pOutPaddingRight = pWidget->padding.paddingRight;
}
void UI_GetWidgetTopLeft(HWidget hWidget, float* pOutLeft, float* pOutTop)
{
WIDGET_POOL_BOUNDS_CHECK_NO_RETURN(hWidget);
struct UIWidget* pWidget = UI_GetWidget(hWidget);
*pOutLeft = pWidget->left;
*pOutTop = pWidget->top;
}
void UI_GetHitBox(GeomRect outRect, HWidget hWidget)
{
float padTop, padBot, padLeft, padRight;
float top, left;
float widgth, height;
UI_GetWidgetPadding(hWidget, &padTop, &padBot, &padLeft, &padRight);
UI_GetWidgetTopLeft(hWidget, &left, &top);
UI_GetWidgetSize(hWidget, &widgth, &height);
widgth -= padLeft + padRight;
height -= padTop + padBot;
outRect[GR_TLX] = left + padLeft;
outRect[GR_TLY] = top + padTop;
outRect[GR_BRX] = outRect[GR_TLX] + widgth;
outRect[GR_BRY] = outRect[GR_TLY] + height;
}
bool UI_IsAttributeStringABindingExpression(const char* attributeValue)
{
return attributeValue[0] == '{' && attributeValue[strlen(attributeValue) - 1] == '}';
}
char* UI_MakeBindingGetterFunctionName(const char* inBindingName)
{
char* fnName = malloc(strlen(inBindingName) + 1 + 4);
char* writePtr = fnName;
*writePtr++ = 'G';
*writePtr++ = 'e';
*writePtr++ = 't';
*writePtr++ = '_';
strcpy(writePtr, inBindingName);
return fnName;
}
char* UI_MakeBindingSetterFunctionName(const char* inBindingName)
{
char* fnName = malloc(strlen(inBindingName) + 1 + 4);
char* writePtr = fnName;
*writePtr++ = 'S';
*writePtr++ = 'e';
*writePtr++ = 't';
*writePtr++ = '_';
strcpy(writePtr, inBindingName);
return fnName;
}
struct WidgetPropertyBinding* UI_FindBinding(struct UIWidget* pWidget, const char* bindingName)
{
for (int i = 0; i < pWidget->numBindings; i++)
{
if (strcmp(pWidget->bindings[i].boundPropertyName, bindingName) == 0)
{
return &pWidget->bindings[i];
}
}
return NULL;
}
static char* PopulateBinding(struct WidgetPropertyBinding* pBinding, char* inBindingExpression, char* inBoundPropertyName)
{
inBindingExpression[strlen(inBindingExpression) - 1] = '\0';
inBindingExpression++;
strcpy(pBinding->name, inBindingExpression);
strcpy(pBinding->boundPropertyName, inBoundPropertyName);
return inBindingExpression;
}
void UI_AddStringPropertyBinding(struct UIWidget* pWidget, char* inBoundPropertyName, char* inBindingExpression, char** pOutData, int viewmodelTableIndex)
{
if (pWidget->numBindings >= MAX_NUM_BINDINGS)
{
printf("MAX_NUM_BINDINGS exceeded\n");
EASSERT(false);
return;
}
struct WidgetPropertyBinding* pBinding = &pWidget->bindings[pWidget->numBindings++];
inBindingExpression = PopulateBinding(pBinding, inBindingExpression, inBoundPropertyName);
pBinding->type = WBT_String;
pBinding->data.str = NULL;
char* fnName = UI_MakeBindingGetterFunctionName(inBindingExpression);
Sc_CallFuncInRegTableEntryTable(viewmodelTableIndex, fnName, NULL, 0, 1);
size_t strLen = Sc_StackTopStringLen();
*pOutData = malloc(strLen + 1);
Sc_StackTopStrCopy(*pOutData);
Sc_ResetStack();
free(fnName);
}
void UI_AddIntPropertyBinding(struct UIWidget* pWidget, char* inBoundPropertyName, char* inBindingExpression, int* pOutData, int viewmodelTableIndex)
{
if (pWidget->numBindings >= MAX_NUM_BINDINGS)
{
Log_Error("MAX_NUM_BINDINGS exceeded");
EASSERT(false);
return;
}
struct WidgetPropertyBinding* pBinding = &pWidget->bindings[pWidget->numBindings++];
inBindingExpression = PopulateBinding(pBinding, inBindingExpression, inBoundPropertyName);
pBinding->type = WBT_Int;
pBinding->data.str = NULL;
char* fnName = UI_MakeBindingGetterFunctionName(inBindingExpression);
Sc_CallFuncInRegTableEntryTable(viewmodelTableIndex, fnName, NULL, 0, 1);
int i = Sc_Int();
Sc_ResetStack();
*pOutData = i;
}
void UI_AddFloatPropertyBinding(struct UIWidget* pWidget, char* inBoundPropertyName, char* inBindingExpression, float* pOutData, int viewmodelTableIndex)
{
if (pWidget->numBindings >= MAX_NUM_BINDINGS)
{
Log_Error("MAX_NUM_BINDINGS exceeded");
EASSERT(false);
return;
}
struct WidgetPropertyBinding* pBinding = &pWidget->bindings[pWidget->numBindings++];
inBindingExpression = PopulateBinding(pBinding, inBindingExpression, inBoundPropertyName);
pBinding->type = WBT_Float;
pBinding->data.str = NULL;
char* fnName = UI_MakeBindingGetterFunctionName(inBindingExpression);
Sc_CallFuncInRegTableEntryTable(viewmodelTableIndex, fnName, NULL, 0, 1);
float i = Sc_Float();
Sc_ResetStack();
*pOutData = i;
}