//- sixten: Rows and columns. inline void UI_RowBegin(u32 Flags, string Name) { UI_SetNextLayoutAxis(Axis2_X); ui_box *Box = UI_MakeBox(Flags, Name); UI_PushParent(Box); } inline void UI_RowEnd(void) { UI_PopParent(); } inline void UI_ColumnBegin(u32 Flags, string Name) { UI_SetNextLayoutAxis(Axis2_Y); ui_box *Box = UI_MakeBox(Flags, Name); UI_PushParent(Box); } inline void UI_ColumnEnd(void) { UI_PopParent(); } //- sixten: Compositions inline void UI_PushAxisSize(axis2 Axis, ui_size Size) { if(Axis == Axis2_X) { UI_PushWidth(Size); } else { UI_PushHeight(Size); } } inline void UI_PopAxisSize(axis2 Axis) { if(Axis == Axis2_X) { UI_PopWidth(); } else { UI_PopHeight(); } } inline void UI_SetNextAxisSize(axis2 Axis, ui_size Size) { if(Axis == Axis2_X) { UI_SetNextWidth(Size); } else { UI_SetNextHeight(Size); } } //- sixten: Spacing static ui_box *UI_NamedSpacer(ui_size Size, string String) { ui_box *Parent = UI_TopParent(); UI_SetNextAxisSize(Parent->LayoutAxis, Size); ui_box *Box = UI_MakeBox(0, String); return(Box); } static ui_box *UI_NamedSpacerF(ui_size Size, char *Format, ...) { temporary_memory Scratch = GetScratch(0, 0); va_list Arguments; va_start(Arguments, Format); string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); va_end(Arguments); ui_box *Box = UI_NamedSpacer(Size, String); ReleaseScratch(Scratch); return(Box); } static void UI_Spacer(ui_size Size) { UI_NamedSpacer(Size, StrLit("")); } //- sixten: Scrollable regions //- sixten: Common widgets static ui_box *UI_Label(string String) { ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); return(Box); } static ui_box *UI_LabelF(char *Format, ...) { temporary_memory Scratch = GetScratch(0, 0); va_list Arguments; va_start(Arguments, Format); string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); va_end(Arguments); ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String); ReleaseScratch(Scratch); return(Box); } static ui_signal UI_Button(string String) { ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| UI_BoxFlag_DrawBackground| UI_BoxFlag_DrawBorder| UI_BoxFlag_HotAnimation| UI_BoxFlag_ActiveAnimation| UI_BoxFlag_Clickable, String); ui_signal Signal = UI_SignalFromBox(Box); return(Signal); } static ui_signal UI_ButtonF(char *Format, ...) { temporary_memory Scratch = GetScratch(0, 0); va_list Arguments; va_start(Arguments, Format); string String = PushFormatVariadic(Scratch.Arena, Format, Arguments); va_end(Arguments); ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText| UI_BoxFlag_DrawBackground| UI_BoxFlag_DrawBorder| UI_BoxFlag_HotAnimation| UI_BoxFlag_ActiveAnimation| UI_BoxFlag_Clickable, String); ui_signal Signal = UI_SignalFromBox(Box); ReleaseScratch(Scratch); return(Signal); } static ui_signal UI_Checkbox(b32 *Checked, string String) { UI_SetNextSize(UI_ChildrenSum(1, 1), UI_ChildrenSum(1, 1)); UI_SetNextLayoutAxis(Axis2_X); ui_box *ContainerBox = UI_MakeBox(UI_BoxFlag_Clickable, String); UI_Parent(ContainerBox) { r32 OpacityTransition = AnimationCurve_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked); v4 TextColor = UI_TopTextColor(); TextColor.a = OpacityTransition; UI_CornerRadius(2) UI_Size(UI_Em(1, 1), UI_Em(1, 1)) UI_Font(Font_Icons) UI_TextColor(TextColor) UI_MakeBoxF(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawText, "%U", FontIcon_Cancel); UI_Size(UI_TextContent(15, 1), UI_Em(1, 1)) UI_Label(String); } ui_signal Signal = UI_SignalFromBox(ContainerBox); if(Signal.Hovering) { Platform.SetCursor(PlatformCursor_Hand); } if(Signal.Pressed) { *Checked = !*Checked; } return(Signal); } //- sixten: Scrollable regions static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset) { ui_signal Signal; UI_SetNextAxisSize(Axis, UI_Percent(1, 0)); UI_SetNextAxisSize(Opposite(Axis), UI_Em(1, 1)); UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); UI_SetNextLayoutAxis(Axis); UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, Name)) { UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); UI_SetNextFont(Font_Icons); UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_UpDir:FontIcon_LeftDir); UI_Spacer(UI_Pixels(Offset, 1)); UI_SetNextAxisSize(Axis, UI_Pixels(Size, 1)); UI_SetNextAxisSize(Opposite(Axis), UI_Percent(1, 1)); Signal = UI_SignalFromBox(UI_MakeBox(UI_BoxFlag_DrawBorder | UI_BoxFlag_DrawBackground | UI_BoxFlag_Clickable | UI_BoxFlag_HotAnimation | UI_BoxFlag_ActiveAnimation, StrLit("Slider"))); UI_Spacer(UI_Percent(1, 0)); UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); UI_SetNextFont(Font_Icons); UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Axis?FontIcon_DownDir:FontIcon_RightDir); } return(Signal); } /* Goal for next week: Be able to make basic dialog trees. Goal for tomorrow: Scrollable regions and basic text editing (with line wrapping). */ static void UI_ScrollBegin(r32 *X, r32 *Y, string Name) { u32 Flags = 0; b32 AllowOnX = (X != 0); b32 AllowOnY = (Y != 0); UI_RowBegin(UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder, Name); { UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0)); UI_ColumnBegin(); { if(AllowOnX) { Flags |= UI_BoxFlag_OverflowX; UI_SetNextOffsetX(-(*X)); } if(AllowOnY) { Flags |= UI_BoxFlag_OverflowY; UI_SetNextOffsetY(-(*Y)); } UI_SetNextSize(AllowOnX?UI_ChildrenSum(1, 1):UI_Percent(1, 0), AllowOnY?UI_ChildrenSum(1, 1):UI_Percent(1, 0)); ui_box *ScrollableBox = UI_MakeBox(Flags, StrLit("Scrollable Box")); UI_PushParent(ScrollableBox); } } } static void UI_ScrollEnd(r32 *X, r32 *Y) { b32 AllowOnX = (X != 0); b32 AllowOnY = (Y != 0); ui_box *ScrollableBox = UI_TopParent(); { { UI_PopParent(); if(AllowOnX) { r32 TotalWidth = UI_CalculateBoxSize(ScrollableBox, Axis2_X); r32 ViewWidth = UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_X); if(ViewWidth / TotalWidth < 1) { r32 TotalScrollWidth = ViewWidth - ScrollableBox->FontSize*2; r32 ScrollScale = TotalScrollWidth / TotalWidth; // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. r32 ScrollWidth = ViewWidth*ScrollScale; r32 ScrollOffset = (*X)*ScrollScale; ui_signal Signal = UI_Scrollbar(Axis2_X, StrLit("Scroll X"), ScrollWidth, ScrollOffset); { if(Signal.Dragging) { if(Signal.Pressed) { UI_StoreDragR32(*X); } r32 StartOffset = UI_GetDragR32(); r32 EndOffset = StartOffset + Signal.DragDelta.x/ScrollScale; AnimationCurve_AnimateValueDirect(EndOffset, 0.2, X); } } } *X = Clamp(*X, 0, Max(0.0, TotalWidth - ViewWidth)); } } UI_ColumnEnd(); if(AllowOnY) { UI_SetNextSize(UI_ChildrenSum(1, 1), UI_Percent(1, 0)); UI_Column() { r32 TotalHeight = UI_CalculateBoxSize(ScrollableBox, Axis2_Y); r32 ViewHeight = UI_CalculateBoxSize(ScrollableBox->Parent->Parent, Axis2_Y); if(ViewHeight / TotalHeight < 1) { r32 TotalScrollHeight = ViewHeight - ScrollableBox->FontSize*2; r32 ScrollScale = TotalScrollHeight / TotalHeight; // sixten(TODO): Add a max of 1 em, and figure out the associated algebra. r32 ScrollHeight = ViewHeight*ScrollScale; r32 ScrollOffset = (*Y)*ScrollScale; ui_signal Signal = UI_Scrollbar(Axis2_Y, StrLit("Scroll Y"), ScrollHeight, ScrollOffset); { if(Signal.Dragging) { if(Signal.Pressed) { UI_StoreDragR32(*Y); } r32 StartOffset = UI_GetDragR32(); r32 EndOffset = StartOffset + Signal.DragDelta.y/ScrollScale; AnimationCurve_AnimateValueDirect(EndOffset, 0.2, Y); } } } *Y = Clamp(*Y, 0, Max(0.0, TotalHeight - ViewHeight)); } // sixten: Add padding if(AllowOnX && AllowOnY) { UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1)); UI_SetNextBackgroundColor(Darken(UI_TopBackgroundColor(), 2)); UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Padding")); } } } UI_RowEnd(); }