diff --git a/src/app/editor/foldhaus_editor.cpp b/src/app/editor/foldhaus_editor.cpp index 5d08c1f..067b4e6 100644 --- a/src/app/editor/foldhaus_editor.cpp +++ b/src/app/editor/foldhaus_editor.cpp @@ -88,17 +88,260 @@ Editor_Update(app_state* State, context* Context, input_queue InputQueue) Editor_HandleInput(State, State->WindowBounds, InputQueue, Context->Mouse, *Context); } +internal void +Editor_DrawWidgetString(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ClippingBox, v4 Color) +{ + 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, ClippingBox, Color); + }break; + + case Align_Right: + { + RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(Widget.String), RegisterPosition, State->Interface.Style.Font, ClippingBox, Color); + }break; + + InvalidDefaultCase; + } +} + +internal void +Editor_DrawWidget(app_state* State, context* Context, render_command_buffer* RenderBuffer, ui_widget Widget, rect2 ParentClipBounds) +{ + rect2 WidgetParentUnion = Widget.Bounds; + WidgetParentUnion = Rect2Union(Widget.Bounds, ParentClipBounds); + + if (!Widget.Parent || (Rect2Area(WidgetParentUnion) > 0)) + { + 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; + } + PushRenderQuad2DClipped(RenderBuffer, Widget.Bounds, WidgetParentUnion, Color); + } + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) + { + v4 Color = State->Interface.Style.TextColor; + Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, Color); + } + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill) || + ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill)) + { + v4 Color = State->Interface.Style.ButtonColor_Selected; + if (ui_WidgetIdsEqual(Widget.Id, State->Interface.HotWidget) || + ui_WidgetIdsEqual(Widget.Id, State->Interface.ActiveWidget)) + { + Color = WhiteV4; + } + + rect2 FillBounds = {}; + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawHorizontalFill)) + { + FillBounds.Min.y = Widget.Bounds.Min.y; + FillBounds.Max.y = Widget.Bounds.Max.y; + r32 FillToPoint = LerpR32(Widget.FillPercent, Widget.Bounds.Min.x, Widget.Bounds.Max.x); + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillReversed)) + { + FillBounds.Min.x = FillToPoint; + FillBounds.Max.x = Widget.Bounds.Max.x; + } + else if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillAsHandle)) + { + FillBounds.Min.x = FillToPoint - 5; + FillBounds.Max.x = FillToPoint + 5; + } + else + { + FillBounds.Min.x = Widget.Bounds.Min.x; + FillBounds.Max.x = FillToPoint; + } + } + else if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawVerticalFill)) + { + FillBounds.Min.x = Widget.Bounds.Min.x; + FillBounds.Max.x = Widget.Bounds.Max.x; + r32 FillToPoint = LerpR32(Widget.FillPercent, Widget.Bounds.Min.y, Widget.Bounds.Max.y); + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillReversed)) + { + FillBounds.Min.y = FillToPoint; + FillBounds.Max.y = Widget.Bounds.Max.y; + } + else if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawFillAsHandle)) + { + FillBounds.Min.y = FillToPoint - 5; + FillBounds.Max.y = FillToPoint + 5; + } + else + { + FillBounds.Min.y = Widget.Bounds.Min.y; + FillBounds.Max.y = FillToPoint; + } + } + PushRenderQuad2DClipped(RenderBuffer, FillBounds, WidgetParentUnion, Color); + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawString) && Widget.String.Length > 0) + { + // TODO(pjs): Mask this text by the horizontal fill + // TODO(pjs): add this color to the style + v4 TextColor = BlackV4; + Editor_DrawWidgetString(State, Context, RenderBuffer, Widget, WidgetParentUnion, TextColor); + } + } + + + if (ui_WidgetIsFlagSet(Widget, UIWidgetFlag_DrawOutline)) + { + // TODO(pjs): replace these with values from the style + r32 Thickness = 1.0f; + v4 Color = WhiteV4; + PushRenderBoundingBox2D(RenderBuffer, WidgetParentUnion.Min, WidgetParentUnion.Max, Thickness, Color); + } + } + + if (Widget.ChildrenRoot) + { + Editor_DrawWidget(State, Context, RenderBuffer, *Widget.ChildrenRoot, WidgetParentUnion); + } + + if (Widget.Next) + { + Editor_DrawWidget(State, Context, RenderBuffer, *Widget.Next, ParentClipBounds); + } +} + +global r32 TestSlider_Value = 5; +global r32 TestSlider_Min = 0; +global r32 TestSlider_Max = 10; +global bool TestToggle = true; + +internal void +TestRender(app_state* State, context* Context, render_command_buffer* RenderBuffer) +{ + ui_InterfaceReset(&State->Interface); + State->Interface.RenderBuffer = RenderBuffer; + State->Interface.WindowBounds = Context->WindowBounds; + + gs_string A = MakeString("TestRender Layout"); + + ui_PushLayout(&State->Interface, A); + { +#if 1 + ui_Label(&State->Interface, MakeString("Spacer")); + ui_Label(&State->Interface, MakeString("Spacer")); + ui_Label(&State->Interface, MakeString("Spacer")); + ui_Label(&State->Interface, MakeString("Spacer")); + ui_Label(&State->Interface, MakeString("Spacer")); + + ui_BeginList(&State->Interface, MakeString("TestList"), 5, 16); + { + ui_BeginRow(&State->Interface, 3); + for (u32 i = 0; i < 16; i++) + { + + ui_Button(&State->Interface, MakeString("B")); + ui_Button(&State->Interface, MakeString("C")); + ui_Button(&State->Interface, MakeString("D")); + } + ui_EndRow(&State->Interface); + } + ui_EndList(&State->Interface); + //ui_Button(&State->Interface, MakeString("B")); + //ui_Button(&State->Interface, MakeString("C")); + //TestSlider_Value = ui_RangeSlider(&State->Interface, MakeString("TestSlider"), TestSlider_Value, TestSlider_Min, TestSlider_Max); +#elif 0 + ui_PushLayout(&State->Interface, MakeString("Outer")); + { + for (u32 i = 0; i < 3; i++) + { + ui_Button(&State->Interface, MakeString("A")); + } + } + 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_PopLayout(&State->Interface); + } + ui_EndRow(&State->Interface); + + 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 Editor_Render(app_state* State, context* Context, render_command_buffer* RenderBuffer) { PushRenderOrthographic(RenderBuffer, State->WindowBounds); PushRenderClearScreen(RenderBuffer); +#if 0 + TestRender(State, Context, RenderBuffer); +#else 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, MakeString("Editor Layout")); DrawAllPanels(State->PanelSystem, RenderBuffer, &Context->Mouse, State, *Context); @@ -112,63 +355,13 @@ Editor_Render(app_state* State, context* Context, render_command_buffer* RenderB } ui_PopLayout(&State->Interface); +#endif // Draw the Interface - for (u32 i = 0; i < State->Interface.WidgetsCount; i++) + if (State->Interface.DrawOrderRoot != 0) { - 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->WindowBounds); } Context->GeneralWorkQueue->CompleteQueueWork(Context->GeneralWorkQueue, Context->ThreadContext); diff --git a/src/app/editor/foldhaus_interface.cpp b/src/app/editor/foldhaus_interface.cpp index 7a83912..29258ec 100644 --- a/src/app/editor/foldhaus_interface.cpp +++ b/src/app/editor/foldhaus_interface.cpp @@ -386,7 +386,8 @@ DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, rect2 FooterB rect2 PanelSelectBtnBounds = MakeRect2MinDim(FooterBounds.Min + v2{30, 1}, v2{100, 23}); - if (ui_BeginDropdown(&State->Interface, MakeString("Select"), PanelSelectBtnBounds)) + panel_definition CurrentDef = State->PanelSystem.PanelDefs[Panel->TypeIndex]; + if (ui_BeginDropdown(&State->Interface, MakeString(CurrentDef.PanelName, CurrentDef.PanelNameLength), PanelSelectBtnBounds)) { for (s32 i = 0; i < GlobalPanelDefsCount; i++) { diff --git a/src/app/editor/panels/foldhaus_panel_animation_timeline.h b/src/app/editor/panels/foldhaus_panel_animation_timeline.h index 9bedf30..412cadf 100644 --- a/src/app/editor/panels/foldhaus_panel_animation_timeline.h +++ b/src/app/editor/panels/foldhaus_panel_animation_timeline.h @@ -241,13 +241,12 @@ SelectAndBeginDragAnimationBlock(animation_timeline_state* TimelineState, handle operation_mode* DragAnimationBlockMode = ActivateOperationModeWithCommands(&State->Modes, DragAnimationBlockCommands, UpdateDragAnimationBlock); drag_animation_block_state* OpState = CreateOperationState(DragAnimationBlockMode, + &State->Modes, drag_animation_block_state); OpState->TimelineBounds = TimelineBounds; OpState->BlockHandle = BlockHandle; OpState->VisibleRange = VisibleRange; - - animation_block* SelectedBlock = Animation_GetBlockFromHandle(ActiveAnim, BlockHandle); OpState->ClipRange = SelectedBlock->Range; } // ------------------- @@ -303,10 +302,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,8 +586,7 @@ PANEL_MODAL_OVERRIDE_CALLBACK(LoadAnimationFileCallback) internal void DrawAnimationPatternList(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, MakeString("AnimClips Layout")); for (s32 i = 0; i < GlobalAnimationPatternsCount; i++) { animation_pattern Pattern = GlobalAnimationPatterns[i]; @@ -609,11 +604,10 @@ 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, MakeString("PlayBar Layout")); ui_FillRect(Interface, Bounds, Interface->Style.PanelBGColors[0]); - ui_StartRow(&State->Interface, 4); + ui_BeginRow(&State->Interface, 4); { if (ui_Button(Interface, MakeString("Pause"))) { @@ -797,14 +791,13 @@ 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, MakeString("AnimInfo Layout")); ui_FillRect(&State->Interface, Bounds, Interface->Style.PanelBGColors[0]); - ui_StartRow(&State->Interface, 2); + ui_BeginRow(&State->Interface, 2); { - ui_DrawString(Interface, MakeString("Active Animation")); + ui_Label(Interface, MakeString("Active Animation")); if (ui_BeginDropdown(Interface, ActiveAnim->Name)) { for (u32 i = 0; i < AnimSystem->Animations.Count; i++) diff --git a/src/app/editor/panels/foldhaus_panel_file_view.h b/src/app/editor/panels/foldhaus_panel_file_view.h index f4427c0..8183829 100644 --- a/src/app/editor/panels/foldhaus_panel_file_view.h +++ b/src/app/editor/panels/foldhaus_panel_file_view.h @@ -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, MakeString("FileView Layout")); if (ui_Button(&State->Interface, MakeString("Exit"))) { @@ -97,7 +96,7 @@ FileView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBu } // Header - ui_DrawString(&State->Interface, FileViewState->WorkingDirectory); + ui_Label(&State->Interface, FileViewState->WorkingDirectory); // File Display for (u32 i = 0; i < FileViewState->FileNames.Count; i++) @@ -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) { diff --git a/src/app/editor/panels/foldhaus_panel_hierarchy.h b/src/app/editor/panels/foldhaus_panel_hierarchy.h index 54b6b8c..ec6ca55 100644 --- a/src/app/editor/panels/foldhaus_panel_hierarchy.h +++ b/src/app/editor/panels/foldhaus_panel_hierarchy.h @@ -38,18 +38,20 @@ 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, MakeString("Hierarchy 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); - 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); v4 ListItemBGColor = ui_GetListItemBGColor(State->Interface.Style, Line); ui_FillRect(&State->Interface, LineBounds[Line], ListItemBGColor); } @@ -62,7 +64,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 +82,7 @@ HierarchyView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Ren Panel_PushModalOverride(Panel, FileBrowser, LoadAssemblyCallback); } } +#endif ui_PopLayout(&State->Interface); } diff --git a/src/app/editor/panels/foldhaus_panel_profiler.h b/src/app/editor/panels/foldhaus_panel_profiler.h index 0f38547..3a49177 100644 --- a/src/app/editor/panels/foldhaus_panel_profiler.h +++ b/src/app/editor/panels/foldhaus_panel_profiler.h @@ -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* Transient) { 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; @@ -45,11 +45,7 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb debug_scope_record_list* ThreadScopeCalls = GetScopeListForThreadInFrame(GlobalDebugServices, VisibleFrame); - scope_record* HotRecord = 0; - scope_name* HotRecordName = 0; - - char Backbuffer[256]; - gs_string String = MakeString(Backbuffer, 0, 256); + gs_string String = PushString(Transient, 256); for (s32 i = 0; i < ThreadScopeCalls->Count; i++) { scope_record* Record = ThreadScopeCalls->Calls + i; @@ -70,41 +66,55 @@ RenderProfiler_ScopeVisualization(ui_interface* Interface, ui_layout Layout, deb if (PointIsInRect(ScopeBounds, Interface->Mouse.Pos)) { Color = GreenV4; - HotRecord = Record; - HotRecordName = Name; + + ui_BeginMousePopup(Interface, rect2{ 25, 25, 300, 57 }, LayoutDirection_TopDown, MakeString("Hover")); + { + PrintF(&String, "%S : %d - %d", Name->Name, Record->StartCycles, Record->EndCycles); + ui_Label(Interface, String); + } + ui_EndMousePopup(Interface); } ui_FillRect(Interface, ScopeBounds, Color); ui_OutlineRect(Interface, ScopeBounds, 1, BlackV4); } } - - if (HotRecord != 0) - { - PrintF(&String, "%S : %d - %d", HotRecordName->Name, HotRecord->StartCycles, HotRecord->EndCycles); - - rect2 TextBounds = MakeRect2MinDim(Interface->Mouse.Pos, v2{256, 32}); - ui_DrawString(Interface, String, TextBounds); - } } 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); - r32 ColumnWidths[] = {256, 128, 128, 128, 128}; - ui_StartRow(Interface, 5, &ColumnWidths[0]); + ui_column_spec ColumnWidths[] = { + { UIColumnSize_Fixed, 256 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }, + { UIColumnSize_Fixed, 128 }}; + ui_BeginRow(Interface, 5, &ColumnWidths[0]); { - ui_DrawString(Interface, MakeString("Procedure")); - ui_DrawString(Interface, MakeString("% Frame")); - ui_DrawString(Interface, MakeString("Seconds")); - ui_DrawString(Interface, MakeString("Cycles")); - ui_DrawString(Interface, MakeString("Calls")); + ui_Label(Interface, MakeString("Procedure")); + ui_Label(Interface, MakeString("% Frame")); + ui_Label(Interface, MakeString("Seconds")); + ui_Label(Interface, MakeString("Cycles")); + ui_Label(Interface, MakeString("Calls")); } ui_EndRow(Interface); + s32 CountedScopes = 0; + for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) + { + scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; + if (NameEntry.Hash != 0) + { + CountedScopes += 1; + } + } + + ui_BeginList(Interface, MakeString("Scope List"), 10, CountedScopes); + ui_BeginRow(Interface, 5, &ColumnWidths[0]); for (s32 n = 0; n < VisibleFrame->ScopeNamesMax; n++) { scope_name NameEntry = VisibleFrame->ScopeNamesHash[n]; @@ -112,26 +122,24 @@ RenderProfiler_ListVisualization(ui_interface* Interface, ui_layout Layout, debu { collated_scope_record* CollatedRecord = VisibleFrame->CollatedScopes + n; - ui_StartRow(Interface, 5, &ColumnWidths[0]); - { - PrintF(&String, "%S", NameEntry.Name); - ui_DrawString(Interface, String); - - PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime); - ui_DrawString(Interface, String); - - PrintF(&String, "%fs", CollatedRecord->TotalSeconds); - ui_DrawString(Interface, String); - - PrintF(&String, "%dcy", CollatedRecord->TotalCycles); - ui_DrawString(Interface, String); - - PrintF(&String, "%d", CollatedRecord->CallCount); - ui_DrawString(Interface, String); - } - ui_EndRow(Interface); + PrintF(&String, "%S", NameEntry.Name); + ui_Label(Interface, String); + + PrintF(&String, "%f%%", CollatedRecord->PercentFrameTime); + ui_Label(Interface, String); + + PrintF(&String, "%fs", CollatedRecord->TotalSeconds); + ui_Label(Interface, String); + + PrintF(&String, "%dcy", CollatedRecord->TotalCycles); + ui_Label(Interface, String); + + PrintF(&String, "%d", CollatedRecord->CallCount); + ui_Label(Interface, String); } } + ui_EndRow(Interface); + ui_EndList(Interface); } GSMetaTag(panel_render); @@ -179,23 +187,22 @@ 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, MakeString("Profiler Layout")); - ui_StartRow(&State->Interface, 4); + ui_BeginRow(&State->Interface, 4); { s64 FrameStartCycles = VisibleFrame->FrameStartCycles; s64 FrameTotalCycles = VisibleFrame->FrameEndCycles - VisibleFrame->FrameStartCycles; u32 CurrentDebugFrame = GlobalDebugServices->CurrentDebugFrame - 1; PrintF(&String, "Frame %d", CurrentDebugFrame); - ui_DrawString(&State->Interface, String); + ui_Label(&State->Interface, String); PrintF(&String, "Total Cycles: %lld", FrameTotalCycles); - ui_DrawString(&State->Interface, String); + ui_Label(&State->Interface, String); // NOTE(NAME): Skipping a space for aesthetic reasons, not functional, and could // be removed, or used for something else - ui_ReserveElementBounds(&Layout); + ui_ReserveBounds(&State->Interface, Layout, true); if (ui_Button(&State->Interface, MakeString("Resume Recording"))) { @@ -204,7 +211,7 @@ ProfilerView_Render(panel* Panel, rect2 PanelBounds, render_command_buffer* Rend } ui_EndRow(&State->Interface); - ui_StartRow(&State->Interface, 8); + ui_BeginRow(&State->Interface, 8); { if (ui_Button(&State->Interface, MakeString("Scope View"))) { diff --git a/src/app/foldhaus_app.cpp b/src/app/foldhaus_app.cpp index a1b0d95..3d74388 100644 --- a/src/app/foldhaus_app.cpp +++ b/src/app/foldhaus_app.cpp @@ -106,10 +106,12 @@ INITIALIZE_APPLICATION(InitializeApplication) State->Interface.Style.ListBGHover = v4{ .22f, .22f, .22f, 1.f }; State->Interface.Style.ListBGSelected = v4{.44f, .44f, .44f, 1.f }; 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.Widgets = PushArray(&State->Permanent, ui_widget, State->Interface.WidgetsCountMax); + State->Interface.PerFrameMemory = PushStruct(&State->Permanent, gs_memory_arena); + *State->Interface.PerFrameMemory = CreateMemoryArena(Context.ThreadContext.Allocator); State->SACN = SACN_Initialize(Context); @@ -152,7 +154,6 @@ INITIALIZE_APPLICATION(InitializeApplication) State->AnimationSystem.TimelineShouldAdvance = true; } // End Animation Playground - PanelSystem_Init(&State->PanelSystem, GlobalPanelDefs, GlobalPanelDefsCount, &State->Permanent); PanelSystem_PushPanel(&State->PanelSystem, PanelType_SculptureView, State, Context); } diff --git a/src/app/foldhaus_debug.h b/src/app/foldhaus_debug.h index ca26188..7acdfcc 100644 --- a/src/app/foldhaus_debug.h +++ b/src/app/foldhaus_debug.h @@ -54,6 +54,7 @@ struct debug_frame s64 FrameEndCycles; s32 ScopeNamesMax; + s32 ScopeNamesCount; scope_name* ScopeNamesHash; s32 ThreadCount; @@ -302,6 +303,7 @@ EndDebugFrame (debug_services* Services) CollateThreadScopeCalls(ClosingFrame->ThreadCalls + t, ClosingFrame); } + s32 ScopeNamesCount = 0; for (s32 n = 0; n < ClosingFrame->ScopeNamesMax; n++) { if (ClosingFrame->ScopeNamesHash[n].Hash != 0) @@ -310,8 +312,10 @@ EndDebugFrame (debug_services* Services) CollatedRecord->TotalSeconds = (r32)CollatedRecord->TotalCycles / (r32)Services->PerformanceCountFrequency; CollatedRecord->PercentFrameTime = (r32)CollatedRecord->TotalCycles / (r32)FrameTotalCycles; CollatedRecord->AverageSecondsPerCall = CollatedRecord->TotalSeconds / CollatedRecord->CallCount; + ScopeNamesCount += 1; } } + ClosingFrame->ScopeNamesCount = ScopeNamesCount; Services->CurrentDebugFrame = (Services->CurrentDebugFrame + 1) % DEBUG_FRAME_COUNT; StartDebugFrame(&Services->Frames[Services->CurrentDebugFrame], Services); diff --git a/src/app/foldhaus_renderer.h b/src/app/foldhaus_renderer.h index e316c71..a40c0f4 100644 --- a/src/app/foldhaus_renderer.h +++ b/src/app/foldhaus_renderer.h @@ -589,6 +589,21 @@ PushRenderQuad2D (render_command_buffer* Buffer, v2 Min, v2 Max, v4 Color) PushQuad2DOnBatch(&Batch, Min, Max, Color); } +internal void +PushRenderQuad2D (render_command_buffer* Buffer, rect2 Rect, v4 Color) +{ + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, Rect.Min, Rect.Max, Color); +} + +internal void +PushRenderQuad2DClipped (render_command_buffer* Buffer, rect2 Rect, rect2 ClippingBox, v4 Color) +{ + rect2 Clipped = Rect2Union(Rect, ClippingBox); + render_quad_batch_constructor Batch = PushRenderQuad2DBatch(Buffer, 1); + PushQuad2DOnBatch(&Batch, Clipped.Min, Clipped.Max, Color); +} + internal void PushRenderQuad2D(render_command_buffer* Buffer, v2 P0, v2 P1, v2 P2, v2 P3, v4 Color) { diff --git a/src/app/interface.h b/src/app/interface.h index d390069..3cd2772 100644 --- a/src/app/interface.h +++ b/src/app/interface.h @@ -5,14 +5,6 @@ // #ifndef INTERFACE_H -// Widget Capabilities -// - string -// - background -// - outline -// - active (mouse is interacting) -// - hot (mouse could be about to interact) -// - retained state - if a toggle is active, or a drop down is open - enum gs_string_alignment { Align_Left, @@ -21,22 +13,60 @@ enum gs_string_alignment }; internal void -DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, v4 Color) +ClipUVRect(rect2* Bounds, rect2* UVs, rect2 ClippingBox) { + rect2 NewBounds = Rect2Union(*Bounds, ClippingBox); + + r32 OldWidth = Rect2Width(*Bounds); + r32 OldHeight = Rect2Height(*Bounds); + + v2 MinInsetPercent = v2{ + (NewBounds.Min.x - Bounds->Min.x) / OldWidth, + (NewBounds.Min.y - Bounds->Min.y) / OldHeight, + }; + + v2 MaxInsetPercent = v2{ + (NewBounds.Max.x - Bounds->Min.x) / OldWidth, + (NewBounds.Max.y - Bounds->Min.y) / OldHeight, + }; + + UVs->Min.x = LerpR32(MinInsetPercent.x, UVs->Min.x, UVs->Max.x); + UVs->Min.y = LerpR32(MinInsetPercent.y, UVs->Min.y, UVs->Max.y); + UVs->Max.x = LerpR32(MaxInsetPercent.x, UVs->Min.x, UVs->Max.x); + UVs->Max.y = LerpR32(MaxInsetPercent.y, UVs->Min.y, UVs->Max.y); + + *Bounds = NewBounds; +} + +internal void +DrawCharacter_ (render_quad_batch_constructor* BatchConstructor, r32 MinX, r32 MinY, codepoint_bitmap CodepointInfo, rect2 ClippingBox, v4 Color) +{ + rect2 Bounds = {}; + Bounds.Min.x = FloorR32(MinX); + Bounds.Min.y = FloorR32(MinY); + Bounds.Max.x = Bounds.Min.x + (CodepointInfo.Width); + Bounds.Max.y = Bounds.Min.y + (CodepointInfo.Height); + + rect2 UVBounds = {}; + UVBounds.Min = CodepointInfo.UVMin; + UVBounds.Max = CodepointInfo.UVMax; + + ClipUVRect(&Bounds, &UVBounds, ClippingBox); + s32 AlignedMinX = (s32)(MinX); s32 AlignedMinY = (s32)(MinY); s32 AlignedMaxX = AlignedMinX + (CodepointInfo.Width); s32 AlignedMaxY = AlignedMinY + (CodepointInfo.Height); PushQuad2DOnBatch(BatchConstructor, - v2{(r32)AlignedMinX, (r32)AlignedMinY}, v2{(r32)AlignedMaxX, (r32)AlignedMinY}, - v2{(r32)AlignedMaxX, (r32)AlignedMaxY}, v2{(r32)AlignedMinX, (r32)AlignedMaxY}, - CodepointInfo.UVMin, CodepointInfo.UVMax, + Rect2BottomLeft(Bounds), Rect2BottomRight(Bounds), + Rect2TopRight(Bounds), Rect2TopLeft(Bounds), + UVBounds.Min, UVBounds.Max, Color); } internal v2 -DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, v4 Color) +DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color) { s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; @@ -44,7 +74,7 @@ DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char // NOTE(Peter): r32 MinX = Position.x + CodepointInfo.XOffset; r32 MinY = Position.y + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, Color); + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); // NOTE(Peter): v2 PointAfterCharacter = v2{Position.x + CodepointInfo.Width, Position.y}; @@ -52,7 +82,7 @@ DrawCharacterLeftAligned (render_quad_batch_constructor* BatchConstructor, char } internal v2 -DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, v4 Color) +DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char C, bitmap_font Font, v2 Position, rect2 ClippingBox, v4 Color) { s32 GlyphDataIndex = GetIndexForCodepoint(Font, C); codepoint_bitmap CodepointInfo = Font.CodepointValues[GlyphDataIndex]; @@ -60,7 +90,7 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char // NOTE(Peter): r32 MinX = Position.x - (CodepointInfo.XOffset + CodepointInfo.Width); r32 MinY = Position.y + CodepointInfo.YOffset + CodepointInfo.YOffset; - DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, Color); + DrawCharacter_(BatchConstructor, MinX, MinY, CodepointInfo, ClippingBox, Color); // NOTE(Peter): v2 PointAfterCharacter = v2{Position.x-(CodepointInfo.Width + CodepointInfo.XOffset), Position.y}; @@ -68,13 +98,13 @@ DrawCharacterRightAligned (render_quad_batch_constructor* BatchConstructor, char } internal v2 -DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, v4 Color) +DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color) { v2 RegisterPosition = InitialRegisterPosition; char* C = gs_string; for (s32 i = 0; i < Length; i++) { - v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, Color); + v2 PositionAfterCharacter = DrawCharacterLeftAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); RegisterPosition.x = PositionAfterCharacter.x; C++; } @@ -82,13 +112,13 @@ DrawStringLeftAligned (render_quad_batch_constructor* BatchConstructor, s32 Leng } internal v2 -DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, v4 Color) +DrawStringRightAligned (render_quad_batch_constructor* BatchConstructor, s32 Length, char* gs_string, v2 InitialRegisterPosition, bitmap_font* Font, rect2 ClippingBox, v4 Color) { v2 RegisterPosition = InitialRegisterPosition; char* C = gs_string + Length - 1; for (s32 i = Length - 1; i >= 0; i--) { - v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, Color); + v2 PositionAfterCharacter = DrawCharacterRightAligned(BatchConstructor, *C, *Font, RegisterPosition, ClippingBox, Color); RegisterPosition.x = PositionAfterCharacter.x; C--; } @@ -109,14 +139,22 @@ DrawString(render_command_buffer* RenderBuffer, gs_string String, bitmap_font* F Font->BitmapBytesPerPixel, Font->BitmapStride); + // TODO(pjs): I don't like this solution but it'll do for now and I want to focus on other problems + // especially since I think this one will go away once I finish the ui overhaul + rect2 InfiniteClipBox = {}; + InfiniteClipBox.Min.x = -100000; + InfiniteClipBox.Min.y = -100000; + InfiniteClipBox.Max.x = 100000; + InfiniteClipBox.Max.y = 100000; + v2 RegisterPosition = Position; if (Alignment == Align_Left) { - RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); + RegisterPosition = DrawStringLeftAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); } else if (Alignment == Align_Right) { - RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, Color); + RegisterPosition = DrawStringRightAligned(&BatchConstructor, StringExpand(String), RegisterPosition, Font, InfiniteClipBox, Color); } else { @@ -136,6 +174,7 @@ DrawCursor (render_quad_batch_constructor* BatchConstructor, v2 Position, v4 Col PushQuad2DOnBatch(BatchConstructor, Min, Max, Color); } +#if 0 internal v2 DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 CursorPosition, bitmap_font* Font, v2 Position, v4 Color, v4 CursorColor, gs_string_alignment Alignment = Align_Left) { @@ -187,21 +226,38 @@ DrawStringWithCursor (render_command_buffer* RenderBuffer, gs_string String, s32 LowerRight.x = RegisterPosition.x; return LowerRight; } - -// TODO(pjs): remove the need for htis (go thru and remove code that's in the #else block of #ifdef EXTERNAL_RENDERER s -#define EXTERNAL_RENDERER +#endif enum ui_widget_flag { + UIWidgetFlag_ExpandsToFitChildren, UIWidgetFlag_DrawBackground, + UIWidgetFlag_DrawString, UIWidgetFlag_DrawOutline, UIWidgetFlag_Clickable, + UIWidgetFlag_DrawHorizontalFill, + UIWidgetFlag_DrawVerticalFill, + UIWidgetFlag_DrawFillReversed, + UIWidgetFlag_DrawFillAsHandle, }; struct ui_widget_id { - u64 Index; - u64 LayoutId; + u64 Id; + u64 ParentId; +}; + +enum ui_layout_direction +{ + LayoutDirection_TopDown, + LayoutDirection_BottomUp, + LayoutDirection_Inherit, +}; + +struct ui_column +{ + r32 XMin; + r32 XMax; }; struct ui_widget @@ -213,12 +269,40 @@ struct ui_widget rect2 Bounds; u64 Flags; - bool RetainedState; + + ui_widget* Next; + + // Slider + r32 FillPercent; + + // Layout + ui_widget* Parent; + + v2 Margin; + r32 RowHeight; + r32 RowYAt; + + ui_layout_direction FillDirection; + + ui_column* Columns; + u32 ColumnsCount; + u32 ColumnsFilled; + + // 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 { bool Clicked; + bool Held; + v2 DragDelta; }; struct interface_config @@ -241,75 +325,89 @@ 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; bool Value; + r32 InitialValueR32; + u32 FramesSinceAccess; + + // For use in layouts that allow you to scroll / pan + v2 ChildrenDrawOffset; }; struct ui_interface { interface_config Style; + mouse_state Mouse; + rect2 WindowBounds; + render_command_buffer* RenderBuffer; ui_widget* Widgets; 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]; u64 RetainedStateCount; + + gs_memory_arena* PerFrameMemory; }; internal void ui_InterfaceReset(ui_interface* Interface) { Interface->WidgetsCount = 0; - Interface->LayoutStackCount = 0; - Interface->LayoutIdAcc = 0; + Interface->DrawOrderHead = 0; + Interface->DrawOrderRoot = 0; + ClearArena(Interface->PerFrameMemory); + + for (u32 i = 0; i < Interface->RetainedStateCount; i++) + { + Interface->RetainedState[i].FramesSinceAccess += 1; + if (Interface->RetainedState[i].FramesSinceAccess > 1) + { + Interface->RetainedState[i] = {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; +} + +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; } @@ -321,6 +419,7 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) { if (ui_WidgetIdsEqual(Interface->RetainedState[i].Id, Id)) { + Interface->RetainedState[i].FramesSinceAccess = 0; Result = Interface->RetainedState + i; break; } @@ -329,11 +428,51 @@ ui_GetRetainedState(ui_interface* Interface, ui_widget_id Id) } internal ui_widget_retained_state* -ui_CreateRetainedState(ui_interface* Interface, ui_widget_id Id) +ui_CreateRetainedState(ui_interface* Interface, ui_widget* Widget) { u64 Index = Interface->RetainedStateCount++; ui_widget_retained_state* Result = Interface->RetainedState + Index; - Result->Id = Id; + Result->Id = Widget->Id; + return Result; +} + +internal ui_widget_retained_state* +ui_GetOrCreateRetainedState(ui_interface* Interface, ui_widget* Widget) +{ + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); + } + return State; +} + +internal ui_widget* +ui_CreateWidget(ui_interface* Interface, gs_string String) +{ + Assert(Interface->WidgetsCount < Interface->WidgetsCountMax); + ui_widget* Result = Interface->Widgets + Interface->WidgetsCount++; + ZeroStruct(Result); + + 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.ParentId = Result->Parent->Id.Id; + } + Result->Id.Id = Id; + + Result->String = PushStringCopy(Interface->PerFrameMemory, String.ConstString); + Result->Alignment = Align_Left; + Result->Next = 0; + Result->ChildrenRoot = 0; + Result->ChildrenHead = 0; + Result->Flags = 0; + ui_WidgetSetFlag(Result, UIWidgetFlag_ExpandsToFitChildren); + return Result; } @@ -351,231 +490,405 @@ 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 rect2 +ui_ReserveBounds(ui_interface* Interface, ui_widget* Widget, bool Inset) { - ui_layout Result = {0}; - Result.Bounds = Bounds; - Result.Margin = Interface->Style.Margin; - Result.RowHeight = Interface->Style.RowHeight; - Result.FillDirection = FillDirection; - switch(FillDirection) + Assert(Widget->ColumnsCount > 0); + rect2 Bounds = {0}; + u32 ColumnIndex = Widget->ChildCount % Widget->ColumnsCount; + + ui_column Column = Widget->Columns[ColumnIndex]; + Bounds.Min.x = Column.XMin; + Bounds.Min.y = Widget->RowYAt; + Bounds.Max.x = Column.XMax; + Bounds.Max.y = Bounds.Min.y + Widget->RowHeight; + + 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; + } + + if (Widget->ChildCount == 0) + { + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (State) + { + Bounds = Rect2Translate(Bounds, State->ChildrenDrawOffset); + } + } + + 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: + { + Parent->RowYAt = Bounds.Max.y; + }break; + + case LayoutDirection_TopDown: + { + Parent->RowYAt = Bounds.Min.y - Parent->RowHeight; + }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; + Result->RowYAt = Bounds.Min.y; }break; case LayoutDirection_TopDown: { - Result.RowYAt = Bounds.Max.y - Result.RowHeight; + Result->RowYAt = Bounds.Max.y - Result->RowHeight; }break; + + InvalidDefaultCase; } - Result.Id = ++Interface->LayoutIdAcc; - return Result; } -static void -ui_PushLayout(ui_interface* Interface, ui_layout Layout) +static ui_widget* +ui_PushOverlayLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) { - Assert(Interface->LayoutStackCount < LAYOUT_COUNT_MAX); - Interface->LayoutStack[Interface->LayoutStackCount++] = Layout; + ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); + SLLPushOrInit(Interface->DrawOrderRoot, Interface->DrawOrderHead, Result); + Interface->ActiveLayout = Result; + return Result; +} + +static ui_widget* +ui_PushLayout(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Name) +{ + ui_widget* Result = ui_CreateLayoutWidget(Interface, Bounds, Name, FillDir); + + 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 ui_widget* +ui_PushLayout(ui_interface* Interface, gs_string Name, bool Inset = true) +{ + rect2 Bounds = {}; + ui_layout_direction Direction = LayoutDirection_TopDown; + if (Interface->ActiveLayout) + { + Bounds = ui_ReserveBounds(Interface, 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) +{ + if (!ui_WidgetIsFlagSet(*Parent, UIWidgetFlag_ExpandsToFitChildren)) { return; } + + 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 = Max(Extents.y + Parent->Margin.y, Parent->Bounds.Max.y); + }break; + + case LayoutDirection_TopDown: + { + Parent->Bounds.Min.y = Min(Extents.x - Parent->Margin.y, Parent->Bounds.Min.y); + }break; + + InvalidDefaultCase; + } } static void ui_PopLayout(ui_interface* Interface) { - Assert(Interface->LayoutStackCount > 0); - Interface->LayoutStackCount -= 1; + Assert(Interface->ActiveLayout != 0); + + ui_widget* Layout = Interface->ActiveLayout; + ui_ExpandToFitChildren(Layout); + + 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 -ui_StartRow(ui_interface* Interface, u32 ColumnsMax = 0) +static ui_widget* +ui_BeginRow(ui_interface* Interface, u32 ColumnsMax) { - 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; + ui_widget* Layout = ui_PushLayout(Interface, MakeString("Row"), false); + ui_WidgetCreateColumns(Layout, ColumnsMax, Interface); + ui_WidgetInitUniformColumns(Layout); + return Layout; } -static void -ui_StartRow(ui_interface* Interface, u32 ColumnsMax, r32* ColumnWidths) +enum ui_column_size_rule { - 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; + UIColumnSize_Fixed, + UIColumnSize_Percent, + UIColumnSize_Fill, +}; + +struct ui_column_spec +{ + ui_column_size_rule Rule; + union + { + r32 Width; + r32 Percent; + }; +}; + +static ui_widget* +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 + // If a size is specified, it is stored in Column->XMax + 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; + } + + return Layout; } 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; -} - -static b32 -ui_TryReserveElementBounds(ui_layout* Layout, rect2* Bounds) -{ - b32 Result = true; - if (!Layout->DrawHorizontal) - { - Bounds->Min = { Layout->Bounds.Min.x, Layout->RowYAt }; - Bounds->Max = { Layout->Bounds.Max.x, Bounds->Min.y + Layout->RowHeight }; - - switch (Layout->FillDirection) - { - case LayoutDirection_BottomUp: - { - Layout->RowYAt += Layout->RowHeight; - }break; - - case LayoutDirection_TopDown: - { - Layout->RowYAt -= Layout->RowHeight; - }break; - - InvalidDefaultCase; - } - } - else - { - if (Layout->ColumnsMax > 0) - { - Assert(Layout->ColumnsCount < Layout->ColumnsMax); - if (Layout->ColumnWidths != 0) - { - v2 Min = { Layout->Bounds.Min.x, Layout->RowYAt }; - for (u32 i = 0; i < Layout->ColumnsCount; i++) - { - Min.x += Layout->ColumnWidths[i]; - } - Bounds->Min = Min; - Bounds->Max = Bounds->Min + v2{ Layout->ColumnWidths[Layout->ColumnsCount], Layout->RowHeight }; - } - else - { - r32 ElementWidth = Rect2Width(Layout->Bounds) / Layout->ColumnsMax; - Bounds->Min = { - Layout->Bounds.Min.x + (ElementWidth * Layout->ColumnsCount) + Layout->Margin.x, - Layout->RowYAt - }; - Bounds->Max = { - Bounds->Min.x + ElementWidth - Layout->Margin.x, - Bounds->Min.y + Layout->RowHeight - }; - } - Layout->ColumnsCount++; - } - else - { - Result = false; - } - } - return Result; + ui_PopLayout(Interface); } static rect2 -ui_ReserveElementBounds(ui_layout* Layout) -{ - rect2 Bounds = {0}; - if (!ui_TryReserveElementBounds(Layout, &Bounds)) - { - InvalidCodePath; - } - return Bounds; -} - -static rect2 -ui_LayoutRemaining(ui_layout Layout) +ui_LayoutRemaining(ui_widget Layout) { rect2 Result = Layout.Bounds; Result.Max.y = Layout.RowYAt; - if (Layout.DrawHorizontal) - { - Result.Max.y -= Layout.RowHeight; - } return Result; } // 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) -{ - 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_eval_result 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; - Widget->Bounds = Bounds; - Interface->Widgets[Widget->Id.Index] = *Widget; + SLLPushOrInit(Interface->ActiveLayout->ChildrenRoot, Interface->ActiveLayout->ChildrenHead, Widget); + Interface->ActiveLayout->ChildCount += 1; + ui_CommitBounds(Widget->Parent, Widget->Bounds); if (ui_WidgetIsFlagSet(*Widget, UIWidgetFlag_Clickable)) { - if (PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) + if (PointIsInRect(Widget->Parent->Bounds, Interface->Mouse.Pos) && + PointIsInRect(Widget->Bounds, Interface->Mouse.Pos)) { if (ui_WidgetIdsEqual(Interface->HotWidget, Widget->Id) && MouseButtonTransitionedDown(Interface->Mouse.LeftButtonState)) { + Assert(!ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id)); Result.Clicked = true; Interface->ActiveWidget = Widget->Id; } - - if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) && - MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState)) - { - Interface->ActiveWidget = {}; - } - Interface->HotWidget = Widget->Id; } + + if (MouseButtonHeldDown(Interface->Mouse.LeftButtonState) && + PointIsInRect(Widget->Bounds, Interface->Mouse.DownPos)) + { + Result.Held = true; + Result.DragDelta = Interface->Mouse.Pos - Interface->Mouse.DownPos; + } + + if (ui_WidgetIdsEqual(Interface->ActiveWidget, Widget->Id) && + MouseButtonTransitionedUp(Interface->Mouse.LeftButtonState)) + { + Interface->ActiveWidget = {}; + } + } + Assert(Widget->Parent != 0); return Result; } internal ui_eval_result ui_EvaluateWidget(ui_interface* Interface, ui_widget* Widget) { - rect2 Bounds = {0}; - ui_layout* Layout = Interface->LayoutStack + Interface->LayoutStackCount - 1; - if (!ui_TryReserveElementBounds(Layout, &Bounds)) - { - // TODO(pjs): This isn't invalid, but Idk when we'd hit this case yet - InvalidCodePath; - } - + ui_widget* Layout = Interface->ActiveLayout; + rect2 Bounds = ui_ReserveBounds(Interface, Layout, true); return ui_EvaluateWidget(Interface, Widget, Bounds); } @@ -603,39 +916,47 @@ ui_OutlineRect(ui_interface* Interface, rect2 Bounds, r32 Thickness, v4 Color) } internal void -ui_DrawString(ui_interface* Interface, gs_string String, rect2 Bounds, gs_string_alignment Alignment = Align_Left) +ui_Label(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); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget, Bounds); } internal void -ui_DrawString(ui_interface* Interface, gs_string String, gs_string_alignment Alignment = Align_Left) +ui_Label(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_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_EvaluateWidget(Interface, Widget); } -static b32 +internal ui_widget* +ui_CreateButtonWidget(ui_interface* Interface, gs_string Text) +{ + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + return Widget; +} + +internal 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_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); return Result.Clicked; } -static b32 +internal 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_CreateButtonWidget(Interface, Text); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); return Result.Clicked; } @@ -666,42 +987,29 @@ 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); - if (!State) { - State = ui_CreateRetainedState(Interface, Widget.Id); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Widget->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Widget); } if (EvalResult.Clicked) @@ -711,10 +1019,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 +1031,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 - ParentLayout.Margin.x, Widget->Bounds.Max.y }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.x, ParentLayoutMaxY } }; } else @@ -732,14 +1040,15 @@ 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 - ParentLayout.Margin.x, ParentLayoutMinY }, + v2{ Widget->Bounds.Max.x + ParentLayout.Margin.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_PushOverlayLayout(Interface, MenuBounds, Direction, MakeString("WidgetLayout")); + Layout->Margin.y = 0; + Layout->WidgetReference = Widget->Id; + ui_WidgetClearFlag(Layout, UIWidgetFlag_DrawOutline); } return State->Value; @@ -748,28 +1057,32 @@ 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_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + 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_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + 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) @@ -779,205 +1092,170 @@ ui_EndDropdown(ui_interface* Interface) } } -// -// OLD -// - -enum selection_state +internal r32 +ui_EvaluateRangeSlider(ui_interface* Interface, ui_widget* Widget, ui_eval_result EvalResult, r32 Value, r32 MinValue, r32 MaxValue) { - Selection_None, - Selection_Selected, - Selection_Deselected, -}; - -struct interface_list -{ - rect2 ListBounds; + r32 NewValue = Value; + ui_widget_retained_state* State = ui_GetOrCreateRetainedState(Interface, Widget); - v2 ListElementDimensions; - v2 ElementLabelIndent; - - v4 TextColor; - v4* LineBGColors; - s32 LineBGColorsCount; - v4 LineBGHoverColor; - - s32 ListElementsCount; -}; - -internal rect2 -DrawListElementBackground(interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer) -{ - rect2 LineBounds = {}; - LineBounds.Min = v2{ - List->ListBounds.Min.x, - List->ListBounds.Max.y - (List->ListElementDimensions.y * (List->ListElementsCount + 1)) - }; - LineBounds.Max = LineBounds.Min + List->ListElementDimensions; - - v4 Color = List->LineBGColors[List->ListElementsCount % List->LineBGColorsCount]; - if (PointIsInRect(LineBounds, Mouse.Pos)) + if (EvalResult.Clicked) { - Color = List->LineBGHoverColor; + State->InitialValueR32 = Value; } - PushRenderQuad2D(RenderBuffer, LineBounds.Min, LineBounds.Max, Color); - return LineBounds; + if (EvalResult.Held) + { + r32 Percent = (Interface->Mouse.Pos.x - Widget->Bounds.Min.x) / Rect2Width(Widget->Bounds); + NewValue = LerpR32(Percent, MinValue, MaxValue); + } + + NewValue = Clamp(MinValue, NewValue, MaxValue); + Widget->FillPercent = RemapR32(NewValue, MinValue, MaxValue, 0, 1); + return NewValue; } -internal rect2 -DrawListElement(gs_string Label, interface_list* List, mouse_state Mouse, render_command_buffer* RenderBuffer, interface_config Interface) +internal ui_widget* +ui_CreateRangeSliderWidget(ui_interface* Interface, gs_string Text, r32 Value) { - rect2 Bounds = DrawListElementBackground(List, Mouse, RenderBuffer); - - v2 LabelPosition = Bounds.Min + List->ElementLabelIndent; - DrawString(RenderBuffer, Label, Interface.Font, LabelPosition, List->TextColor); - - List->ListElementsCount++; - return Bounds; + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawString); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + Widget->String = PushStringF(Interface->PerFrameMemory, 128, "%f", Value); + return Widget; } - internal r32 -EvaluateColorChannelSlider (render_command_buffer* RenderBuffer, v4 ChannelMask, v2 Min, v2 Max, r32 Current, mouse_state Mouse) +ui_RangeSlider(ui_interface* Interface, gs_string Text, r32 Value, r32 ValueMin, r32 ValueMax) { - r32 Result = Current; + ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget); + return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); - // TODO(Peter): Can this come from outside the function? Would rather pass rect around than min/max - rect2 Rect = rect2{ Min, Max }; +} + +internal r32 +ui_RangeSlider(ui_interface* Interface, gs_string Text, rect2 Bounds, r32 Value, r32 ValueMin, r32 ValueMax) +{ + ui_widget* Widget = ui_CreateRangeSliderWidget(Interface, Text, Value); + ui_eval_result Result = ui_EvaluateWidget(Interface, Widget, Bounds); + return ui_EvaluateRangeSlider(Interface, Widget, Result, Value, ValueMin, ValueMax); +} + +internal bool +ui_Toggle(ui_interface* Interface, gs_string Text, bool Value) +{ + ui_widget* Widget = ui_CreateWidget(Interface, Text); + ui_WidgetSetFlag(Widget, UIWidgetFlag_Clickable); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawBackground); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawHorizontalFill); + ui_WidgetSetFlag(Widget, UIWidgetFlag_DrawOutline); + ui_eval_result Eval = ui_EvaluateWidget(Interface, Widget); - render_quad_batch_constructor Batch = PushRenderQuad2DBatch(RenderBuffer, 2); - - v4 LeftColor = ChannelMask * 0; - LeftColor.a = 1.f; - v4 RightColor = ChannelMask; - PushQuad2DOnBatch(&Batch, - RectBottomLeft(Rect), RectBottomRight(Rect), - RectTopRight(Rect), RectTopLeft(Rect), - v2{0, 0}, v2{1, 0}, v2{1, 1}, v2{0, 1}, - LeftColor, RightColor, RightColor, LeftColor); - - if (MouseButtonTransitionedDown(Mouse.LeftButtonState)) + bool Result = Eval.Clicked ? !Value : Value; + Widget->FillPercent = Result ? 1.0f : 0.0f; + return Result; +} + +internal void +ui_BeginList(ui_interface* Interface, gs_string Text, u32 ViewportRows, u32 ElementCount) +{ + if (ElementCount < ViewportRows) { - if (PointIsInRect(Rect, Mouse.DownPos)) - { - Result = ((r32)Mouse.Pos.x - Min.x) / (Max.x - Min.x); - Result = Clamp01(Result); - } + ViewportRows = ElementCount; } - r32 DragBarWidth = 8; - v2 DragBarMin = v2{ - LerpR32(Result, Min.x, Max.x) - (DragBarWidth / 2), - Min.y - 2 + ui_column_spec ColumnRules[] = { + { UIColumnSize_Fixed, 32 }, + { UIColumnSize_Fill, 0 }, }; - v2 DragBarMax = DragBarMin + v2{DragBarWidth, (Max.y - Min.y) + 4}; + ui_widget* Layout = ui_BeginRow(Interface, 2, ColumnRules); + ui_WidgetClearFlag(Layout, UIWidgetFlag_ExpandsToFitChildren); - PushQuad2DOnBatch(&Batch, DragBarMin, DragBarMax, v4{.3f, .3f, .3f, 1.f}); + ui_widget_retained_state* State = ui_GetRetainedState(Interface, Layout->Id); + if (!State) + { + State = ui_CreateRetainedState(Interface, Layout); + State->InitialValueR32 = 1.0f; + } - return Result; + r32 LayoutHeight = Layout->RowHeight * ViewportRows; + switch (Layout->Parent->FillDirection) + { + case LayoutDirection_TopDown: + { + Layout->Bounds.Min.y = Layout->Bounds.Max.y - LayoutHeight; + }break; + + case LayoutDirection_BottomUp: + { + Layout->Bounds.Max.y = Layout->Bounds.Min.y + LayoutHeight; + }break; + + InvalidDefaultCase; + } + + // Create the scroll bar + // + // TODO(pjs): Maybe make this a vertical slider widget? + + ui_widget* SliderRegion = ui_CreateWidget(Interface, MakeString("Slider")); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawOutline); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawVerticalFill); + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_DrawFillAsHandle); + + ui_WidgetSetFlag(SliderRegion, UIWidgetFlag_Clickable); + + rect2 SliderBounds = ui_ReserveBounds(Interface, Layout, true); + SliderBounds.Min.y = Layout->Bounds.Min.y + Layout->Margin.y; + SliderBounds.Max.y = Layout->Bounds.Max.y - Layout->Margin.y; + + ui_eval_result SliderEval = ui_EvaluateWidget(Interface, SliderRegion, SliderBounds); + if (SliderEval.Clicked || SliderEval.Held) + { + r32 Percent = (Interface->Mouse.Pos.y - SliderRegion->Bounds.Min.y) / Rect2Height(SliderRegion->Bounds); + State->InitialValueR32 = Clamp01(Percent); + } + SliderRegion->FillPercent = State->InitialValueR32; + + // Create the viewport that offsets list contents (and at render time determines what is visible) + // + ui_widget* ViewportLayout = ui_PushLayout(Interface, MakeString("Contents")); + ui_WidgetClearFlag(ViewportLayout, UIWidgetFlag_ExpandsToFitChildren); + + ViewportLayout->Bounds.Min.y = SliderBounds.Min.y; + ViewportLayout->Bounds.Max.y = SliderBounds.Max.y; + + s32 ScrollableElements = Max(0, ElementCount - ViewportRows); + ui_widget_retained_state* ViewportState = ui_GetOrCreateRetainedState(Interface, ViewportLayout); + ViewportState->ChildrenDrawOffset.x = 0; + ViewportState->ChildrenDrawOffset.y = ((1.0f - State->InitialValueR32) * (r32)(ScrollableElements)) * ViewportLayout->RowHeight; } -internal b32 -EvaluateColorPicker (render_command_buffer* RenderBuffer, v4* Value, v2 PanelMin, interface_config Config, mouse_state Mouse) +internal void +ui_EndList(ui_interface* Interface) { - b32 ShouldClose = false; - - v2 PanelMax = v2{400, 500}; - // TODO(Peter): Can this get passed from outside? rather pass rect2 than min/max pairs - rect2 PanelRect = rect2{PanelMin, PanelMax}; - if (MouseButtonTransitionedDown(Mouse.LeftButtonState) && !PointIsInRect(PanelRect, Mouse.Pos)) - { - ShouldClose = true; - } - else - { - PushRenderQuad2D(RenderBuffer, PanelRect.Min, PanelRect.Max, v4{.5f, .5f, .5f, 1.f}); - - v2 TitleMin = v2{PanelRect.Min.x + 5, PanelRect.Max.y - (Config.Font->PixelHeight + 5)}; - DrawString(RenderBuffer, MakeString("Color Picker"), Config.Font, - TitleMin, WhiteV4); - - v2 SliderDim = v2{(PanelMax.x - PanelMin.x) - 20, 32}; - // channel sliders - v2 SliderMin = TitleMin - v2{0, SliderDim.y + 10}; - Value->r = EvaluateColorChannelSlider(RenderBuffer, RedV4, SliderMin, SliderMin + SliderDim, Value->r, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->g = EvaluateColorChannelSlider(RenderBuffer, GreenV4, SliderMin, SliderMin + SliderDim, Value->g, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->b = EvaluateColorChannelSlider(RenderBuffer, BlueV4, SliderMin, SliderMin + SliderDim, Value->b, Mouse); - SliderMin.y -= SliderDim.y + 10; - Value->a = EvaluateColorChannelSlider(RenderBuffer, WhiteV4, SliderMin, SliderMin + SliderDim, Value->a, Mouse); - - // Output Color Display - SliderMin.y -= 100; - PushRenderQuad2D(RenderBuffer, SliderMin, SliderMin + v2{75, 75}, *Value); - } - - return ShouldClose; + // Pop the Viewport Layout + ui_PopLayout(Interface); + // TODO(pjs): Ensure that the active layout is the row layout we started in begin list + // Pop the actual list layout + ui_EndRow(Interface); } -struct search_lister_result +internal void +ui_BeginMousePopup(ui_interface* Interface, rect2 Bounds, ui_layout_direction FillDir, gs_string Text) { - s32 HotItem; - s32 SelectedItem; - b32 ShouldRemainOpen; -}; + rect2 FollowMouseBounds = Rect2Translate(Bounds, Interface->Mouse.Pos); + ui_widget* Layout = ui_PushOverlayLayout(Interface, FollowMouseBounds, FillDir, Text); + ui_WidgetSetFlag(Layout, UIWidgetFlag_DrawBackground); +} -typedef gs_string search_lister_get_list_item_at_offset(u8* ListMemory, s32 ListLength, gs_string Searchgs_string, s32 Offset); - -internal search_lister_result -EvaluateSearchLister (ui_interface* Interface, v2 TopLeft, v2 Dimension, gs_string Title, - gs_string* ItemList, s32* ListLUT, s32 ListLength, - s32 HotItem, - gs_string* Searchgs_string, s32 Searchgs_stringCursorPosition) +internal void +ui_EndMousePopup(ui_interface* Interface) { - search_lister_result Result = {}; - Result.ShouldRemainOpen = true; - Result.HotItem = HotItem; - - // TODO(Peter): Was tired. Nothing wrong with the code below - InvalidCodePath; -#if 0 - // Title Bar - rect2 TitleBarBounds = rect2{v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}}; - ui_FillRect(Interface, TitleBarBounds, v4{.3f, .3f, .3f, 1.f}); - ui_Drawgs_string(Interface, Title, TitleBarBounds, Interface->Style.TextColor); - - MakeStringBuffer(Debuggs_string, 256); - PrintF(&Debuggs_string, "Hot Item: %d | Filtered Items: %d", HotItem, ListLength); - rect2 DebugBounds = MakeRectMinWidth(v2{ TopLeft.x + 256, TopLeft.y - 25}, v2{256, Interface->Style.LineHeight}); - ui_Drawgs_string(Interface, Debuggs_string, DebugBounds, Interface->Style.TextColor); - - // Search Bar - PushRenderQuad2D(RenderBuffer, v2{TopLeft.x, TopLeft.y - 30}, v2{TopLeft.x + 300, TopLeft.y}, v4{.3f, .3f, .3f, 1.f}); - Drawgs_stringWithCursor(RenderBuffer, *Searchgs_string, Searchgs_stringCursorPosition, Font, v2{TopLeft.x, TopLeft.y - 25}, WhiteV4, GreenV4); - TopLeft.y -= 30; - - for (s32 i = 0; i < ListLength; i++) - { - s32 FilteredIndex = ListLUT[i]; - gs_string ListItemgs_string = ItemList[FilteredIndex]; - - v2 Min = v2{TopLeft.x, TopLeft.y - 30}; - v2 Max = Min + Dimension - v2{0, Config.Margin.y}; - - v4 ButtonColor = Config.ButtonColor_Inactive; - if (i == HotItem) - { - ButtonColor = Config.ButtonColor_Active; - } - - if (ui_Button(Interface, ListItemgs_string, rect2{Min, Max})) - { - Result.SelectedItem = i; - } - - TopLeft.y -= 30; - } -#endif - - return Result; + ui_PopLayout(Interface); } #define INTERFACE_H diff --git a/src/app/platform_win32/win32_foldhaus.cpp b/src/app/platform_win32/win32_foldhaus.cpp index f3d12c5..6f58fbb 100644 --- a/src/app/platform_win32/win32_foldhaus.cpp +++ b/src/app/platform_win32/win32_foldhaus.cpp @@ -187,7 +187,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, false, true, ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState = KeyState_IsDown & ~KeyState_WasDown; + Mouse->LeftButtonState |= KeyState_IsDown; Mouse->DownPos = Mouse->Pos; // :Win32MouseEventCapture @@ -237,7 +237,7 @@ HandleWindowMessage (MSG Message, window* Window, input_queue* InputQueue, mouse AddInputEventEntry(InputQueue, KeyCode_MouseLeftButton, true, false, ShiftDown, AltDown, CtrlDown, false); - Mouse->LeftButtonState = ~KeyState_IsDown & KeyState_WasDown; + Mouse->LeftButtonState &= ~KeyState_IsDown; // :Win32MouseEventCapture ReleaseCapture(); @@ -413,7 +413,7 @@ Win32_SendAddressedDataBuffers(gs_thread_context Context, addressed_data_buffer_ gs_string OutputStr = AllocatorAllocString(Context.Allocator, 256); PrintF(&OutputStr, "Buffers Sent: %d\n", BuffersSent); NullTerminate(&OutputStr); - OutputDebugStringA(OutputStr.Str); + //OutputDebugStringA(OutputStr.Str); } internal void @@ -634,7 +634,7 @@ WinMain ( AddressedDataBufferList_Clear(&OutputData); - { // Mouse Position + { // Mouse POINT MousePos; GetCursorPos (&MousePos); ScreenToClient(MainWindow.Handle, &MousePos); @@ -643,6 +643,15 @@ WinMain ( Context.Mouse.OldPos = Context.Mouse.Pos; Context.Mouse.Pos = v2{(r32)MousePos.x, (r32)MainWindow.Height - MousePos.y}; Context.Mouse.DeltaPos = Context.Mouse.Pos - Context.Mouse.OldPos; + + if (KeyIsDown(Context.Mouse.LeftButtonState)) + { + SetKeyWasDown(Context.Mouse.LeftButtonState); + } + else + { + SetKeyWasUp(Context.Mouse.LeftButtonState); + } } MSG Message; @@ -726,6 +735,10 @@ WinMain ( LastFrameSecondsElapsed = SecondsElapsed; LastFrameEnd = GetWallClock(); + + + //OutputDebugStringA("-- Frame END -- \n"); + } Context.CleanupApplication(Context); diff --git a/src/app/platform_win32/win32_foldhaus_work_queue.h b/src/app/platform_win32/win32_foldhaus_work_queue.h index 2db58f3..0b31fa6 100644 --- a/src/app/platform_win32/win32_foldhaus_work_queue.h +++ b/src/app/platform_win32/win32_foldhaus_work_queue.h @@ -46,6 +46,8 @@ Win32CreateThreadContext(gs_memory_arena* Transient = 0) Win32EnumerateDirectory, Result.Transient); + Result.DebugOutput.Print = Win32DebugPrint; + return Result; } diff --git a/src/gs_libs/gs_input.h b/src/gs_libs/gs_input.h index d4a83f0..daabf05 100644 --- a/src/gs_libs/gs_input.h +++ b/src/gs_libs/gs_input.h @@ -15,32 +15,32 @@ enum key_code KeyCode_CapsLock, KeyCode_LeftShift, KeyCode_RightShift, KeyCode_LeftCtrl, KeyCode_RightCtrl, - KeyCode_Fn, - KeyCode_Alt, + KeyCode_Fn, + KeyCode_Alt, KeyCode_PageUp, KeyCode_PageDown, KeyCode_Backspace, KeyCode_Delete, KeyCode_Enter, // Function Keys KeyCode_F0, KeyCode_F1, KeyCode_F2, KeyCode_F3, KeyCode_F4, KeyCode_F5, KeyCode_F6, KeyCode_F7, - KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12, + KeyCode_F8, KeyCode_F9, KeyCode_F10, KeyCode_F11, KeyCode_F12, // Letters KeyCode_a, KeyCode_b, KeyCode_c, KeyCode_d, KeyCode_e, KeyCode_f, KeyCode_g, KeyCode_h, - KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p, - KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x, + KeyCode_i, KeyCode_j, KeyCode_k, KeyCode_l, KeyCode_m, KeyCode_n, KeyCode_o, KeyCode_p, + KeyCode_q, KeyCode_r, KeyCode_s, KeyCode_t, KeyCode_u, KeyCode_v, KeyCode_w, KeyCode_x, KeyCode_y, KeyCode_z, KeyCode_A, KeyCode_B, KeyCode_C, KeyCode_D, KeyCode_E, KeyCode_F, KeyCode_G, KeyCode_H, - KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P, - KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X, + KeyCode_I, KeyCode_J, KeyCode_K, KeyCode_L, KeyCode_M, KeyCode_N, KeyCode_O, KeyCode_P, + KeyCode_Q, KeyCode_R, KeyCode_S, KeyCode_T, KeyCode_U, KeyCode_V, KeyCode_W, KeyCode_X, KeyCode_Y, KeyCode_Z, // Numbers KeyCode_0, KeyCode_1, KeyCode_2, KeyCode_3, KeyCode_4, KeyCode_5, KeyCode_6, KeyCode_7, KeyCode_8, KeyCode_9, - KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5, + KeyCode_Num0, KeyCode_Num1, KeyCode_Num2, KeyCode_Num3, KeyCode_Num4, KeyCode_Num5, KeyCode_Num6, KeyCode_Num7, KeyCode_Num8, KeyCode_Num9, // Symbols @@ -48,7 +48,7 @@ enum key_code KeyCode_Ampersand, KeyCode_Star, KeyCode_LeftParen, KeyCode_RightParen, KeyCode_Minus, KeyCode_Plus, KeyCode_Equals, KeyCode_Underscore, KeyCode_LeftBrace, KeyCode_RightBrace, KeyCode_LeftBracket, KeyCode_RightBracket, KeyCode_Colon, KeyCode_SemiColon, KeyCode_SingleQuote, KeyCode_DoubleQuote, - KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period, + KeyCode_ForwardSlash, KeyCode_Backslash, KeyCode_Pipe, KeyCode_Comma, KeyCode_Period, KeyCode_QuestionMark, KeyCode_LessThan, KeyCode_GreaterThan, KeyCode_Tilde, KeyCode_BackQuote, // Arrows @@ -77,9 +77,9 @@ CharacterFromKeyCode (key_code Code) case KeyCode_Tab: { Result = '\t'; }break; // Letters - case KeyCode_a: { Result = 'a'; }break; - case KeyCode_b: { Result = 'b'; }break; - case KeyCode_c: { Result = 'c'; }break; + case KeyCode_a: { Result = 'a'; }break; + case KeyCode_b: { Result = 'b'; }break; + case KeyCode_c: { Result = 'c'; }break; case KeyCode_d: { Result = 'd'; }break; case KeyCode_e: { Result = 'e'; }break; case KeyCode_f: { Result = 'f'; }break; @@ -92,7 +92,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_m: { Result = 'm'; }break; case KeyCode_n: { Result = 'n'; }break; case KeyCode_o: { Result = 'o'; }break; - case KeyCode_p: { Result = 'p'; }break; + case KeyCode_p: { Result = 'p'; }break; case KeyCode_q: { Result = 'q'; }break; case KeyCode_r: { Result = 'r'; }break; case KeyCode_s: { Result = 's'; }break; @@ -100,7 +100,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_u: { Result = 'u'; }break; case KeyCode_v: { Result = 'v'; }break; case KeyCode_w: { Result = 'w'; }break; - case KeyCode_x: { Result = 'x'; }break; + case KeyCode_x: { Result = 'x'; }break; case KeyCode_y: { Result = 'y'; }break; case KeyCode_z: { Result = 'z'; }break; @@ -119,7 +119,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_M: { Result = 'M'; }break; case KeyCode_N: { Result = 'N'; }break; case KeyCode_O: { Result = 'O'; }break; - case KeyCode_P: { Result = 'P'; }break; + case KeyCode_P: { Result = 'P'; }break; case KeyCode_Q: { Result = 'Q'; }break; case KeyCode_R: { Result = 'R'; }break; case KeyCode_S: { Result = 'S'; }break; @@ -127,7 +127,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_U: { Result = 'U'; }break; case KeyCode_V: { Result = 'V'; }break; case KeyCode_W: { Result = 'W'; }break; - case KeyCode_X: { Result = 'X'; }break; + case KeyCode_X: { Result = 'X'; }break; case KeyCode_Y: { Result = 'Y'; }break; case KeyCode_Z: { Result = 'Z'; }break; @@ -148,7 +148,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_Num2: { Result = '2'; }break; case KeyCode_Num3: { Result = '3'; }break; case KeyCode_Num4: { Result = '4'; }break; - case KeyCode_Num5: { Result = '5'; }break; + case KeyCode_Num5: { Result = '5'; }break; case KeyCode_Num6: { Result = '6'; }break; case KeyCode_Num7: { Result = '7'; }break; case KeyCode_Num8: { Result = '8'; }break; @@ -181,7 +181,7 @@ CharacterFromKeyCode (key_code Code) case KeyCode_Backslash: { Result = '\\'; }break; case KeyCode_Pipe: { Result = '|'; }break; case KeyCode_Comma: { Result = ','; }break; - case KeyCode_Period: { Result = '.'; }break; + case KeyCode_Period: { Result = '.'; }break; case KeyCode_QuestionMark: { Result = '?'; }break; case KeyCode_LessThan: { Result = '<'; }break; case KeyCode_GreaterThan: { Result = '>'; }break; @@ -227,6 +227,11 @@ enum key_state_flags #define KeyWasDown(event) ((event & KeyState_WasDown) > 0) #define KeyIsDown(event) ((event & KeyState_IsDown) > 0) +#define SetKeyDown(key) (key |= KeyState_IsDown) +#define SetKeyWasDown(key) (key |= KeyState_WasDown) +#define SetKeyUp(key) (key &= ~KeyState_IsDown) +#define SetKeyWasUp(key) (key &= ~KeyState_WasDown) + struct input_entry { key_code Key; @@ -266,6 +271,8 @@ struct mouse_state b32 MiddleButtonState; b32 RightButtonState; + + cursor_type CursorType; }; @@ -327,7 +334,7 @@ KeyTransitionedUp (input Input, key_code Key) } internal void -AddInputEventEntry (input_queue* Queue, key_code Key, +AddInputEventEntry (input_queue* Queue, key_code Key, b32 WasDown, b32 IsDown, b32 ShiftDown, b32 AltDown, b32 CtrlDown, b32 SysDown) { Assert(Queue->QueueUsed < Queue->QueueSize); @@ -392,10 +399,10 @@ GetMouseButtonStateAdvanced (b32 ButtonState) !((ButtonState & KeyState_IsDown) > 0)) { Result= 0; - } - else if (ButtonState & KeyState_IsDown) - { - Result |= KeyState_WasDown; + } + else if (ButtonState & KeyState_IsDown) + { + Result |= KeyState_WasDown; } return Result; } diff --git a/src/gs_libs/gs_types.cpp b/src/gs_libs/gs_types.cpp index 7d6b174..28d57b0 100644 --- a/src/gs_libs/gs_types.cpp +++ b/src/gs_libs/gs_types.cpp @@ -942,6 +942,10 @@ Range2Union(range2 A, range2 B) Result.Min.y = Max(A.Min.y, B.Min.y); Result.Max.x = Min(A.Max.x, B.Max.x); Result.Max.y = Min(A.Max.y, B.Max.y); + + if (Rect2Width(Result) < 0) { Result.Min.x = Result.Max.x; } + if (Rect2Height(Result) < 0) { Result.Min.y = Result.Max.y; } + return Result; } internal range3 @@ -964,6 +968,41 @@ Rect2GetRectLocalPoint(rect2 Rect, v2 Point) return Result; } +internal r32 +Rect2Area(rect2 Rect) +{ + r32 Result = Rect2Width(Rect) * Rect2Height(Rect); + return Result; +} + +internal v2 +Rect2BottomLeft(rect2 Rect) +{ + v2 Result = Rect.Min; + return Result; +} + +internal v2 +Rect2BottomRight(rect2 Rect) +{ + v2 Result = v2{ Rect.Max.x, Rect.Min.y }; + return Result; +} + +internal v2 +Rect2TopRight(rect2 Rect) +{ + v2 Result = Rect.Max; + return Result; +} + +internal v2 +Rect2TopLeft(rect2 Rect) +{ + v2 Result = v2{ Rect.Min.x, Rect.Max.y }; + return Result; +} + /////////////////////////// // // Ray @@ -1994,31 +2033,31 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args) if (FormatAt[0] == 'h' && FormatAt[1] == 'h') { LengthSpecified = true; - LengthSpecified = 1; + Length = 1; FormatAt += 2; } else if (FormatAt[0] == 'h') { LengthSpecified = true; - LengthSpecified = 2; + Length = 2; FormatAt++; } else if (FormatAt[0] == 'l' && FormatAt[1] == 'l') { LengthSpecified = true; - LengthSpecified = 8; + Length = 8; FormatAt += 2; } else if (FormatAt[0] == 'l') { LengthSpecified = true; - LengthSpecified = 4; + Length = 4; FormatAt++; } else if (FormatAt[0] == 'j') { LengthSpecified = true; - LengthSpecified = 8; + Length = 8; FormatAt++; } else if (FormatAt[0] == 'z') @@ -2049,7 +2088,7 @@ PrintFArgsList (gs_string* String, char* Format, va_list Args) } else if (FormatAt[0] == 'u') { - u32 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); + u64 UnsignedInt = ReadVarArgsUnsignedInteger(Length, &Args); U64ToASCII(&StringRemaining, UnsignedInt, 10, Base10Chars); } else if (FormatAt[0] == 'o') @@ -2490,6 +2529,19 @@ PushStringF(gs_memory_arena* Arena, u32 MaxLength, char* Format, ...) return Result; } +internal gs_string +PushStringCopy(gs_memory_arena* Arena, gs_const_string String) +{ + gs_string Result = PushString(Arena, String.Length); + Result.Size = String.Length; + Result.Length = String.Length; + for (u32 i = 0; i < String.Length; i++) + { + Result.Str[i] = String.Str[i]; + } + return Result; +} + internal void ClearArena(gs_memory_arena* Arena) { @@ -3126,6 +3178,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) { diff --git a/src/todo.txt b/src/todo.txt index 3dcb1f0..c2ef7b1 100644 --- a/src/todo.txt +++ b/src/todo.txt @@ -43,7 +43,6 @@ STREAM #1: 3D Overhaul - :ErrorLogging - Animation System - - blending between animation - layers - layer masks by sculpture - layer masks by tag / value diff --git a/src/todo_done.txt b/src/todo_done.txt index 6aae2d4..ac67406 100644 --- a/src/todo_done.txt +++ b/src/todo_done.txt @@ -1,3 +1,5 @@ +x blending between animation + x saving animation timelines x Buckets & Lists x On second thought, I just really don't like these. Lets get rid of them, and put custom structures in place