#ifndef UI_H #define UI_H #include "platform.h" #include "render.h" #include "../core.h" #include "GLFW/glfw3.h" enum UI_Flag { UI_Flag_WidthGrow=1<<0, // Default is fixed UI_Flag_HeightGrow=1<<1, // Default is fixed UI_Flag_Vertical=1<<2, // Default is horizontal UI_Flag_3DScene=1<<3, UI_Flag_Center=1<<4, UI_Flag_Ignore=1<<7, // For optional parameters UI_Flag_Pos_Absolute=1<<8, // Default is relative // .. UI_Flag_COUNT, }; typedef struct UI_Padding UI_Padding; struct UI_Padding { real32 top; real32 right; real32 bottom; real32 left; }; enum UI_Cursor { UI_Cursor_Arrow=1<<0, UI_Cursor_Pointer=1<<1, UI_Cursor_Forbidden=1<<2, UI_Cursor_Count=1<<3, }; enum UI_TxtAlign { UI_TxtAlign_Left=1<<0, // Default UI_TxtAlign_Center=1<<1, UI_TxtAlign_Right=1<<2, }; typedef struct UI_RectStr UI_RectStr; struct UI_RectStr { string s; real32 xOffset; real32 yOffset; real32 lineHeight; real32 fontSize; Vec4 color; enum UI_TxtAlign alignment; }; typedef struct UI_Rect UI_Rect; struct UI_Rect { enum UI_Flag flags; int32 parent; int32 firstChild; int32 lastChild; int32 nextSibling; real32 xOffset; real32 yOffset; real32 width; real32 minWidth; real32 maxWidth; real32 height; real32 minHeight; real32 maxHeight; real32 borderRadius; real32 borderThickness; Vec4 color; Vec4 borderColor; UI_Padding padding; real32 childGap; real32 x; real32 y; real32 resolvedWidth; real32 resolvedHeight; UI_RectStr *stringData; /** * Inherited from parent to propagate attributes. Actual string is ignored, only used for filling out defaults in * the next stringData pointer that is set. * This pointer is always set, as the global UI context will set it on the root node if it does not present its own * text data. */ UI_RectStr *inheritedTextAttr; }; DefineList(UI_Rect, UI_Rect); typedef struct UI_Context UI_Context; struct UI_Context { Arena *arena; UI_RectList prevRects; UI_RectList rects; int32 hotNode; int32 hoveredNode; int32 prevHoveredNode; int32 scene3DHandle; int32 prevHotNode; Renderer *renderer; OS_Input *prevInput; OS_Input *input; enum UI_Cursor cursorType; int32 rootRect; int32 currRect; }; void ui_begin(UI_Context *ui); void ui_end(UI_Context *ui); UI_Context ui_initContext(Arena *arena, Renderer *renderer); void ui_resolve(); void ui_rect(UI_Context *ui, UI_Rect rect); void ui_placeText(UI_Context *ui, UI_RectStr strData); void ui_attachTextAttr(UI_Context *ui, UI_Rect *rect, UI_RectStr strData); void ui_openElement(UI_Context *ui, UI_Rect rect); void ui_closeElement(UI_Context *ui); bool ui_CheckboxRect(UI_Context *ui, bool *value, UI_Rect rect); bool ui_Button(UI_Context *ui, UI_Rect rect, UI_RectStr textAttr); bool ui_ButtonWithHover(UI_Context *ui, UI_Rect rect, UI_Rect hovered, UI_RectStr textAttr); UI_Rect ui_HoverRect(UI_Context *ui, UI_Rect rect, UI_Rect hovered); extern UI_Context *__UI_current_ctx__; #if 0 #define UI_DEBUG_ATTR .borderColor=COLOR_RED, .borderThickness=1, #else #define UI_DEBUG_ATTR #endif #define UI_Pass(ui) DeferLoop(ui_begin((ui)), ui_end((ui))) #define UI_PadUniform(padding) ((UI_Padding){ (padding), (padding), (padding), (padding) }) #define UI_NextID() ((__UI_current_ctx__)->rects.length) #define UI(...)\ /** Place a UI_Rect defined in-place in the current position in the UI tree */\ DeferLoop(ui_openElement((__UI_current_ctx__), UI_RectAttr(__VA_ARGS__)), ui_closeElement((__UI_current_ctx__))) #define UI_RectAttr(...)\ /** Generate a UI_Rect to be passed as a value */\ ((UI_Rect){UI_DEBUG_ATTR .minHeight=-1, .minWidth=-1, .width=-1, .height=-1, .inheritedTextAttr=(__UI_current_ctx__->rects.data[(__UI_current_ctx__)->currRect].inheritedTextAttr), __VA_ARGS__}) #define UI_FromRect(rect)\ /** Place a UI_Rect in the current position in the UI tree, as defined by an existing value */\ DeferLoop(ui_openElement((__UI_current_ctx__), (rect)), ui_closeElement((__UI_current_ctx__))) #define UI_TxtAttrS(str, ...)\ /** Generate UI text to be used as a value */\ ((UI_RectStr){ .color={-1,-1,-1,-1}, .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=(str), __VA_ARGS__ }) #define UI_TxtAttr(...)\ /** Generate UI text to be used as a value, attributes only, no string */\ ((UI_RectStr){ .color={-1,-1,-1,-1}, .alignment=-1, .xOffset=0, .yOffset=0, .fontSize=-1, .lineHeight=-1, .s=((string){.str=NULL,.length=0}), __VA_ARGS__ }) #define UI_Txt(str, ...)\ /** Place UI text in the current position in the UI tree. Attaches to the current UI_Rect */\ (ui_placeText((__UI_current_ctx__), (UI_TxtAttrS(str, __VA_ARGS__)))) #define UI_AttachTxtAttr(rect, txtAttr)\ /** Attach "phantom" text to the specified rect in order to set its heritable attributes for subsequent text in the UI subtrees */\ (ui_attachTextAttr((__UI_current_ctx__), (rect), (txtAttr))) #define UI_SetTxtAttr(...)\ /** Attach "phantom" text to the current rect in order to set its heritable attributes for subsequent text in the UI subtrees */\ (ui_placeText((__UI_current_ctx__), (UI_TxtAttr(__VA_ARGS__)))) #define UI_TxtSeg(str, ...)\ /** Place a text fragment in its own box (useful for placing many text fragments in a row */\ UI() (UI_Txt((str), __VA_ARGS__)) #endif