Implemented variable sized row specifications

This commit is contained in:
PS 2020-11-15 14:48:04 -08:00
parent 50b6980bec
commit 296472a588
6 changed files with 410 additions and 175 deletions

View File

@ -200,39 +200,86 @@ TestRender(app_state* State, context* Context, render_command_buffer* RenderBuff
{ {
ui_InterfaceReset(&State->Interface); ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer; State->Interface.RenderBuffer = RenderBuffer;
State->Interface.WindowBounds = Context->WindowBounds;
ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown, MakeString("TestRender Layout")); gs_string A = MakeString("TestRender Layout");
ui_widget_id Ids[2]; ui_PushLayout(&State->Interface, A);
{
#if 1
ui_column_spec ColumnRules[] = {
{ UIColumnSize_Fixed, 128 },
{ UIColumnSize_Fill, 0 },
{ UIColumnSize_Percent, .5f }
};
ui_BeginRow(&State->Interface, 3, ColumnRules);
gs_string String = MakeString("Select");
ui_StartRow(&State->Interface, 2);
for (u32 j = 0; j < 2; j++)
{ {
if (ui_BeginDropdown(&State->Interface, String)) ui_Button(&State->Interface, MakeString("B"));
ui_Button(&State->Interface, MakeString("B"));
ui_Button(&State->Interface, MakeString("B"));
}
ui_EndRow(&State->Interface);
ui_Button(&State->Interface, MakeString("B"));
ui_Button(&State->Interface, MakeString("C"));
#elif 0
ui_PushLayout(&State->Interface, MakeString("Outer"));
{ {
for (u32 i = 0; i < State->PanelSystem.PanelDefsCount; i++) for (u32 i = 0; i < 3; i++)
{ {
panel_definition Def = State->PanelSystem.PanelDefs[i]; ui_Button(&State->Interface, MakeString("A"));
gs_string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
if (ui_Button(&State->Interface, DefName))
{
} }
} }
ui_PopLayout(&State->Interface);
ui_BeginRow(&State->Interface, 2);
{
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
for (u32 i = 0; i < 5; i++)
{
ui_Button(&State->Interface, MakeString("TestButon"));
}
}
ui_PopLayout(&State->Interface);
ui_PushLayout(&State->Interface, MakeString("TestLayout"));
{
ui_Button(&State->Interface, MakeString("TestButon"));
TestToggle = ui_Toggle(&State->Interface, MakeString("Toggle"), TestToggle);
TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
if (ui_BeginDropdown(&State->Interface, MakeString("TestDropdown")))
{
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
ui_Button(&State->Interface, MakeString("TestButon"));
} }
ui_EndDropdown(&State->Interface); ui_EndDropdown(&State->Interface);
} }
ui_EndRow(&State->Interface);
TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("Test Slider"), TestSlider_Value, TestSlider_Min, TestSlider_Max);
TestToggle = ui_Toggle(&State->Interface, MakeString("test toggle"), TestToggle);
ui_Button(&State->Interface, MakeString("Hello"));
ui_PopLayout(&State->Interface); ui_PopLayout(&State->Interface);
}
ui_EndRow(&State->Interface);
Assert(!ui_WidgetIdsEqual(Ids[0], Ids[1])); ui_PushLayout(&State->Interface, MakeString("Outer"));
{
for (u32 i = 0; i < 3; i++)
{
ui_Button(&State->Interface, MakeString("B"));
}
}
ui_PopLayout(&State->Interface);
#else
ui_BeginList(&State->Interface, MakeString("Test List"), 10);
{
for (u32 i = 0; i < 32; i++)
{
ui_Button(&State->Interface, MakeString("Option"));
}
}
ui_EndList(&State->Interface);
#endif
}
ui_PopLayout(&State->Interface);
} }
internal void internal void
@ -243,9 +290,6 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB
#if 0 #if 0
TestRender(State, Context, RenderBuffer); TestRender(State, Context, RenderBuffer);
//ui_widget_id IdTwo = TestRender(State, Context, RenderBuffer);
//Assert(ui_WidgetIdsEqual(IdOne, IdTwo));
#else #else
ui_InterfaceReset(&State->Interface); ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer; State->Interface.RenderBuffer = RenderBuffer;

View File

@ -608,7 +608,7 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan
ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("PlayBar Layout")); ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown, MakeString("PlayBar Layout"));
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]); ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
ui_StartRow(&State->Interface, 4); ui_BeginRow(&State->Interface, 4);
{ {
if (ui_Button(Interface, MakeString("Pause"))) if (ui_Button(Interface, MakeString("Pause")))
{ {
@ -796,7 +796,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, rende
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]);
ui_StartRow(&State->Interface, 2); ui_BeginRow(&State->Interface, 2);
{ {
ui_Label(Interface, MakeString("Active Animation")); ui_Label(Interface, MakeString("Active Animation"));
if (ui_BeginDropdown(Interface, ActiveAnim->Name)) if (ui_BeginDropdown(Interface, ActiveAnim->Name))

View File

@ -52,7 +52,6 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
// Fill in alternating color rows for the backgrounds // Fill in alternating color rows for the backgrounds
for (u32 Line = 0; Line < LineCount; Line++) for (u32 Line = 0; Line < LineCount; Line++)
{ {
//LineBounds[Line] = ui_ReserveElementBounds(Layout);
v4 ListItemBGColor = ui_GetListItemBGColor(State->Interface.Style, Line); v4 ListItemBGColor = ui_GetListItemBGColor(State->Interface.Style, Line);
ui_FillRect(&State->Interface, LineBounds[Line], ListItemBGColor); ui_FillRect(&State->Interface, LineBounds[Line], ListItemBGColor);
} }

View File

@ -94,8 +94,13 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, deb
char Backbuffer[256]; char Backbuffer[256];
gs_string String = MakeString(Backbuffer, 0, 256); gs_string String = MakeString(Backbuffer, 0, 256);
r32 ColumnWidths[] = {256, 128, 128, 128, 128}; ui_column_spec ColumnWidths[] = {
ui_StartRow(Interface, 5, &ColumnWidths[0]); { UIColumnSize_Fixed, 256 },
{ UIColumnSize_Fixed, 128 },
{ UIColumnSize_Fixed, 128 },
{ UIColumnSize_Fixed, 128 },
{ UIColumnSize_Fixed, 128 }};
ui_BeginRow(Interface, 5, &ColumnWidths[0]);
{ {
ui_Label(Interface, MakeString("Procedure")); ui_Label(Interface, MakeString("Procedure"));
ui_Label(Interface, MakeString("% Frame")); ui_Label(Interface, MakeString("% Frame"));
@ -112,7 +117,7 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, deb
{ {
collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n;
ui_StartRow(Interface, 5, &ColumnWidths[0]); ui_BeginRow(Interface, 5, &ColumnWidths[0]);
{ {
PrintF(&String, "%S", NameEntry.Name); PrintF(&String, "%S", NameEntry.Name);
ui_Label(Interface, String); ui_Label(Interface, String);
@ -181,7 +186,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout")); ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown, MakeString("Profiler Layout"));
ui_StartRow(&State->Interface, 4); ui_BeginRow(&State->Interface, 4);
{ {
s64 FrameStartCycles = VisibleFrame->FrameStartCycles; s64 FrameStartCycles = VisibleFrame->FrameStartCycles;
s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles;
@ -194,7 +199,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
// NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could
// be removed, or used for something else // be removed, or used for something else
ui_ReserveElementBounds(Layout); ui_ReserveBounds(Layout, true);
if (ui_Button(&State->Interface, MakeString("Resume Recording"))) if (ui_Button(&State->Interface, MakeString("Resume Recording")))
{ {
@ -203,7 +208,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
} }
ui_EndRow(&State->Interface); ui_EndRow(&State->Interface);
ui_StartRow(&State->Interface, 8); ui_BeginRow(&State->Interface, 8);
{ {
if (ui_Button(&State->Interface, MakeString("Scope View"))) if (ui_Button(&State->Interface, MakeString("Scope View")))
{ {

View File

@ -106,7 +106,7 @@ INITIALIZE_APPLICATION(InitializeApplication)
State->Interface.Style.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; State->Interface.Style.ListBGHover = v4{ .22f, .22f, .22f, 1.f };
State->Interface.Style.ListBGSelected = v4{.44f, .44f, .44f, 1.f }; State->Interface.Style.ListBGSelected = v4{.44f, .44f, .44f, 1.f };
State->Interface.Style.Margin = v2{5, 5}; State->Interface.Style.Margin = v2{5, 5};
State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface); State->Interface.Style.RowHeight = ui_GetTextLineHeight(State->Interface) + (2 * State->Interface.Style.Margin.y);
State->Interface.WidgetsCountMax = 4096; State->Interface.WidgetsCountMax = 4096;
State->Interface.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax); State->Interface.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax);

View File

@ -199,6 +199,13 @@ enum ui_layout_direction
{ {
LayoutDirection_TopDown, LayoutDirection_TopDown,
LayoutDirection_BottomUp, LayoutDirection_BottomUp,
LayoutDirection_Inherit,
};
struct ui_column
{
r32 XMin;
r32 XMax;
}; };
struct ui_widget struct ui_widget
@ -225,10 +232,9 @@ struct ui_widget
ui_layout_direction FillDirection; ui_layout_direction FillDirection;
b32 DrawHorizontal; ui_column* Columns;
u32 ColumnsMax;
r32* ColumnWidths;
u32 ColumnsCount; u32 ColumnsCount;
u32 ColumnsFilled;
// NOTE(pjs): I'm not sure this will stay but // NOTE(pjs): I'm not sure this will stay but
// its here so that when we end things like a dropdown, // its here so that when we end things like a dropdown,
@ -278,7 +284,10 @@ struct ui_widget_retained_state
struct ui_interface struct ui_interface
{ {
interface_config Style; interface_config Style;
mouse_state Mouse; mouse_state Mouse;
rect2 WindowBounds;
render_command_buffer* RenderBuffer; render_command_buffer* RenderBuffer;
ui_widget* Widgets; ui_widget* Widgets;
@ -325,6 +334,28 @@ ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
return Result; return Result;
} }
internal void
ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
{
u64 Value = ((u64)1 << Flag);
Widget->Flags = Widget->Flags | Value;
}
internal void
ui_WidgetClearFlag(ui_widget* Widget, u64 Flag)
{
u64 Value = ((u64)1 << Flag);
Widget->Flags = Widget->Flags & ~Value;
}
internal bool
ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
{
u64 Value = ((u64)1 << Flag);
bool Result = (Widget.Flags & Value);
return Result;
}
internal ui_widget_retained_state* internal ui_widget_retained_state*
ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id)
{ {
@ -374,21 +405,7 @@ ui_CreateWidget(ui_interface* Interface, gs_string String)
Result->ChildrenRoot = 0; Result->ChildrenRoot = 0;
Result->ChildrenHead = 0; Result->ChildrenHead = 0;
Result->Flags = 0; Result->Flags = 0;
return Result;
}
internal void
ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
{
u64 Value = ((u64)1 << Flag);
Widget->Flags = Widget->Flags | Value;
}
internal bool
ui_WidgetIsFlagSet(ui_widget Widget, u64 Flag)
{
u64 Value = ((u64)1 << Flag);
bool Result = (Widget.Flags & Value);
return Result; return Result;
} }
@ -406,37 +423,146 @@ ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
// Layout // Layout
internal ui_widget* static rect2
ui_CreateLayoutWidget(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) ui_ReserveBounds(ui_widget* Widget, bool Inset)
{ {
ui_widget* Result = ui_CreateWidget(Interface, Name); Assert(Widget->ColumnsCount > 0);
//ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline); rect2 Bounds = {0};
u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount;
Result->Bounds = Bounds; ui_column Column = Widget->Columns[ColumnIndex];
Result->Margin = Interface->Style.Margin; Bounds.Min.x = Column.XMin;
Result->RowHeight = Interface->Style.RowHeight; Bounds.Min.y = Widget->RowYAt;
Result->FillDirection = FillDir; Bounds.Max.x = Column.XMax;
Bounds.Max.y = Bounds.Min.y + Widget->RowHeight;
switch(FillDir) if (Inset)
{
Bounds.Min.x += Widget->Margin.x;
Bounds.Min.y += Widget->Margin.y;
Bounds.Max.x -= Widget->Margin.x;
Bounds.Max.y -= Widget->Margin.y;
}
return Bounds;
}
internal void
ui_CommitBounds(ui_widget* Parent, rect2 Bounds)
{
u32 ColumnIndex = Parent->ChildCount % Parent->ColumnsCount;
if (ColumnIndex == 0)
{
switch (Parent->FillDirection)
{ {
case LayoutDirection_BottomUp: case LayoutDirection_BottomUp:
{ {
Result->RowYAt = Bounds.Min.y + Result->Margin.y; Parent->RowYAt = Bounds.Max.y;
}break; }break;
case LayoutDirection_TopDown: case LayoutDirection_TopDown:
{ {
Result->RowYAt = Bounds.Max.y - (Result->RowHeight + Result->Margin.y); Parent->RowYAt = Bounds.Min.y - Parent->RowHeight;
}break; }break;
} }
}
}
internal void
ui_ExpandParentToFit(ui_widget* Widget)
{
ui_widget* Parent = Widget->Parent;
switch (Widget->FillDirection)
{
case LayoutDirection_TopDown:
{
Parent->Bounds.Min.y = Min(Parent->Bounds.Min.y, Widget->Bounds.Min.y - Parent->Margin.y);
}break;
case LayoutDirection_BottomUp:
{
Parent->Bounds.Max.y = Max(Parent->Bounds.Max.y, Widget->Bounds.Max.y + Parent->Margin.y);
}break;
InvalidDefaultCase;
}
}
internal void
ui_WidgetCreateColumns(ui_widget* Widget, u32 ColumnsCount, ui_interface* Interface)
{
Widget->Columns = PushArray(Interface->PerFrameMemory, ui_column, ColumnsCount);
Widget->ColumnsCount = ColumnsCount;
Widget->ColumnsFilled = 0;
}
internal void
ui_WidgetInitUniformColumns(ui_widget* Widget)
{
r32 CurrentRowWidth = Rect2Width(Widget->Bounds);
r32 ColumnWidth = CurrentRowWidth / Widget->ColumnsCount;
for (u32 i = 0; i < Widget->ColumnsCount; i++)
{
ui_column* Column = Widget->Columns + i;
Column->XMin = Widget->Bounds.Min.x + (ColumnWidth * i);
Column->XMax = Column->XMin + ColumnWidth;
}
}
internal ui_widget*
ui_CreateLayoutWidget(ui_interface* Interface, rect2 Bounds, gs_string Name, ui_layout_direction FillDir = LayoutDirection_Inherit)
{
ui_widget* Result = ui_CreateWidget(Interface, Name);
ui_WidgetSetFlag(Result, UIWidgetFlag_DrawOutline);
Result->Bounds = Bounds;
Result->Margin = Interface->Style.Margin;
Result->RowHeight = Interface->Style.RowHeight;
// Single Column Layout
ui_WidgetCreateColumns(Result, 1, Interface);
ui_WidgetInitUniformColumns(Result);
if (FillDir == LayoutDirection_Inherit && Result->Parent != 0)
{
Result->FillDirection = Result->Parent->FillDirection;
}
else
{
Result->FillDirection = FillDir;
}
switch(Result->FillDirection)
{
case LayoutDirection_BottomUp:
{
Result->RowYAt = Bounds.Min.y;
}break;
case LayoutDirection_TopDown:
{
Result->RowYAt = Bounds.Max.y - Result->RowHeight;
}break;
InvalidDefaultCase;
}
return Result; return Result;
} }
static ui_widget*
ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
{
ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
Interface->ActiveLayout = Result;
return Result;
}
static ui_widget* static ui_widget*
ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name)
{ {
ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, FillDir, Name); ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir);
if (Interface->DrawOrderRoot) if (Interface->DrawOrderRoot)
{ {
@ -453,124 +579,178 @@ ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir
} }
static ui_widget* static ui_widget*
ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true)
{ {
ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, FillDir, Name); rect2 Bounds = {};
SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); ui_layout_direction Direction = LayoutDirection_TopDown;
Interface->ActiveLayout = Result; if (Interface->ActiveLayout)
return Result; {
Bounds = ui_ReserveBounds(Interface->ActiveLayout, Inset);
Direction = Interface->ActiveLayout->FillDirection;
}
else
{
Bounds.Min.x = Interface->WindowBounds.Min.x;
Bounds.Min.y = Interface->WindowBounds.Max.y;
Bounds.Max.x = Interface->WindowBounds.Max.x;
Bounds.Max.y = Interface->WindowBounds.Max.y;
if (Inset)
{
Bounds.Min.x += Interface->Style.Margin.x;
Bounds.Max.x -= Interface->Style.Margin.x;
}
Direction = LayoutDirection_TopDown;
}
return ui_PushLayout(Interface, Bounds, Direction, Name);
}
internal void
ui_ExpandToFitChildren(ui_widget* Parent)
{
v2 Extents = { Parent->Bounds.Max.y, Parent->Bounds.Min.y };
for (ui_widget* Child = Parent->ChildrenRoot; Child != 0; Child = Child->Next)
{
Extents.x = Min(Extents.x, Child->Bounds.Min.y);
Extents.y = Max(Extents.y, Child->Bounds.Max.y);
}
switch(Parent->FillDirection)
{
case LayoutDirection_BottomUp:
{
Parent->Bounds.Max.y = Extents.y + Parent->Margin.y;
}break;
case LayoutDirection_TopDown:
{
Parent->Bounds.Min.y = Extents.x - Parent->Margin.y;
}break;
InvalidDefaultCase;
}
} }
static void static void
ui_PopLayout(ui_interface* Interface) ui_PopLayout(ui_interface* Interface)
{ {
Assert(Interface->ActiveLayout != 0); Assert(Interface->ActiveLayout != 0);
ui_widget* Layout = Interface->ActiveLayout;
ui_ExpandToFitChildren(Layout);
Interface->ActiveLayout = Interface->ActiveLayout->Parent; Interface->ActiveLayout = Interface->ActiveLayout->Parent;
// NOTE(pjs): This indicates that the parent layout should
// expand to fit the layout that we just popped
if (Interface->ActiveLayout != 0 &&
Interface->ActiveLayout->ChildrenHead == Layout)
{
ui_CommitBounds(Interface->ActiveLayout, Layout->Bounds);
}
} }
static void static void
ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0) ui_BeginRow(ui_interface* Interface, u32 ColumnsMax)
{ {
Interface->ActiveLayout->DrawHorizontal = true; ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
Interface->ActiveLayout->ColumnsMax = ColumnsMax; ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
Interface->ActiveLayout->ColumnWidths = 0; ui_WidgetInitUniformColumns(Layout);
Interface->ActiveLayout->ColumnsCount = 0;
} }
static void enum ui_column_size_rule
ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths)
{ {
Interface->ActiveLayout->DrawHorizontal = true; UIColumnSize_Fixed,
Interface->ActiveLayout->ColumnsMax = ColumnsMax; UIColumnSize_Percent,
Interface->ActiveLayout->ColumnWidths = ColumnWidths; UIColumnSize_Fill,
Interface->ActiveLayout->ColumnsCount = 0; };
struct ui_column_spec
{
ui_column_size_rule Rule;
union
{
r32 Width;
r32 Percent;
};
};
static void
ui_BeginRow(ui_interface* Interface, u32 ColumnsMax, ui_column_spec* ColumnRules)
{
ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false);
ui_WidgetCreateColumns(Layout, ColumnsMax, Interface);
// First Pass, determine widths of each column, and how much space is left to be divided by the fill columns
r32 RowWidth = Rect2Width(Layout->Bounds);
r32 RemainingSpace = RowWidth;
u32 FillColumnsCount = 0;
for (u32 i = 0; i < Layout->ColumnsCount; i++)
{
ui_column_spec Spec = ColumnRules[i];
ui_column* Column = Layout->Columns + i;
switch (Spec.Rule)
{
case UIColumnSize_Fixed:
{
Column->XMax = Spec.Width;
RemainingSpace -= Column->XMax;
}break;
case UIColumnSize_Percent:
{
Column->XMax = Spec.Percent * RowWidth;
RemainingSpace -= Column->XMax;
}break;
case UIColumnSize_Fill:
{
FillColumnsCount += 1;
}break;
InvalidDefaultCase;
}
}
r32 FillColumnWidth = RemainingSpace / FillColumnsCount;
// Second Pass, specify the actual XMin and XMax of each column
r32 ColumnStartX = Layout->Bounds.Min.x;
for (u32 i = 0; i < Layout->ColumnsCount; i++)
{
ui_column_spec Spec = ColumnRules[i];
ui_column* Column = Layout->Columns + i;
r32 ColumnWidth = 0;
switch (Spec.Rule)
{
case UIColumnSize_Fixed:
case UIColumnSize_Percent:
{
ColumnWidth = Column->XMax;
}break;
case UIColumnSize_Fill:
{
ColumnWidth = FillColumnWidth;
}break;
}
Column->XMin = ColumnStartX ;
Column->XMax = Column->XMin + Max(0, ColumnWidth);
ColumnStartX = Column->XMax;
}
} }
static void static void
ui_EndRow(ui_interface* Interface) ui_EndRow(ui_interface* Interface)
{ {
Interface->ActiveLayout->DrawHorizontal = false; ui_PopLayout(Interface);
Interface->ActiveLayout->ColumnWidths = 0; //ui_widget* Layout = Interface->ActiveLayout;
Interface->ActiveLayout->RowYAt -= (Interface->ActiveLayout->RowHeight + Interface->ActiveLayout->Margin.y); //Layout->DrawHorizontal = false;
} //Layout->RowYAt -= (Layout->RowHeight + Layout->Margin.y);
static b32
ui_TryReserveElementBounds(ui_widget* Widget, rect2* Bounds)
{
b32 Result = true;
if (!Widget->DrawHorizontal)
{
Bounds->Min = { Widget->Bounds.Min.x + Widget->Margin.x, Widget->RowYAt };
Bounds->Max = { Widget->Bounds.Max.x - Widget->Margin.x, Bounds->Min.y + Widget->RowHeight };
r32 RowOffset = Widget->RowHeight + Widget->Margin.y;
switch (Widget->FillDirection)
{
case LayoutDirection_BottomUp:
{
Widget->RowYAt += RowOffset;
}break;
case LayoutDirection_TopDown:
{
Widget->RowYAt -= RowOffset;
}break;
InvalidDefaultCase;
}
}
else
{
if (Widget->ColumnsMax > 0)
{
Assert(Widget->ColumnsCount < Widget->ColumnsMax);
if (Widget->ColumnWidths != 0)
{
v2 Min = {
Widget->Bounds.Min.x + Widget->Margin.x,
Widget->RowYAt
};
for (u32 i = 0; i < Widget->ColumnsCount; i++)
{
Min.x += Widget->ColumnWidths[i];
}
Bounds->Min = Min;
Bounds->Max = Bounds->Min + v2{
Widget->ColumnWidths[Widget->ColumnsCount] - Widget->Margin.x,
Widget->RowHeight
};
}
else
{
r32 ElementWidth = Rect2Width(Widget->Bounds) / Widget->ColumnsMax;
Bounds->Min = {
Widget->Bounds.Min.x + (ElementWidth * Widget->ColumnsCount) + Widget->Margin.x,
Widget->RowYAt
};
Bounds->Max = {
Bounds->Min.x + ElementWidth - (Widget->Margin.x * 2),
Bounds->Min.y + Widget->RowHeight
};
}
Widget->ColumnsCount++;
}
else
{
Result = false;
}
}
return Result;
}
static rect2
ui_ReserveElementBounds(ui_widget* Layout)
{
rect2 Bounds = {0};
if (!ui_TryReserveElementBounds(Layout, &Bounds))
{
InvalidCodePath;
}
return Bounds;
} }
static rect2 static rect2
@ -578,10 +758,6 @@ ui_LayoutRemaining(ui_widget Layout)
{ {
rect2 Result = Layout.Bounds; rect2 Result = Layout.Bounds;
Result.Max.y = Layout.RowYAt; Result.Max.y = Layout.RowYAt;
if (Layout.DrawHorizontal)
{
Result.Max.y -= Layout.RowHeight;
}
return Result; return Result;
} }
@ -625,20 +801,17 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
} }
Assert(Widget->Parent != 0);
ui_CommitBounds(Widget->Parent, Widget->Bounds);
return Result; return Result;
} }
internal ui_eval_result internal ui_eval_result
ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
{ {
rect2 Bounds = {0};
ui_widget* Layout = Interface->ActiveLayout; ui_widget* Layout = Interface->ActiveLayout;
if (!ui_TryReserveElementBounds(Layout, &Bounds)) rect2 Bounds = ui_ReserveBounds(Layout, true);
{
// TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet
InvalidCodePath;
}
return ui_EvaluateWidget(Interface, Widget, Bounds); return ui_EvaluateWidget(Interface, Widget, Bounds);
} }
@ -781,8 +954,8 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result E
r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y; r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y;
Direction = LayoutDirection_BottomUp; Direction = LayoutDirection_BottomUp;
MenuBounds = rect2{ MenuBounds = rect2{
v2{ Widget->Bounds.Min.x, Widget->Bounds.Max.y }, v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, Widget->Bounds.Max.y },
v2{ Widget->Bounds.Max.x, ParentLayoutMaxY } v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY }
}; };
} }
else else
@ -790,14 +963,15 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result E
r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y; r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y;
Direction = LayoutDirection_TopDown; Direction = LayoutDirection_TopDown;
MenuBounds = rect2{ MenuBounds = rect2{
v2{ Widget->Bounds.Min.x, ParentLayoutMinY }, v2{ Widget->Bounds.Min.x - ParentLayout.Margin.x, ParentLayoutMinY },
v2{ Widget->Bounds.Max.x, Widget->Bounds.Min.y } v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, Widget->Bounds.Min.y }
}; };
} }
ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("WidgetLayout")); ui_widget* Layout = ui_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("WidgetLayout"));
Layout->Margin.y = 0; Layout->Margin.y = 0;
Layout->WidgetReference = Widget->Id; Layout->WidgetReference = Widget->Id;
ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline);
} }
return State->Value; return State->Value;
@ -913,6 +1087,19 @@ ui_Toggle(ui_interface* Interface, gs_string Text, bool Value)
return Result; return Result;
} }
internal void
ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows)
{
ui_widget* Layout = ui_PushLayout(Interface, Text);
}
internal void
ui_EndList(ui_interface* Interface)
{
}
// //
// OLD // OLD