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

@ -88,6 +88,72 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue)
Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context);
}
internal void
Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget)
{
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawBackground))
{
v4 Color = State->Interface.Style.ButtonColor_Inactive;
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget))
{
Color = State->Interface.Style.ButtonColor_Active;
}
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget))
{
Color = State->Interface.Style.ButtonColor_Selected;
}
PushRenderQuad2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Color);
}
if (Widget.String.Length > 0)
{
v4 Color = State->Interface.Style.TextColor;
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
Widget.String.Length,
State->Interface.Style.Font->BitmapMemory,
State->Interface.Style.Font->BitmapTextureHandle,
State->Interface.Style.Font->BitmapWidth,
State->Interface.Style.Font->BitmapHeight,
State->Interface.Style.Font->BitmapBytesPerPixel,
State->Interface.Style.Font->BitmapStride);
v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin;
switch (Widget.Alignment)
{
case Align_Left:
{
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
}break;
case Align_Right:
{
RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
}break;
InvalidDefaultCase;
}
}
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline))
{
// TODO(pjs): replace these with values from the style
r32 Thickness = 1.0f;
v4 Color = WhiteV4;
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)
{
@ -97,9 +163,32 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB
ui_InterfaceReset(&State->Interface);
State->Interface.RenderBuffer = RenderBuffer;
ui_layout Layout = ui_CreateLayout(&State->Interface, Context->WindowBounds);
ui_PushLayout(&State->Interface, Layout);
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++)
@ -110,66 +199,13 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB
OperationMode.Render(State, RenderBuffer, OperationMode, Context->Mouse, *Context);
}
}
#endif
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))
{
v4 Color = State->Interface.Style.ButtonColor_Inactive;
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget))
{
Color = State->Interface.Style.ButtonColor_Active;
}
if (ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget))
{
Color = State->Interface.Style.ButtonColor_Selected;
}
PushRenderQuad2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Color);
}
if (Widget.String.Length > 0)
{
v4 Color = State->Interface.Style.TextColor;
render_quad_batch_constructor BatchConstructor = PushRenderTexture2DBatch(RenderBuffer,
Widget.String.Length,
State->Interface.Style.Font->BitmapMemory,
State->Interface.Style.Font->BitmapTextureHandle,
State->Interface.Style.Font->BitmapWidth,
State->Interface.Style.Font->BitmapHeight,
State->Interface.Style.Font->BitmapBytesPerPixel,
State->Interface.Style.Font->BitmapStride);
v2 RegisterPosition = Widget.Bounds.Min + State->Interface.Style.Margin;
switch (Widget.Alignment)
{
case Align_Left:
{
RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
}break;
case Align_Right:
{
RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, Color);
}break;
InvalidDefaultCase;
}
}
if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline))
{
// TODO(pjs): replace these with values from the style
r32 Thickness = 1.0f;
v4 Color = WhiteV4;
PushRenderBoundingBox2D(RenderBuffer, Widget.Bounds.Min, Widget.Bounds.Max, Thickness, Color);
}
}
ui_widget Widget = *State->Interface.DrawOrderRoot;
Editor_DrawWidget(State, Context, RenderBuffer, Widget);
Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext);
Context->GeneralWorkQueue->ResetWorkQueue(Context->GeneralWorkQueue);

View File

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

View File

@ -38,18 +38,21 @@ GSMetaTag(panel_type_hierarchy);
internal void
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, Layout);
ui_PushLayout(&State->Interface, PanelBounds, LayoutDirection_TopDown);
// 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);
u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout.RowHeight) + 1;
u32 LineCount = (u32)(Rect2Height(PanelBounds) / Layout->RowHeight) + 1;
u32 AssembliesToDraw = Min(LineCount, State->Assemblies.Count);
rect2* LineBounds = PushArray(State->Transient, rect2, LineCount);
// Fill in alternating color rows for the backgrounds
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);
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_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);
}
@ -80,6 +83,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren
Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback);
}
}
#endif
ui_PopLayout(&State->Interface);
}

View File

@ -25,7 +25,7 @@ ProfilerView_Cleanup(panel* Panel, app_state* State)
}
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{.73f, .33f, .83f, 1},
@ -35,7 +35,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb
v4{.74f, .40f, .25f, 1},
};
rect2 Bounds = ui_LayoutRemaining(Layout);
rect2 Bounds = ui_LayoutRemaining(*Layout);
r32 Width = Rect2Width(Bounds);
r32 DepthHeight = 64;
@ -89,7 +89,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb
}
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];
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);
ui_layout Layout = ui_CreateLayout(&State->Interface, ProcListBounds);
ui_PushLayout(&State->Interface, Layout);
ui_widget* Layout = ui_PushLayout(&State->Interface, ProcListBounds, LayoutDirection_TopDown);
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
// be removed, or used for something else
ui_ReserveElementBounds(&Layout);
ui_ReserveElementBounds(Layout);
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
{
UIWidgetFlag_DrawBackground,
UIWidgetFlag_DrawString,
UIWidgetFlag_DrawOutline,
UIWidgetFlag_Clickable,
};
struct ui_widget_id
{
u64 Index;
u64 LayoutId;
u64 Id;
u64 ParentId;
};
enum ui_layout_direction
{
LayoutDirection_TopDown,
LayoutDirection_BottomUp,
};
struct ui_widget
@ -213,7 +220,31 @@ struct ui_widget
rect2 Bounds;
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
@ -241,34 +272,6 @@ struct interface_config
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
{
ui_widget_id Id;
@ -285,13 +288,13 @@ struct ui_interface
u64 WidgetsCount;
u64 WidgetsCountMax;
ui_widget* DrawOrderHead;
ui_widget* DrawOrderRoot;
ui_widget_id HotWidget;
ui_widget_id ActiveWidget;
#define LAYOUT_COUNT_MAX 8
ui_layout LayoutStack[LAYOUT_COUNT_MAX];
u64 LayoutStackCount;
u64 LayoutIdAcc;
ui_widget* ActiveLayout;
#define RETAINED_STATE_MAX 128
ui_widget_retained_state RetainedState[RETAINED_STATE_MAX];
@ -302,14 +305,14 @@ internal void
ui_InterfaceReset(ui_interface* Interface)
{
Interface->WidgetsCount = 0;
Interface->LayoutStackCount = 0;
Interface->LayoutIdAcc = 0;
Interface->DrawOrderHead = 0;
Interface->DrawOrderRoot = 0;
}
internal bool
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;
}
@ -337,6 +340,30 @@ ui_CreateRetainedState(ui_interface* Interface, ui_widget_id Id)
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
//
@ -351,94 +378,95 @@ ui_MouseClickedRect(ui_interface Interface, rect2 Rect)
// Layout
static ui_layout
ui_CreateLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDirection = LayoutDirection_TopDown)
static ui_widget*
ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir)
{
ui_layout Result = {0};
Result.Bounds = Bounds;
Result.Margin = Interface->Style.Margin;
Result.RowHeight = Interface->Style.RowHeight;
Result.FillDirection = FillDirection;
switch(FillDirection)
ui_widget* Result = ui_CreateWidget(Interface, MakeString("Layout"));
Result->Bounds = Bounds;
Result->Margin = Interface->Style.Margin;
Result->RowHeight = Interface->Style.RowHeight;
Result->FillDirection = FillDir;
switch(FillDir)
{
case LayoutDirection_BottomUp:
{
Result.RowYAt = Bounds.Min.y;
Result->RowYAt = Bounds.Min.y;
}break;
case LayoutDirection_TopDown:
{
Result.RowYAt = Bounds.Max.y - Result.RowHeight;
Result->RowYAt = Bounds.Max.y - Result->RowHeight;
}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;
}
static void
ui_PushLayout(ui_interface* Interface, ui_layout Layout)
{
Assert(Interface->LayoutStackCount < LAYOUT_COUNT_MAX);
Interface->LayoutStack[Interface->LayoutStackCount++] = Layout;
}
internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget, rect2 Bounds);
static void
ui_PopLayout(ui_interface* Interface)
{
Assert(Interface->LayoutStackCount > 0);
Interface->LayoutStackCount -= 1;
Assert(Interface->ActiveLayout != 0);
ui_EvaluateWidget(Interface, Interface->ActiveLayout, Interface->ActiveLayout->Bounds);
Interface->ActiveLayout = Interface->ActiveLayout->Parent;
}
static void
ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0)
{
u64 LayoutIdx = Interface->LayoutStackCount - 1;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true;
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax;
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0;
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
Interface->ActiveLayout->DrawHorizontal = true;
Interface->ActiveLayout->ColumnsMax = ColumnsMax;
Interface->ActiveLayout->ColumnWidths = 0;
Interface->ActiveLayout->ColumnsCount = 0;
}
static void
ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths)
{
u64 LayoutIdx = Interface->LayoutStackCount - 1;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = true;
Interface->LayoutStack[LayoutIdx].ColumnsMax = ColumnsMax;
Interface->LayoutStack[LayoutIdx].ColumnWidths = ColumnWidths;
Interface->LayoutStack[LayoutIdx].ColumnsCount = 0;
Interface->ActiveLayout->DrawHorizontal = true;
Interface->ActiveLayout->ColumnsMax = ColumnsMax;
Interface->ActiveLayout->ColumnWidths = ColumnWidths;
Interface->ActiveLayout->ColumnsCount = 0;
}
static void
ui_EndRow(ui_interface* Interface)
{
u64 LayoutIdx = Interface->LayoutStackCount - 1;
Interface->LayoutStack[LayoutIdx].DrawHorizontal = false;
Interface->LayoutStack[LayoutIdx].ColumnWidths = 0;
Interface->LayoutStack[LayoutIdx].RowYAt -= Interface->LayoutStack[LayoutIdx].RowHeight;
Interface->ActiveLayout->DrawHorizontal = false;
Interface->ActiveLayout->ColumnWidths = 0;
Interface->ActiveLayout->RowYAt -= Interface->ActiveLayout->RowHeight;
}
static b32
ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
ui_TryReserveElementBounds(ui_widget* Widget, rect2* Bounds)
{
b32 Result = true;
if (!Layout->DrawHorizontal)
if (!Widget->DrawHorizontal)
{
Bounds->Min = { Layout->Bounds.Min.x, Layout->RowYAt };
Bounds->Max = { Layout->Bounds.Max.x, Bounds->Min.y + Layout->RowHeight };
Bounds->Min = { Widget->Bounds.Min.x, Widget->RowYAt };
Bounds->Max = { Widget->Bounds.Max.x, Bounds->Min.y + Widget->RowHeight };
switch (Layout->FillDirection)
switch (Widget->FillDirection)
{
case LayoutDirection_BottomUp:
{
Layout->RowYAt += Layout->RowHeight;
Widget->RowYAt += Widget->RowHeight;
}break;
case LayoutDirection_TopDown:
{
Layout->RowYAt -= Layout->RowHeight;
Widget->RowYAt -= Widget->RowHeight;
}break;
InvalidDefaultCase;
@ -446,32 +474,32 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
}
else
{
if (Layout->ColumnsMax > 0)
if (Widget->ColumnsMax > 0)
{
Assert(Layout->ColumnsCount < Layout->ColumnsMax);
if (Layout->ColumnWidths != 0)
Assert(Widget->ColumnsCount < Widget->ColumnsMax);
if (Widget->ColumnWidths != 0)
{
v2 Min = { Layout->Bounds.Min.x, Layout->RowYAt };
for (u32 i = 0; i < Layout->ColumnsCount; i++)
v2 Min = { Widget->Bounds.Min.x, Widget->RowYAt };
for (u32 i = 0; i < Widget->ColumnsCount; i++)
{
Min.x += Layout->ColumnWidths[i];
Min.x += Widget->ColumnWidths[i];
}
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
{
r32 ElementWidth = Rect2Width(Layout->Bounds) / Layout->ColumnsMax;
r32 ElementWidth = Rect2Width(Widget->Bounds) / Widget->ColumnsMax;
Bounds->Min = {
Layout->Bounds.Min.x + (ElementWidth * Layout->ColumnsCount) + Layout->Margin.x,
Layout->RowYAt
Widget->Bounds.Min.x + (ElementWidth * Widget->ColumnsCount) + Widget->Margin.x,
Widget->RowYAt
};
Bounds->Max = {
Bounds->Min.x + ElementWidth - Layout->Margin.x,
Bounds->Min.y + Layout->RowHeight
Bounds->Min.x + ElementWidth - Widget->Margin.x,
Bounds->Min.y + Widget->RowHeight
};
}
Layout->ColumnsCount++;
Widget->ColumnsCount++;
}
else
{
@ -482,7 +510,7 @@ ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds)
}
static rect2
ui_ReserveElementBounds(ui_layout* Layout)
ui_ReserveElementBounds(ui_widget* Layout)
{
rect2 Bounds = {0};
if (!ui_TryReserveElementBounds(Layout, &Bounds))
@ -493,7 +521,7 @@ ui_ReserveElementBounds(ui_layout* Layout)
}
static rect2
ui_LayoutRemaining(ui_layout Layout)
ui_LayoutRemaining(ui_widget Layout)
{
rect2 Result = Layout.Bounds;
Result.Max.y = Layout.RowYAt;
@ -506,15 +534,6 @@ ui_LayoutRemaining(ui_layout Layout)
// Widgets
internal ui_widget
ui_CreateWidget(gs_string String)
{
ui_widget Result = {};
Result.String = String;
Result.Alignment = Align_Left;
return Result;
}
internal void
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 = {};
Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
Widget->Id.Index = Interface->WidgetsCount++;
Widget->Id.LayoutId = Interface->LayoutStack[Interface->LayoutStackCount - 1].Id;
//Assert(Interface->WidgetsCount < Interface->WidgetsCountMax);
//u64 Index = Interface->WidgetsCount++;
Widget->Id.Id = HashDJB2ToU64(StringExpand(Widget->String));
Widget->Id.ParentId = Interface->ActiveLayout->Id.Id;
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))
{
@ -569,7 +591,7 @@ internal ui_eval_result
ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget)
{
rect2 Bounds = {0};
ui_layout* Layout = Interface->LayoutStack + Interface->LayoutStackCount - 1;
ui_widget* Layout = Interface->ActiveLayout;
if (!ui_TryReserveElementBounds(Layout, &Bounds))
{
// 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)
{
DEBUG_TRACK_FUNCTION;
ui_widget Widget = ui_CreateWidget(String);
Widget.Bounds = Bounds;
ui_EvaluateWidget(Interface, &Widget);
ui_widget* Widget = ui_CreateWidget(Interface, String);
Widget->Bounds = Bounds;
ui_EvaluateWidget(Interface, Widget);
}
internal void
ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left)
{
DEBUG_TRACK_FUNCTION;
ui_widget Widget = ui_CreateWidget(String);
ui_EvaluateWidget(Interface, &Widget);
ui_widget* Widget = ui_CreateWidget(Interface, String);
ui_EvaluateWidget(Interface, Widget);
}
static b32
ui_Button(ui_interface* Interface, gs_string Text)
{
ui_widget Widget = ui_CreateWidget(Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return Result.Clicked;
}
static b32
ui_Button(ui_interface* Interface, gs_string Text, rect2 Bounds)
{
ui_widget Widget = ui_CreateWidget(Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds);
ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
return Result.Clicked;
}
@ -666,42 +690,28 @@ ui_GetListItemColors(ui_interface* Interface, u32 ListItemIndex)
static b32
ui_ListButton(ui_interface* Interface, gs_string Text, rect2 Bounds, u32 ListItemIndex)
{
ui_widget Widget = ui_CreateWidget(Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
// TODO(pjs): Reimplement alternating color backgrounds
Widget.Bounds = Bounds;
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
Widget->Bounds = Bounds;
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return Result.Clicked;
}
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);
}
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
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) {
State = ui_CreateRetainedState(Interface, Widget.Id);
State = ui_CreateRetainedState(Interface, Widget->Id);
}
if (EvalResult.Clicked)
@ -711,10 +721,10 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
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 SpaceBelow = Widget.Bounds.Min.y - ParentLayout.Bounds.Min.y;
r32 SpaceAbove = ParentLayout.Bounds.Max.y - Widget->Bounds.Max.y;
r32 SpaceBelow = Widget->Bounds.Min.y - ParentLayout.Bounds.Min.y;
ui_layout_direction Direction = LayoutDirection_TopDown;
rect2 MenuBounds = {};
@ -723,8 +733,8 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
r32 ParentLayoutMaxY = ParentLayout.Bounds.Max.y;
Direction = LayoutDirection_BottomUp;
MenuBounds = rect2{
v2{ Widget.Bounds.Min.x, Widget.Bounds.Max.y },
v2{ Widget.Bounds.Max.x, ParentLayoutMaxY }
v2{ Widget->Bounds.Min.x, Widget->Bounds.Max.y },
v2{ Widget->Bounds.Max.x, ParentLayoutMaxY }
};
}
else
@ -732,14 +742,13 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
r32 ParentLayoutMinY = ParentLayout.Bounds.Min.y;
Direction = LayoutDirection_TopDown;
MenuBounds = rect2{
v2{ Widget.Bounds.Min.x, ParentLayoutMinY },
v2{ Widget.Bounds.Max.x, Widget.Bounds.Min.y }
v2{ Widget->Bounds.Min.x, ParentLayoutMinY },
v2{ Widget->Bounds.Max.x, Widget->Bounds.Min.y }
};
}
ui_layout Layout = ui_CreateLayout(Interface, MenuBounds, Direction);
Layout.WidgetReference = Widget.Id;
ui_PushLayout(Interface, Layout);
ui_widget* Layout = ui_PushLayout(Interface, MenuBounds, Direction);
Layout->WidgetReference = Widget->Id;
}
return State->Value;
@ -748,28 +757,28 @@ ui_EvaluateDropdown(ui_interface* Interface, ui_widget Widget, ui_eval_result Ev
internal bool
ui_BeginDropdown(ui_interface* Interface, gs_string Text, rect2 Bounds)
{
ui_widget Widget = ui_CreateWidget(Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget, Bounds);
ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds);
return ui_EvaluateDropdown(Interface, Widget, Result);
}
internal bool
ui_BeginDropdown(ui_interface* Interface, gs_string Text)
{
ui_widget Widget = ui_CreateWidget(Text);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(&Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, &Widget);
ui_widget* Widget = ui_CreateWidget(Interface, Text);
ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable);
ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground);
ui_eval_result Result = ui_EvaluateWidget(Interface, Widget);
return ui_EvaluateDropdown(Interface, Widget, Result);
}
internal void
ui_EndDropdown(ui_interface* Interface)
{
ui_layout Layout = Interface->LayoutStack[Interface->LayoutStackCount - 1];
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout.WidgetReference);
ui_widget* Layout = Interface->ActiveLayout;
ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->WidgetReference);
if (State)
{
if (State->Value)

View File

@ -3111,6 +3111,24 @@ TimeHandlerGetSecondsElapsed(gs_time_handler TimeHandler, s64 StartCycles, s64 E
//
// 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
HashDJB2ToU32(char* String)
{