2023-06-27 14:14:28 +00:00
|
|
|
//- sixten: Rows and columns.
|
|
|
|
inline void UI_RowBegin(u32 Flags, string Name)
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_SetNextLayoutAxis(Axis2_X);
|
|
|
|
ui_box *Box = UI_MakeBox(Flags, Name);
|
|
|
|
UI_PushParent(Box);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void UI_RowEnd(void)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_PopParent();
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
inline void UI_ColumnBegin(u32 Flags, string Name)
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_SetNextLayoutAxis(Axis2_Y);
|
|
|
|
ui_box *Box = UI_MakeBox(Flags, Name);
|
|
|
|
UI_PushParent(Box);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void UI_ColumnEnd(void)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_PopParent();
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
//- sixten: Compositions
|
2023-06-17 17:00:55 +00:00
|
|
|
inline void UI_PushAxisSize(axis2 Axis, ui_size Size)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
if(Axis == Axis2_X)
|
|
|
|
{
|
|
|
|
UI_PushWidth(Size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UI_PushHeight(Size);
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void UI_PopAxisSize(axis2 Axis)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
if(Axis == Axis2_X)
|
|
|
|
{
|
|
|
|
UI_PopWidth();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UI_PopHeight();
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void UI_SetNextAxisSize(axis2 Axis, ui_size Size)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
if(Axis == Axis2_X)
|
|
|
|
{
|
|
|
|
UI_SetNextWidth(Size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
UI_SetNextHeight(Size);
|
|
|
|
}
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
//- sixten: Spacing
|
2023-06-17 17:00:55 +00:00
|
|
|
static ui_box *UI_NamedSpacer(ui_size Size, string String)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
ui_box *Parent = UI_TopParent();
|
|
|
|
UI_SetNextAxisSize(Parent->LayoutAxis, Size);
|
|
|
|
|
|
|
|
ui_box *Box = UI_MakeBox(0, String);
|
|
|
|
return(Box);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ui_box *UI_NamedSpacerF(ui_size Size, char *Format, ...)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
temp 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);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static ui_box *UI_Spacer(ui_size Size)
|
2023-06-17 17:00:55 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
return(UI_NamedSpacer(Size, StrLit("")));
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
//- sixten: Scrollable regions
|
|
|
|
|
2023-06-17 17:00:55 +00:00
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
//- sixten: Common widgets
|
2023-06-17 17:00:55 +00:00
|
|
|
static ui_box *UI_Label(string String)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText, String);
|
|
|
|
return(Box);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ui_box *UI_LabelF(char *Format, ...)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
temp 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);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ui_signal UI_Button(string String)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
|
|
|
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawText|
|
2023-12-26 08:11:41 +00:00
|
|
|
UI_BoxFlag_DrawBackground|
|
|
|
|
UI_BoxFlag_DrawBorder|
|
|
|
|
UI_BoxFlag_HotAnimation|
|
|
|
|
UI_BoxFlag_ActiveAnimation|
|
|
|
|
UI_BoxFlag_Clickable,
|
|
|
|
String);
|
2023-12-23 07:27:22 +00:00
|
|
|
ui_signal Signal = UI_SignalFromBox(Box);
|
|
|
|
return(Signal);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static ui_signal UI_ButtonF(char *Format, ...)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
temp Scratch = GetScratch(0, 0);
|
|
|
|
|
|
|
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
|
|
|
|
|
|
|
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|
|
2023-12-26 08:11:41 +00:00
|
|
|
UI_BoxFlag_DrawBackground|
|
|
|
|
UI_BoxFlag_DrawBorder|
|
|
|
|
UI_BoxFlag_HotAnimation|
|
|
|
|
UI_BoxFlag_ActiveAnimation|
|
|
|
|
UI_BoxFlag_Clickable,
|
|
|
|
String);
|
2023-12-23 07:27:22 +00:00
|
|
|
ui_signal Signal = UI_SignalFromBox(Box);
|
|
|
|
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
|
|
|
|
return(Signal);
|
2023-06-19 17:12:26 +00:00
|
|
|
}
|
|
|
|
|
2023-06-27 14:14:28 +00:00
|
|
|
static ui_signal UI_Checkbox(b32 *Checked, string String)
|
2023-06-19 17:12:26 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_SetNextSize(UI_ChildrenSum(1, 1), UI_ChildrenSum(1, 1));
|
|
|
|
UI_SetNextLayoutAxis(Axis2_X);
|
|
|
|
UI_SetNextHoverCursor(PlatformCursor_Hand);
|
|
|
|
ui_box *ContainerBox = UI_MakeBox(UI_BoxFlag_Clickable, String);
|
|
|
|
UI_Parent(ContainerBox)
|
|
|
|
{
|
|
|
|
r32 OpacityTransition = AC_AnimateValueF(*Checked, *Checked, 0.15, "UI Checkbox Transition %p", Checked);
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
v4_r32 TextColor = UI_TopTextColor();
|
2023-12-23 07:27:22 +00:00
|
|
|
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.Pressed)
|
|
|
|
{
|
|
|
|
*Checked = !*Checked;
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Signal);
|
2023-06-27 14:14:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//- sixten: Scrollable regions
|
|
|
|
static ui_signal UI_Scrollbar(axis2 Axis, string Name, r32 Size, r32 Offset)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
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);
|
|
|
|
|
2023-12-26 08:11:41 +00:00
|
|
|
Offset = Clamp(Offset, 0, DimOfRange(UI_TopParent()->Rect).E[Axis]-Size-UI_TopFontSize()*2);
|
|
|
|
|
|
|
|
UI_Spacer(UI_Pixels(Offset, 1));
|
|
|
|
|
|
|
|
UI_SetNextCornerRadius(4.0f);
|
|
|
|
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);
|
2023-12-23 07:27:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return(Signal);
|
2023-06-27 14:14:28 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static void UI_ScrollBegin(r32 *X, r32 *Y, ui_box_flags Flags, string Name)
|
2023-06-27 14:14:28 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
b32 AllowOnX = (X != 0);
|
|
|
|
b32 AllowOnY = (Y != 0);
|
|
|
|
|
|
|
|
UI_RowBegin(Flags, Name);
|
|
|
|
{
|
|
|
|
UI_SetNextSize(UI_Percent(1, 0), UI_Percent(1, 0));
|
|
|
|
UI_ColumnBegin();
|
|
|
|
{
|
|
|
|
u32 ScrollFlags = 0;
|
|
|
|
if(AllowOnX)
|
|
|
|
{
|
|
|
|
ScrollFlags |= UI_BoxFlag_OverflowX;
|
|
|
|
UI_SetNextOffsetX(-AC_AnimateValueF(*X, *X, 0.2f, "Scroll %S X", Name));
|
|
|
|
}
|
|
|
|
|
|
|
|
if(AllowOnY)
|
|
|
|
{
|
|
|
|
ScrollFlags |= UI_BoxFlag_OverflowY;
|
|
|
|
UI_SetNextOffsetY(-AC_AnimateValueF(*Y, *Y, 0.2f, "Scroll %S Y", Name));
|
|
|
|
}
|
|
|
|
|
|
|
|
UI_SetNextSize(AllowOnX?UI_ChildrenSum(1, 1):UI_Percent(1, 0),
|
2023-12-26 08:11:41 +00:00
|
|
|
AllowOnY?UI_ChildrenSum(1, 1):UI_Percent(1, 0));
|
2023-12-23 07:27:22 +00:00
|
|
|
|
|
|
|
ui_box *ScrollableBox = UI_MakeBox(ScrollFlags, StrLit("Scrollable Box"));
|
|
|
|
|
|
|
|
UI_PushParent(ScrollableBox);
|
|
|
|
}
|
|
|
|
}
|
2023-06-27 14:14:28 +00:00
|
|
|
}
|
|
|
|
|
2023-08-22 03:19:51 +00:00
|
|
|
static void UI_ScrollEnd(r32 *X, r32 *Y, ui_box_flags Flags, string Name)
|
2023-06-27 14:14:28 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
b32 AllowOnX = (X != 0);
|
|
|
|
b32 AllowOnY = (Y != 0);
|
|
|
|
|
|
|
|
r32 ScrollAmount = UI_TopFontSize()*4.0f;
|
|
|
|
|
|
|
|
ui_box *ScrollableBox = UI_TopParent();
|
|
|
|
ui_signal ScrollableBoxSignal = UI_SignalFromBox(ScrollableBox);
|
|
|
|
{
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
*X = EndOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(platform_event *Event = UI_EventList()->First;
|
2023-12-26 08:11:41 +00:00
|
|
|
Event != 0;
|
|
|
|
Event = Event->Next)
|
2023-12-23 07:27:22 +00:00
|
|
|
{
|
|
|
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.x != 0)
|
|
|
|
{
|
|
|
|
*X -= Event->Scroll.x*ScrollAmount;
|
|
|
|
Platform_ConsumeEvent(UI_EventList(), Event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*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 = DimOfRange(ScrollableBox->Parent->Parent->Rect).y;//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;
|
|
|
|
|
|
|
|
*Y = EndOffset;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(platform_event *Event = UI_EventList()->First;
|
2023-12-26 08:11:41 +00:00
|
|
|
Event != 0;
|
|
|
|
Event = Event->Next)
|
2023-12-23 07:27:22 +00:00
|
|
|
{
|
|
|
|
if(Event->Type == PlatformEvent_MouseScroll && Event->Scroll.y != 0)
|
|
|
|
{
|
|
|
|
*Y -= Event->Scroll.y*ScrollAmount;
|
|
|
|
Platform_ConsumeEvent(UI_EventList(), Event);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*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();
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static r32 UI_Slider(r32 Value, range1_r32 Range)
|
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
r32 Result = Value;
|
|
|
|
UI_Column()
|
|
|
|
{
|
|
|
|
UI_Spacer(UI_Em(1, 1));
|
|
|
|
UI_Height(UI_Em(1, 1)) UI_Row()
|
|
|
|
{
|
|
|
|
UI_SetNextWidth(UI_Em(20, 1));
|
|
|
|
UI_SetNextCornerRadius(UI_TopFontSize()*0.5f);
|
|
|
|
ui_box *Container = UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Scrollable");
|
|
|
|
UI_Parent(Container)
|
|
|
|
{
|
|
|
|
UI_SetNextCornerRadius(UI_TopFontSize()*0.5f);
|
|
|
|
UI_SetNextSize(UI_Em(1, 1), UI_Em(1, 1));
|
|
|
|
UI_SetNextFixedX((DimOfRange(Container->Rect).x-UI_TopFontSize())*(Value-Range.Min)/DimOfRange(Range));
|
|
|
|
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground|
|
2023-12-26 08:11:41 +00:00
|
|
|
UI_BoxFlag_DrawBorder|
|
|
|
|
UI_BoxFlag_HotAnimation|
|
|
|
|
UI_BoxFlag_ActiveAnimation|
|
|
|
|
UI_BoxFlag_Clickable|
|
|
|
|
UI_BoxFlag_FloatingX|
|
|
|
|
0, "Dragable");
|
2023-12-23 07:27:22 +00:00
|
|
|
ui_signal Signal = UI_SignalFromBox(Box);
|
|
|
|
if(Signal.Dragging)
|
|
|
|
{
|
|
|
|
if(Signal.Pressed)
|
|
|
|
{
|
|
|
|
UI_StoreDragR32(Value);
|
|
|
|
}
|
|
|
|
|
|
|
|
r32 StartT = UI_GetDragR32();
|
|
|
|
r32 EndT = StartT + Signal.DragDelta.x/(DimOfRange(Container->Rect).x-UI_TopFontSize());
|
|
|
|
Result = EndT*DimOfRange(Range)+Range.Min;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(Result);
|
2023-08-22 03:19:51 +00:00
|
|
|
}
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
static void UI_TooltipLabel(string Label, v2_r32 P)
|
2023-08-22 03:19:51 +00:00
|
|
|
{
|
2023-12-23 07:27:22 +00:00
|
|
|
UI_Tooltip
|
|
|
|
{
|
|
|
|
UI_SetNextSize(UI_TextContent(7, 1), UI_TextContent(3, 1));
|
|
|
|
UI_CornerRadius(4);
|
|
|
|
UI_SetNextFixedP(P+V2R32(15.0f, 0.0f));
|
|
|
|
UI_MakeBox(UI_BoxFlag_DrawBorder |
|
2023-12-26 08:11:41 +00:00
|
|
|
UI_BoxFlag_DrawBackground |
|
|
|
|
UI_BoxFlag_DrawText |
|
|
|
|
UI_BoxFlag_DrawDropShadow |
|
|
|
|
UI_BoxFlag_FloatingX |
|
|
|
|
UI_BoxFlag_FloatingY,
|
|
|
|
Label);
|
2023-12-23 07:27:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ui_line_edit_callback_data
|
|
|
|
{
|
|
|
|
text_edit_state State;
|
|
|
|
b32 Focus;
|
|
|
|
};
|
|
|
|
|
|
|
|
UI_CUSTOM_DRAW_CALLBACK(UI_LineEditCallback)
|
|
|
|
{
|
|
|
|
ui_line_edit_callback_data *EditData = (ui_line_edit_callback_data *)Data;
|
|
|
|
s64 ClampedCursor = Clamp(0, Box->String.Count, EditData->State.Cursor);
|
|
|
|
s64 ClampedMark = Clamp(0, Box->String.Count, EditData->State.Mark);
|
|
|
|
string ToCursor = MakeString(Box->String.Data, ClampedCursor);
|
|
|
|
string ToMark = MakeString(Box->String.Data, ClampedMark);
|
|
|
|
|
|
|
|
r32 TargetCursorX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToCursor);
|
|
|
|
r32 TargetMarkerX = CalculateRasterizedTextWidth(Atlas, Box->Font, Box->FontSize, ToMark);
|
|
|
|
|
|
|
|
r32 CursorX = AC_AnimateValueF(TargetCursorX, 0, 0.175, "UI Input Cursor %p", Box);
|
|
|
|
r32 MarkerX = AC_AnimateValueF(TargetMarkerX, 0, 0.175, "UI Input Mark %p", Box);
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
v2_r32 BoxDim = DimOfRange(Box->Rect);
|
2023-12-23 07:27:22 +00:00
|
|
|
|
|
|
|
// sixten: Draw selection
|
|
|
|
{
|
2024-01-20 11:18:57 +00:00
|
|
|
v2_r32 Offset = V2R32(7.5, (BoxDim.y - Box->FontSize) / 2);
|
|
|
|
v2_r32 Dim = V2R32(0, Box->FontSize);
|
2023-12-23 07:27:22 +00:00
|
|
|
if(CursorX > MarkerX)
|
|
|
|
{
|
|
|
|
Offset.x += MarkerX;
|
|
|
|
Dim.x = CursorX - MarkerX;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Offset.x += CursorX;
|
|
|
|
Dim.x = MarkerX - CursorX;
|
|
|
|
}
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
v2_r32 P = Box->Rect.Min + Offset;
|
|
|
|
v4_r32 Color = V4R32(0.4, 0.7, 0.8, 0.3);
|
2023-12-23 07:27:22 +00:00
|
|
|
PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// sixten: Draw cursor
|
|
|
|
{
|
|
|
|
range1_r32 CursorSpan = Range1R32(CursorX, TargetCursorX);
|
|
|
|
r32 Height = Box->FontSize + 4;
|
2024-01-20 11:18:57 +00:00
|
|
|
v2_r32 Offset = V2R32(7.5F + CursorSpan.Min, (BoxDim.y - Height) / 2);
|
|
|
|
v2_r32 Dim = V2R32(1.25F + CursorSpan.Max - CursorSpan.Min, Height);
|
2023-12-23 07:27:22 +00:00
|
|
|
r32 FocusT = AC_AnimateValueF(EditData->Focus, 0, 0.175, "UI Input Focus %p", Box);
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
v2_r32 P = Box->Rect.Min + Offset;
|
|
|
|
v4_r32 Color = SetAlpha(V4R32(0.3, 1, 0.3, 0.7), UI_Blink()*FocusT);
|
2023-12-23 07:27:22 +00:00
|
|
|
PushQuad(Group, Range2R32(P, P+Dim), Color, 0, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// sixten(NOTE): this function only kinda works. it is meant for single line text, if that is any indication of its scope.
|
|
|
|
static s64 UI_TextIndexFromP(ui_box *Box, v2_r32 P)
|
|
|
|
{
|
|
|
|
// sixten: yes, usually I don't like doing this, but hey there is only one glyph atlas used throughout the entire application, nobody will notice ._.
|
|
|
|
glyph_atlas *Atlas = UI_GetState()->GlyphAtlas;
|
|
|
|
|
|
|
|
s64 Result = 0;
|
|
|
|
// sixten(NOTE): from now on, we ignore the y-axis
|
|
|
|
r32 Dim = CalculateRasterizedTextWidth(UI_GetState()->GlyphAtlas, Box->Font, Box->FontSize, Box->String);
|
|
|
|
r32 Offset = P.x - Box->Rect.Min.x - (Box->Rect.Max.x - Box->Rect.Min.x - Dim)*0.5;
|
|
|
|
|
|
|
|
r32 Advance = 0;
|
|
|
|
u8 *TextBegin = Box->String.Data;
|
|
|
|
u8 *TextEnd = TextBegin+Box->String.Count;
|
|
|
|
for(u8 *Byte = TextBegin; Byte < TextEnd;)
|
|
|
|
{
|
|
|
|
string_decode Decode = DecodeUTF8Codepoint(Byte, TextEnd-Byte);
|
|
|
|
Byte += Decode.Size;
|
|
|
|
u32 Codepoint = Decode.Codepoint;
|
|
|
|
|
2024-01-20 11:18:57 +00:00
|
|
|
glyph *Glyph = F_GlyphFromAtlas(Atlas, Box->Font, Codepoint, Box->FontSize*Font_Oversample, F_SubpixelSegmentFromP(Advance*Font_Oversample));
|
2023-12-23 07:27:22 +00:00
|
|
|
Assert(Glyph);
|
|
|
|
|
|
|
|
Advance += Glyph->Advance/Font_Oversample;
|
|
|
|
if(Advance > Offset)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Result += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(Result);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ui_signal UI_LineEdit(text_edit_state *State, u64 BufferSize, u8 *Buffer, s64 *BufferUsed, string String, b32 Focused)
|
|
|
|
{
|
|
|
|
temp Scratch = GetScratch();
|
|
|
|
if(Focused)
|
|
|
|
{
|
|
|
|
for(platform_event *Event = UI_EventList()->First; Event != 0; Event = Event->Next)
|
|
|
|
{
|
|
|
|
if((Event->Type == PlatformEvent_Press || Event->Type == PlatformEvent_Text) && (Event->Codepoint != '/' && Event->Codepoint != '\\'))
|
|
|
|
{
|
|
|
|
text_action Action = SingleLineTextActionFromEvent(Event);
|
|
|
|
if(IsValid(&Action))
|
|
|
|
{
|
|
|
|
text_op Op = TextOpFromAction(Scratch.Arena, MakeString(Buffer, *BufferUsed), State, &Action);
|
|
|
|
if(Op.NewCursor >= 0 && Op.NewMark >= 0)
|
|
|
|
{
|
|
|
|
string Left = MakeString(Buffer, Op.Range.Min);
|
|
|
|
string Right = MakeString(Buffer + Op.Range.Max, *BufferUsed - Op.Range.Max);
|
|
|
|
|
|
|
|
u64 NewStringSize = Left.Count + Right.Count + Op.ReplaceString.Count;
|
|
|
|
char *NewString = PushArray(Scratch.Arena, char, NewStringSize);
|
|
|
|
Copy(NewString, Left.Data, Left.Count);
|
|
|
|
Copy(NewString + Left.Count, Op.ReplaceString.Data, Op.ReplaceString.Count);
|
|
|
|
Copy(NewString + Left.Count + Op.ReplaceString.Count, Right.Data, Right.Count);
|
|
|
|
*BufferUsed = Minimum(BufferSize, NewStringSize);
|
|
|
|
Copy(Buffer, NewString, *BufferUsed);
|
|
|
|
|
|
|
|
State->Cursor = Minimum(Op.NewCursor, *BufferUsed);
|
|
|
|
State->Mark = Minimum(Op.NewMark, *BufferUsed);
|
|
|
|
Platform_ConsumeEvent(UI_EventList(), Event);
|
|
|
|
|
|
|
|
UI_ResetBlink();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
UI_SetNextHoverCursor(PlatformCursor_IBeam);
|
|
|
|
ui_box *Container = UI_MakeBox(UI_BoxFlag_Clip|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground|UI_BoxFlag_Clickable, String);
|
|
|
|
ui_box *InputBox = 0;
|
|
|
|
|
|
|
|
UI_Parent(Container) UI_Height(UI_Percent(1, 1))
|
|
|
|
{
|
|
|
|
UI_SetNextWidth(UI_TextContent(15, 1));
|
|
|
|
InputBox = UI_MakeBox(UI_BoxFlag_DrawText, StrLit("Text Input"));
|
|
|
|
UI_EquipBoxText(InputBox, MakeString(Buffer, *BufferUsed));
|
|
|
|
ui_line_edit_callback_data *Data = PushStruct(UI_FrameArena(), ui_line_edit_callback_data);
|
|
|
|
Data->State = *State;
|
|
|
|
Data->Focus = Focused;
|
|
|
|
UI_EquipBoxCustomDrawCallback(InputBox, UI_LineEditCallback, Data);
|
|
|
|
|
|
|
|
UI_Spacer(UI_Percent(1, 0));
|
|
|
|
}
|
|
|
|
ReleaseScratch(Scratch);
|
|
|
|
|
|
|
|
//- sixten: handle mouse dragging
|
|
|
|
ui_signal Signal = UI_SignalFromBox(Container);
|
|
|
|
if(Signal.Dragging)
|
|
|
|
{
|
|
|
|
if(Signal.Pressed)
|
|
|
|
{
|
|
|
|
State->Cursor = State->Mark = UI_TextIndexFromP(InputBox, UI_MouseP());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
State->Cursor = UI_TextIndexFromP(InputBox, UI_MouseP());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return(Signal);
|
2023-06-17 17:00:55 +00:00
|
|
|
}
|