began turning ui_layout into ui_widgets that just have children. Currently, layout works, but the id system needs some thinking.

This commit is contained in:
PS 2020-11-08 19:42:14 -08:00
parent a42d2e81c5
commit fa1d5a5afc
7 changed files with 309 additions and 250 deletions

View File

@ -89,35 +89,8 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue)
} }
internal void internal void
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer) Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget)
{ {
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
PushRenderClearScreen(RenderBuffer);
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
ui_layout Layout = ui_CreateLayout(&State->Interface, Context->WindowBounds);
ui_PushLayout(&State->Interface, Layout);
DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
{
operation_mode OperationMode = State->Modes.ActiveModes[m];
if (OperationMode.Render != 0)
{
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
}
}
ui_PopLayout(&State->Interface);
// Draw the Interface
for (u32 i = 0; i < State->Interface.WidgetsCount; i++)
{
ui_widget Widget = State->Interface.Widgets[i];
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground)) if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
{ {
v4 Color = State->Interface.Style.ButtonColor_Inactive; v4 Color = State->Interface.Style.ButtonColor_Inactive;
@ -169,8 +142,71 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB
v4 Color = WhiteV4; v4 Color = WhiteV4;
PushRenderBoundingBox2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Thickness, Color); PushRenderBoundingBox2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Thickness, Color);
} }
if (Widget.ChildrenRoot)
{
Editor_DrawWidget(State, Context, RenderBuffer, *Widget.ChildrenRoot);
} }
if (Widget.Next)
{
Editor_DrawWidget(State, Context, RenderBuffer, *Widget.Next);
}
}
internal void
Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer)
{
PushRenderOrthographic(RenderBuffer, State->WindowBounds);
PushRenderClearScreen(RenderBuffer);
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
ui_PushLayout(&State->Interface, Context->WindowBounds, LayoutDirection_TopDown);
rect2 Rects[2];
RectVSplitAtPercent(Context->WindowBounds, .5f, &Rects[0], &Rects[1]);
for (u32 j = 0; j < 2; j++)
{
ui_PushLayout(&State->Interface, Rects[j], LayoutDirection_TopDown);
if (ui_BeginDropdown(&State->Interface, MakeString("Select")))
{
for (s32 i = 0; i < GlobalPanelDefsCount; i++)
{
panel_definition Def = State->PanelSystem.PanelDefs[i];
gs_string DefName = MakeString(Def.PanelName, Def.PanelNameLength);
if (ui_Button(&State->Interface, DefName))
{
}
}
}
ui_EndDropdown(&State->Interface);
ui_PopLayout(&State->Interface);
}
#if 0
DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context);
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++)
{
operation_mode OperationMode = State->Modes.ActiveModes[m];
if (OperationMode.Render != 0)
{
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
}
}
#endif
ui_PopLayout(&State->Interface);
// Draw the Interface
ui_widget Widget = *State->Interface.DrawOrderRoot;
Editor_DrawWidget(State, Context, RenderBuffer, Widget);
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue); Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue);

View File

@ -240,14 +240,14 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(&State->AnimationSystem);
operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip); operation_mode* DragAnimationClipMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationClipCommands, UpdateDragAnimationClip);
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle);
drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode, drag_animation_clip_state* OpState = CreateOperationState(DragAnimationClipMode,
&State->Modes, &State->Modes,
drag_animation_clip_state); drag_animation_clip_state);
OpState->TimelineBounds = TimelineBounds; OpState->TimelineBounds = TimelineBounds;
OpState->BlockHandle = BlockHandle; OpState->BlockHandle = BlockHandle;
OpState->VisibleRange = VisibleRange; OpState->VisibleRange = VisibleRange;
animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle);
OpState->ClipRange = SelectedBlock->Range; OpState->ClipRange = SelectedBlock->Range;
} }
// ------------------- // -------------------
@ -303,10 +303,7 @@ DrawFrameBar (animation_system* AnimationSystem, ui_interface Interface, frame_r
r32 BarWidth = Rect2Width(BarBounds); r32 BarWidth = Rect2Width(BarBounds);
// Mouse clicked inside frame nubmer bar -> change current frame on timeline // Mouse clicked inside frame nubmer bar -> change current frame on timeline
// TODO(pjs): both of these functions can get wrapped in a MouseClickedRect if (ui_MouseClickedRect(Interface, BarBounds))
// and an alternate MouseIsDraggingRect
if (MouseButtonTransitionedDown(Interface.Mouse.LeftButtonState) &&
PointIsInRect(BarBounds, Interface.Mouse.DownPos))
{ {
StartDragTimeMarker(BarBounds, VisibleFrames, State); StartDragTimeMarker(BarBounds, VisibleFrames, State);
} }
@ -590,13 +587,12 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback)
internal void internal void
DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem) DrawAnimationClipsList(rect2 PanelBounds, ui_interface* Interface, u32 SelectedAnimationLayerHandle, animation_system* AnimationSystem)
{ {
ui_layout Layout = ui_CreateLayout(Interface, PanelBounds); ui_PushLayout(Interface, PanelBounds, LayoutDirection_TopDown);
ui_PushLayout(Interface, Layout);
for (s32 i = 0; i < GlobalAnimationClipsCount; i++) for (s32 i = 0; i < GlobalAnimationClipsCount; i++)
{ {
animation_clip Clip = GlobalAnimationClips[i]; animation_clip Clip = GlobalAnimationClips[i];
gs_string ClipName = MakeString(Clip.Name, Clip.NameLength); gs_string ClipName = MakeString(Clip.Name, Clip.NameLength);
if (ui_LayoutListEntry(Interface, &Layout, ClipName, i)) if (ui_LayoutListButton(Interface, ClipName, i))
{ {
AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem); AddAnimationBlockAtCurrentTime(i + 1, SelectedAnimationLayerHandle, AnimationSystem);
} }
@ -609,8 +605,7 @@ PlayBar_Render(animation_timeline_state* TimelineState, rect2 Bounds, panel* Pan
{ {
animation_system* AnimSystem = &State->AnimationSystem; animation_system* AnimSystem = &State->AnimationSystem;
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
ui_layout Layout = ui_CreateLayout(Interface, Bounds); ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown);
ui_PushLayout(Interface, Layout);
ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]); ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]);
ui_StartRow(&State->Interface, 4); ui_StartRow(&State->Interface, 4);
@ -797,8 +792,7 @@ AnimInfoView_Render(animation_timeline_state* TimelineState, rect2 Bounds, rende
animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem); animation* ActiveAnim = AnimationSystem_GetActiveAnimation(AnimSystem);
ui_interface* Interface = &State->Interface; ui_interface* Interface = &State->Interface;
ui_layout Layout = ui_CreateLayout(Interface, Bounds); ui_PushLayout(Interface, Bounds, LayoutDirection_TopDown);
ui_PushLayout(Interface, Layout);
ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]);

View File

@ -88,8 +88,7 @@ internal void
FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state); file_view_state* FileViewState = Panel_GetStateStruct(Panel, file_view_state);
ui_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds); ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown);
ui_PushLayout(&State->Interface, Layout);
if (ui_Button(&State->Interface, MakeString("Exit"))) if (ui_Button(&State->Interface, MakeString("Exit")))
{ {
@ -108,7 +107,7 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu
gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length); gs_const_string FileName = Substring(File.Path, LastSlashIndex + 1, File.Path.Length);
gs_string PathString = PushString(State->Transient, FileName.Length); gs_string PathString = PushString(State->Transient, FileName.Length);
PrintF(&PathString, "%S", FileName); PrintF(&PathString, "%S", FileName);
if (ui_LayoutListButton(&State->Interface, &Layout, PathString, i)) if (ui_LayoutListButton(&State->Interface, PathString, i))
{ {
if (File.IsDirectory) if (File.IsDirectory)
{ {

View File

@ -38,18 +38,21 @@ GSMetaTag(panel_type_hierarchy);
internal void internal void
HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context) HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
{ {
ui_layout Layout = ui_CreateLayout(&State->Interface, PanelBounds); ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown);
ui_PushLayout(&State->Interface, Layout);
// TODO(pjs): Come back to this after the layout stuff is handled.
// Ideally it handles the visuals of the hierarchy itself.
#if 0
gs_string TempString = PushString(State->Transient, 256); gs_string TempString = PushString(State->Transient, 256);
u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout.RowHeight) + 1; u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout->RowHeight) + 1;
u32 AssembliesToDraw = Min(LineCount, State->Assemblies.Count); u32 AssembliesToDraw = Min(LineCount, State->Assemblies.Count);
rect2* LineBounds = PushArray(State->Transient, rect2, LineCount); rect2* LineBounds = PushArray(State->Transient, rect2, LineCount);
// 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); //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);
} }
@ -62,7 +65,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
ui_StartRow(&State->Interface, 2); ui_StartRow(&State->Interface, 2);
{ {
ui_DrawString(&State->Interface, TempString); ui_DrawString(&State->Interface, TempString);
if (ui_LayoutListButton(&State->Interface, &Layout, MakeString("X"), AssemblyIndex)) if (ui_LayoutListButton(&State->Interface, MakeString("X"), AssemblyIndex))
{ {
UnloadAssembly(AssemblyIndex, State, Context); UnloadAssembly(AssemblyIndex, State, Context);
} }
@ -80,6 +83,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback);
} }
} }
#endif
ui_PopLayout(&State->Interface); ui_PopLayout(&State->Interface);
} }

View File

@ -25,7 +25,7 @@ ProfilerView_Cleanup(panel* Panel, app_state* State)
} }
internal void internal void
RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory) RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory)
{ {
v4 ThreadColors[] = { v4 ThreadColors[] = {
v4{.73f, .33f, .83f, 1}, v4{.73f, .33f, .83f, 1},
@ -35,7 +35,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb
v4{.74f, .40f, .25f, 1}, v4{.74f, .40f, .25f, 1},
}; };
rect2 Bounds = ui_LayoutRemaining(Layout); rect2 Bounds = ui_LayoutRemaining(*Layout);
r32 Width = Rect2Width(Bounds); r32 Width = Rect2Width(Bounds);
r32 DepthHeight = 64; r32 DepthHeight = 64;
@ -89,7 +89,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb
} }
internal void internal void
RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory) RenderProfiler_ListVisualization(ui_interface* Interface, ui_widget* Layout, debug_frame* VisibleFrame, gs_memory_arena* Memory)
{ {
char Backbuffer[256]; char Backbuffer[256];
gs_string String = MakeString(Backbuffer, 0, 256); gs_string String = MakeString(Backbuffer, 0, 256);
@ -179,8 +179,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend
debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices); debug_frame* VisibleFrame = GetLastDebugFrame(GlobalDebugServices);
ui_layout Layout = ui_CreateLayout(&State->Interface, ProcListBounds); ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown);
ui_PushLayout(&State->Interface, Layout);
ui_StartRow(&State->Interface, 4); ui_StartRow(&State->Interface, 4);
{ {
@ -195,7 +194,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_ReserveElementBounds(Layout);
if (ui_Button(&State->Interface, MakeString("Resume Recording"))) if (ui_Button(&State->Interface, MakeString("Resume Recording")))
{ {

View File

@ -194,14 +194,21 @@ DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32
enum ui_widget_flag enum ui_widget_flag
{ {
UIWidgetFlag_DrawBackground, UIWidgetFlag_DrawBackground,
UIWidgetFlag_DrawString,
UIWidgetFlag_DrawOutline, UIWidgetFlag_DrawOutline,
UIWidgetFlag_Clickable, UIWidgetFlag_Clickable,
}; };
struct ui_widget_id struct ui_widget_id
{ {
u64 Index; u64 Id;
u64 LayoutId; u64 ParentId;
};
enum ui_layout_direction
{
LayoutDirection_TopDown,
LayoutDirection_BottomUp,
}; };
struct ui_widget struct ui_widget
@ -213,7 +220,31 @@ struct ui_widget
rect2 Bounds; rect2 Bounds;
u64 Flags; u64 Flags;
bool RetainedState;
ui_widget* Next;
// Layout
ui_widget* Parent;
v2 Margin;
r32 RowHeight;
r32 RowYAt;
ui_layout_direction FillDirection;
b32 DrawHorizontal;
u32 ColumnsMax;
r32* ColumnWidths;
u32 ColumnsCount;
// NOTE(pjs): I'm not sure this will stay but
// its here so that when we end things like a dropdown,
// we can check the retained state of that dropdown
ui_widget_id WidgetReference;
ui_widget* ChildrenRoot;
ui_widget* ChildrenHead;
u32 ChildCount;
}; };
struct ui_eval_result struct ui_eval_result
@ -241,34 +272,6 @@ struct interface_config
r32 RowHeight; r32 RowHeight;
}; };
enum ui_layout_direction
{
LayoutDirection_TopDown,
LayoutDirection_BottomUp,
};
struct ui_layout
{
u64 Id;
rect2 Bounds;
v2 Margin;
r32 RowHeight;
r32 RowYAt;
ui_layout_direction FillDirection;
b32 DrawHorizontal;
u32 ColumnsMax;
r32* ColumnWidths;
u32 ColumnsCount;
// NOTE(pjs): I'm not sure this will stay but
// its here so that when we end things like a dropdown,
// we can check the retained state of that dropdown
ui_widget_id WidgetReference;
};
struct ui_widget_retained_state struct ui_widget_retained_state
{ {
ui_widget_id Id; ui_widget_id Id;
@ -285,13 +288,13 @@ struct ui_interface
u64 WidgetsCount; u64 WidgetsCount;
u64 WidgetsCountMax; u64 WidgetsCountMax;
ui_widget* DrawOrderHead;
ui_widget* DrawOrderRoot;
ui_widget_id HotWidget; ui_widget_id HotWidget;
ui_widget_id ActiveWidget; ui_widget_id ActiveWidget;
#define LAYOUT_COUNT_MAX 8 ui_widget* ActiveLayout;
ui_layout LayoutStack[LAYOUT_COUNT_MAX];
u64 LayoutStackCount;
u64 LayoutIdAcc;
#define RETAINED_STATE_MAX 128 #define RETAINED_STATE_MAX 128
ui_widget_retained_state RetainedState[RETAINED_STATE_MAX]; ui_widget_retained_state RetainedState[RETAINED_STATE_MAX];
@ -302,14 +305,14 @@ internal void
ui_InterfaceReset(ui_interface* Interface) ui_InterfaceReset(ui_interface* Interface)
{ {
Interface->WidgetsCount = 0; Interface->WidgetsCount = 0;
Interface->LayoutStackCount = 0; Interface->DrawOrderHead = 0;
Interface->LayoutIdAcc = 0; Interface->DrawOrderRoot = 0;
} }
internal bool internal bool
ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B) ui_WidgetIdsEqual(ui_widget_id A, ui_widget_id B)
{ {
bool Result = (A.Index == B.Index) && (A.LayoutId == B.LayoutId); bool Result = (A.Id == B.Id) && (A.ParentId == B.ParentId);
return Result; return Result;
} }
@ -337,6 +340,30 @@ ui_CreateRetainedState(ui_interface* Interface, ui_widget_id Id)
return Result; return Result;
} }
internal ui_widget*
ui_CreateWidget(ui_interface* Interface, gs_string String)
{
Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
ui_widget* Result = Interface->Widgets + Interface->WidgetsCount++;
Result->Parent = Interface->ActiveLayout;
u64 Id = HashDJB2ToU64(StringExpand(String));
if (Result->Parent)
{
Id = HashAppendDJB2ToU32(Id, Result->Parent->Id.Id);
Id = HashAppendDJB2ToU32(Id, Result->Parent->ChildCount);
}
Result->Id.Id = Id;
Result->String = String;
Result->Alignment = Align_Left;
Result->Next = 0;
Result->ChildrenRoot = 0;
Result->ChildrenHead = 0;
Result->Flags = 0;
return Result;
}
// //
// Interaction // Interaction
// //
@ -351,94 +378,95 @@ ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
// Layout // Layout
static ui_layout static ui_widget*
ui_CreateLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDirection = LayoutDirection_TopDown) ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir)
{ {
ui_layout Result = {0}; ui_widget* Result = ui_CreateWidget(Interface, MakeString("Layout"));
Result.Bounds = Bounds; Result->Bounds = Bounds;
Result.Margin = Interface->Style.Margin; Result->Margin = Interface->Style.Margin;
Result.RowHeight = Interface->Style.RowHeight; Result->RowHeight = Interface->Style.RowHeight;
Result.FillDirection = FillDirection; Result->FillDirection = FillDir;
switch(FillDirection) switch(FillDir)
{ {
case LayoutDirection_BottomUp: case LayoutDirection_BottomUp:
{ {
Result.RowYAt = Bounds.Min.y; Result->RowYAt = Bounds.Min.y;
}break; }break;
case LayoutDirection_TopDown: case LayoutDirection_TopDown:
{ {
Result.RowYAt = Bounds.Max.y - Result.RowHeight; Result->RowYAt = Bounds.Max.y - Result->RowHeight;
}break; }break;
} }
Result.Id = ++Interface->LayoutIdAcc; if (Interface->DrawOrderRoot)
{
SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Result);
Interface->ActiveLayout->ChildCount++;
}
else
{
SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result);
}
Interface->ActiveLayout = Result;
return Result; return Result;
} }
static void internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds);
ui_PushLayout(ui_interface* Interface, ui_layout Layout)
{
Assert(Interface->LayoutStackCount < LAYOUT_COUNT_MAX);
Interface->LayoutStack[Interface->LayoutStackCount++] = Layout;
}
static void static void
ui_PopLayout(ui_interface* Interface) ui_PopLayout(ui_interface* Interface)
{ {
Assert(Interface->LayoutStackCount > 0); Assert(Interface->ActiveLayout != 0);
Interface->LayoutStackCount -= 1; ui_EvaluateWidget(Interface, Interface->ActiveLayout, Interface->ActiveLayout->Bounds);
Interface->ActiveLayout = Interface->ActiveLayout->Parent;
} }
static void static void
ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0) ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0)
{ {
u64 LayoutIdx = Interface->LayoutStackCount - 1; Interface->ActiveLayout->DrawHorizontal = true;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true; Interface->ActiveLayout->ColumnsMax = ColumnsMax;
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax; Interface->ActiveLayout->ColumnWidths = 0;
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0; Interface->ActiveLayout->ColumnsCount = 0;
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
} }
static void static void
ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths) ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths)
{ {
u64 LayoutIdx = Interface->LayoutStackCount - 1; Interface->ActiveLayout->DrawHorizontal = true;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true; Interface->ActiveLayout->ColumnsMax = ColumnsMax;
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax; Interface->ActiveLayout->ColumnWidths = ColumnWidths;
Interface->LayoutStack[LayoutIdx].ColumnWidths = ColumnWidths; Interface->ActiveLayout->ColumnsCount = 0;
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
} }
static void static void
ui_EndRow(ui_interface* Interface) ui_EndRow(ui_interface* Interface)
{ {
u64 LayoutIdx = Interface->LayoutStackCount - 1; Interface->ActiveLayout->DrawHorizontal = false;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = false; Interface->ActiveLayout->ColumnWidths = 0;
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0; Interface->ActiveLayout->RowYAt -= Interface->ActiveLayout->RowHeight;
Interface->LayoutStack[LayoutIdx].RowYAt -= Interface->LayoutStack[LayoutIdx].RowHeight;
} }
static b32 static b32
ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds) ui_TryReserveElementBounds(ui_widget* Widget, rect2* Bounds)
{ {
b32 Result = true; b32 Result = true;
if (!Layout->DrawHorizontal) if (!Widget->DrawHorizontal)
{ {
Bounds->Min = { Layout->Bounds.Min.x, Layout->RowYAt }; Bounds->Min = { Widget->Bounds.Min.x, Widget->RowYAt };
Bounds->Max = { Layout->Bounds.Max.x, Bounds->Min.y + Layout->RowHeight }; Bounds->Max = { Widget->Bounds.Max.x, Bounds->Min.y + Widget->RowHeight };
switch (Layout->FillDirection) switch (Widget->FillDirection)
{ {
case LayoutDirection_BottomUp: case LayoutDirection_BottomUp:
{ {
Layout->RowYAt += Layout->RowHeight; Widget->RowYAt += Widget->RowHeight;
}break; }break;
case LayoutDirection_TopDown: case LayoutDirection_TopDown:
{ {
Layout->RowYAt -= Layout->RowHeight; Widget->RowYAt -= Widget->RowHeight;
}break; }break;
InvalidDefaultCase; InvalidDefaultCase;
@ -446,32 +474,32 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
} }
else else
{ {
if (Layout->ColumnsMax > 0) if (Widget->ColumnsMax > 0)
{ {
Assert(Layout->ColumnsCount < Layout->ColumnsMax); Assert(Widget->ColumnsCount < Widget->ColumnsMax);
if (Layout->ColumnWidths != 0) if (Widget->ColumnWidths != 0)
{ {
v2 Min = { Layout->Bounds.Min.x, Layout->RowYAt }; v2 Min = { Widget->Bounds.Min.x, Widget->RowYAt };
for (u32 i = 0; i < Layout->ColumnsCount; i++) for (u32 i = 0; i < Widget->ColumnsCount; i++)
{ {
Min.x += Layout->ColumnWidths[i]; Min.x += Widget->ColumnWidths[i];
} }
Bounds->Min = Min; Bounds->Min = Min;
Bounds->Max = Bounds->Min + v2{ Layout->ColumnWidths[Layout->ColumnsCount], Layout->RowHeight }; Bounds->Max = Bounds->Min + v2{ Widget->ColumnWidths[Widget->ColumnsCount], Widget->RowHeight };
} }
else else
{ {
r32 ElementWidth = Rect2Width(Layout->Bounds) / Layout->ColumnsMax; r32 ElementWidth = Rect2Width(Widget->Bounds) / Widget->ColumnsMax;
Bounds->Min = { Bounds->Min = {
Layout->Bounds.Min.x + (ElementWidth * Layout->ColumnsCount) + Layout->Margin.x, Widget->Bounds.Min.x + (ElementWidth * Widget->ColumnsCount) + Widget->Margin.x,
Layout->RowYAt Widget->RowYAt
}; };
Bounds->Max = { Bounds->Max = {
Bounds->Min.x + ElementWidth - Layout->Margin.x, Bounds->Min.x + ElementWidth - Widget->Margin.x,
Bounds->Min.y + Layout->RowHeight Bounds->Min.y + Widget->RowHeight
}; };
} }
Layout->ColumnsCount++; Widget->ColumnsCount++;
} }
else else
{ {
@ -482,7 +510,7 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
} }
static rect2 static rect2
ui_ReserveElementBounds(ui_layout* Layout) ui_ReserveElementBounds(ui_widget* Layout)
{ {
rect2 Bounds = {0}; rect2 Bounds = {0};
if (!ui_TryReserveElementBounds(Layout, &Bounds)) if (!ui_TryReserveElementBounds(Layout, &Bounds))
@ -493,7 +521,7 @@ ui_ReserveElementBounds(ui_layout* Layout)
} }
static rect2 static rect2
ui_LayoutRemaining(ui_layout Layout) ui_LayoutRemaining(ui_widget Layout)
{ {
rect2 Result = Layout.Bounds; rect2 Result = Layout.Bounds;
Result.Max.y = Layout.RowYAt; Result.Max.y = Layout.RowYAt;
@ -506,15 +534,6 @@ ui_LayoutRemaining(ui_layout Layout)
// Widgets // Widgets
internal ui_widget
ui_CreateWidget(gs_string String)
{
ui_widget Result = {};
Result.String = String;
Result.Alignment = Align_Left;
return Result;
}
internal void internal void
ui_WidgetSetFlag(ui_widget* Widget, u64 Flag) ui_WidgetSetFlag(ui_widget* Widget, u64 Flag)
{ {
@ -535,12 +554,15 @@ ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds)
{ {
ui_eval_result Result = {}; ui_eval_result Result = {};
Assert(Interface->WidgetsCount < Interface->WidgetsCountMax); //Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
Widget->Id.Index = Interface->WidgetsCount++; //u64 Index = Interface->WidgetsCount++;
Widget->Id.LayoutId = Interface->LayoutStack[Interface->LayoutStackCount - 1].Id;
Widget->Id.Id = HashDJB2ToU64(StringExpand(Widget->String));
Widget->Id.ParentId = Interface->ActiveLayout->Id.Id;
Widget->Bounds = Bounds; Widget->Bounds = Bounds;
Interface->Widgets[Widget->Id.Index] = *Widget; //Interface->Widgets[Index] = *Widget;
SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget);
if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable)) if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable))
{ {
@ -569,7 +591,7 @@ internal ui_eval_result
ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
{ {
rect2 Bounds = {0}; rect2 Bounds = {0};
ui_layout* Layout = Interface->LayoutStack + Interface->LayoutStackCount - 1; ui_widget* Layout = Interface->ActiveLayout;
if (!ui_TryReserveElementBounds(Layout, &Bounds)) if (!ui_TryReserveElementBounds(Layout, &Bounds))
{ {
// TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet // TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet
@ -606,36 +628,38 @@ internal void
ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left) ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left)
{ {
DEBUG_TRACK_FUNCTION; DEBUG_TRACK_FUNCTION;
ui_widget Widget = ui_CreateWidget(String); ui_widget* Widget = ui_CreateWidget(Interface, String);
Widget.Bounds = Bounds; Widget->Bounds = Bounds;
ui_EvaluateWidget(Interface, &Widget); ui_EvaluateWidget(Interface, Widget);
} }
internal void internal void
ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left) ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left)
{ {
DEBUG_TRACK_FUNCTION; DEBUG_TRACK_FUNCTION;
ui_widget Widget = ui_CreateWidget(String); ui_widget* Widget = ui_CreateWidget(Interface, String);
ui_EvaluateWidget(Interface, &Widget); ui_EvaluateWidget(Interface, Widget);
} }
static b32 static b32
ui_Button(ui_interface* Interface, gs_string Text) ui_Button(ui_interface* Interface, gs_string Text)
{ {
ui_widget Widget = ui_CreateWidget(Text); ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return Result.Clicked; return Result.Clicked;
} }
static b32 static b32
ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds) ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds)
{ {
ui_widget Widget = ui_CreateWidget(Text); ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
return Result.Clicked; return Result.Clicked;
} }
@ -666,42 +690,28 @@ ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex)
static b32 static b32
ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex) ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex)
{ {
ui_widget Widget = ui_CreateWidget(Text); ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
// TODO(pjs): Reimplement alternating color backgrounds // TODO(pjs): Reimplement alternating color backgrounds
Widget.Bounds = Bounds; Widget->Bounds = Bounds;
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget); ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return Result.Clicked; return Result.Clicked;
} }
static b32 static b32
ui_LayoutListButton(ui_interface* Interface, ui_layout* Layout, gs_string Text, u32 ListItemIndex) ui_LayoutListButton(ui_interface* Interface, gs_string Text, u32 ListItemIndex)
{ {
// TODO(pjs): Reimplement alternating colors
return ui_Button(Interface, Text); return ui_Button(Interface, Text);
} }
static b32
ui_LayoutListEntry(ui_interface* Interface, ui_layout* Layout, gs_string Text, u32 Index)
{
rect2 Bounds = {0};
if (!ui_TryReserveElementBounds(Layout, &Bounds))
{
// TODO(Peter): this isn't really invalid, but I don't have a concrete use case
// for it yet. This should only fire if the Layout component is drawing a row,
// but if you're in row mode during a list, what should happen?
// Punting this till I have a use case
InvalidCodePath;
}
return ui_Button(Interface, Text, Bounds);
}
internal bool internal bool
ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result EvalResult) ui_EvaluateDropdown(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult)
{ {
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget.Id); ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id);
if (!State) { if (!State) {
State = ui_CreateRetainedState(Interface, Widget.Id); State = ui_CreateRetainedState(Interface, Widget->Id);
} }
if (EvalResult.Clicked) if (EvalResult.Clicked)
@ -711,10 +721,10 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
if (State->Value) if (State->Value)
{ {
ui_layout ParentLayout = Interface->LayoutStack[Interface->LayoutStackCount - 1]; ui_widget ParentLayout = *Interface->ActiveLayout;
r32 SpaceAbove = ParentLayout.Bounds.Max.y - Widget.Bounds.Max.y; r32 SpaceAbove = ParentLayout.Bounds.Max.y - Widget->Bounds.Max.y;
r32 SpaceBelow = Widget.Bounds.Min.y - ParentLayout.Bounds.Min.y; r32 SpaceBelow = Widget->Bounds.Min.y - ParentLayout.Bounds.Min.y;
ui_layout_direction Direction = LayoutDirection_TopDown; ui_layout_direction Direction = LayoutDirection_TopDown;
rect2 MenuBounds = {}; rect2 MenuBounds = {};
@ -723,8 +733,8 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
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, Widget->Bounds.Max.y },
v2{ Widget.Bounds.Max.x, ParentLayoutMaxY } v2{ Widget->Bounds.Max.x, ParentLayoutMaxY }
}; };
} }
else else
@ -732,14 +742,13 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
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, ParentLayoutMinY },
v2{ Widget.Bounds.Max.x, Widget.Bounds.Min.y } v2{ Widget->Bounds.Max.x, Widget->Bounds.Min.y }
}; };
} }
ui_layout Layout = ui_CreateLayout(Interface, MenuBounds, Direction); ui_widget* Layout = ui_PushLayout(Interface, MenuBounds, Direction);
Layout.WidgetReference = Widget.Id; Layout->WidgetReference = Widget->Id;
ui_PushLayout(Interface, Layout);
} }
return State->Value; return State->Value;
@ -748,28 +757,28 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
internal bool internal bool
ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds) ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
{ {
ui_widget Widget = ui_CreateWidget(Text); ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds); ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
return ui_EvaluateDropdown(Interface, Widget, Result); return ui_EvaluateDropdown(Interface, Widget, Result);
} }
internal bool internal bool
ui_BeginDropdown(ui_interface* Interface, gs_string Text) ui_BeginDropdown(ui_interface* Interface, gs_string Text)
{ {
ui_widget Widget = ui_CreateWidget(Text); ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable); ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground); ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget); ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return ui_EvaluateDropdown(Interface, Widget, Result); return ui_EvaluateDropdown(Interface, Widget, Result);
} }
internal void internal void
ui_EndDropdown(ui_interface* Interface) ui_EndDropdown(ui_interface* Interface)
{ {
ui_layout Layout = Interface->LayoutStack[Interface->LayoutStackCount - 1]; ui_widget* Layout = Interface->ActiveLayout;
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout.WidgetReference); ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference);
if (State) if (State)
{ {
if (State->Value) if (State->Value)

View File

@ -3111,6 +3111,24 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E
// //
// Hashes // Hashes
internal u32
HashAppendDJB2ToU32(u32 Hash, u8 Byte)
{
u32 Result = Hash;
if (Result == 0) { Result = 5381; }
Result = ((Result << 5) + Result) + Byte;
return Result;
}
internal u64
HashAppendDJB2ToU32(u64 Hash, u8 Byte)
{
u64 Result = Hash;
if (Result == 0) { Result = 5381; }
Result = ((Result << 5) + Result) + Byte;
return Result;
}
internal u32 internal u32
HashDJB2ToU32(char* String) HashDJB2ToU32(char* String)
{ {