From b83d718d37a27ae5bcd5782295d61aa3d7f7fb2c Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Thu, 26 Dec 2019 13:14:00 -0800 Subject: [PATCH] Consolidated all panel code in one file, and removed all external dependencies. ITS A LIBRARY NOW (still needs refinement) --- src/animation/foldhaus_animation_interface.h | 8 - src/foldhaus_app.cpp | 103 +++++- src/foldhaus_app.h | 20 +- src/foldhaus_panel.cpp | 313 ------------------ src/foldhaus_panel.h | 287 ++++++++++++++-- .../foldhaus_panel_animation_timeline.h | 9 + 6 files changed, 384 insertions(+), 356 deletions(-) diff --git a/src/animation/foldhaus_animation_interface.h b/src/animation/foldhaus_animation_interface.h index a6b6726..059c91a 100644 --- a/src/animation/foldhaus_animation_interface.h +++ b/src/animation/foldhaus_animation_interface.h @@ -1,11 +1,3 @@ -// TODO -// [] - Moving animation blocks -// [] - dragging beginning and end of time blocks -// [] - creating a timeblock with a specific animation -// [x] - play, pause, stop, -// [] - setting the start and end of the animation system -// [] - displaying multiple layers -// [] - FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlock) { diff --git a/src/foldhaus_app.cpp b/src/foldhaus_app.cpp index 0ee0504..446d871 100644 --- a/src/foldhaus_app.cpp +++ b/src/foldhaus_app.cpp @@ -1,6 +1,85 @@ #include "foldhaus_platform.h" #include "foldhaus_app.h" +internal void +SetPanelDefinitionExternal(panel* Panel, s32 OldPanelDefinitionIndex, s32 NewPanelDefinitionIndex) +{ + if(OldPanelDefinitionIndex >= 0) + { + GlobalPanelDefs[OldPanelDefinitionIndex].Cleanup(Panel); + } + GlobalPanelDefs[NewPanelDefinitionIndex].Init(Panel); +} + + +internal void +DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, v2 FooterMin, v2 FooterMax, interface_config Interface, mouse_state Mouse) +{ + PushRenderQuad2D(RenderBuffer, FooterMin, v2{FooterMax.x, FooterMin.y + 25}, v4{.5f, .5f, .5f, 1.f}); + PushRenderQuad2D(RenderBuffer, FooterMin, FooterMin + v2{25, 25}, WhiteV4); + + v2 PanelSelectButtonMin = FooterMin + v2{30, 1}; + v2 PanelSelectButtonMax = PanelSelectButtonMin + v2{100, 23}; + + if (Panel->PanelSelectionMenuOpen) + { + v2 ButtonDimension = v2{100, 25}; + v2 ButtonMin = v2{PanelSelectButtonMin.x, FooterMax.y}; + + v2 MenuMin = ButtonMin; + v2 MenuMax = v2{ButtonMin.x + ButtonDimension.x, ButtonMin.y + (ButtonDimension.y * GlobalPanelDefsCount)}; + if (MouseButtonTransitionedDown(Mouse.LeftButtonState) + && !PointIsInRange(Mouse.DownPos, MenuMin, MenuMax)) + { + Panel->PanelSelectionMenuOpen = false; + } + + +for (s32 i = 0; i < GlobalPanelDefsCount; i++) + { + panel_definition Def = GlobalPanelDefs[i]; + string DefName = MakeString(Def.PanelName, Def.PanelNameLength); + button_result DefinitionButton = EvaluateButton(RenderBuffer, + ButtonMin, ButtonMin + ButtonDimension, + DefName, Interface, Mouse); + if (DefinitionButton.Pressed) + { + SetPanelDefinition(Panel, i); + Panel->PanelSelectionMenuOpen = false; + } + + ButtonMin.y += ButtonDimension.y; + } + } + +button_result ButtonResult = EvaluateButton(RenderBuffer, + PanelSelectButtonMin, +PanelSelectButtonMax, + MakeStringLiteral("Select"), Interface, Mouse); + if (ButtonResult.Pressed) + { + Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen; + } + +} + +internal void +RenderPanel(panel* Panel, v2 PanelMin, v2 PanelMax, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) +{ + Assert(Panel->PanelDefinitionIndex >= 0); + +v2 FooterMin = PanelMin; + v2 FooterMax = v2{PanelMax.x, PanelMin.y + 25}; + v2 PanelViewMin = v2{PanelMin.x, FooterMax.y}; + v2 PanelViewMax = PanelMax; + +panel_definition Definition = GlobalPanelDefs[Panel->PanelDefinitionIndex]; + Definition.Render(*Panel, PanelMin, PanelMax, RenderBuffer, State, Context, Mouse); + +PushRenderOrthographic(RenderBuffer, WindowMin.x, WindowMin.y, WindowMax.x, WindowMax.y); +DrawPanelFooter(Panel, RenderBuffer, FooterMin, FooterMax, State->Interface, Mouse); +} + internal v4 MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 WindowHeight) { @@ -271,9 +350,9 @@ INITIALIZE_APPLICATION(InitializeApplication) { // Panels Playground InitializePanelLayout(&State->PanelLayout); panel* Panel = TakeNewPanel(&State->PanelLayout); - SetPanelDefinition(Panel, GlobalPanelDefs[0]); + SetPanelDefinition(Panel, 0); SplitPanelVertically(Panel, .5f, v2{0, 0}, v2{Context.WindowWidth, Context.WindowHeight}, &State->PanelLayout); - SetPanelDefinition(&Panel->Right->Panel, GlobalPanelDefs[1]); + SetPanelDefinition(&Panel->Right->Panel, 1); } // End Panels Playground } @@ -557,14 +636,13 @@ PushRenderOrthographic(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHe } } - v2 TimelineMin = v2{0, 0}; - v2 TimelineMax = v2{Context.WindowWidth, 125}; - animation_block_handle NewSelection = DrawAnimationPanel(&State->AnimationSystem, - TimelineMin, TimelineMax, - State->SelectedAnimationBlockHandle, - RenderBuffer, State->Interface, Mouse); - State->SelectedAnimationBlockHandle = NewSelection; - + DrawDebugInterface(RenderBuffer, 25, + State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight, + Context.DeltaTime, State, State->Camera, Mouse, &State->Transient); +#endif + } + + for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) { operation_mode OperationMode = State->Modes.ActiveModes[m]; @@ -574,11 +652,6 @@ PushRenderOrthographic(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHe } } - DrawDebugInterface(RenderBuffer, 25, - State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight, - Context.DeltaTime, State, State->Camera, Mouse, &State->Transient); -#endif - } // Checking for overflows { diff --git a/src/foldhaus_app.h b/src/foldhaus_app.h index 62004fa..bfad4dd 100644 --- a/src/foldhaus_app.h +++ b/src/foldhaus_app.h @@ -17,6 +17,7 @@ typedef struct app_state app_state; + #include "foldhaus_panel.h" #include "foldhaus_command_dispatch.h" @@ -171,10 +172,27 @@ r32 GreenSize = 20.0f; #include "foldhaus_interface.cpp" #include "animation/foldhaus_animation_interface.h" +#define PANEL_INIT_PROC(name) void name(panel* Panel) +typedef PANEL_INIT_PROC(panel_init_proc); + +#define PANEL_CLEANUP_PROC(name) void name(panel* Panel) +typedef PANEL_CLEANUP_PROC(panel_cleanup_proc); + +#define PANEL_RENDER_PROC(name) void name(panel Panel, v2 PanelMin, v2 PanelMax, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) +typedef PANEL_RENDER_PROC(panel_render_proc); + +struct panel_definition +{ + char* PanelName; + s32 PanelNameLength; + panel_init_proc* Init; + panel_cleanup_proc* Cleanup; + panel_render_proc* Render; +}; + #include "panels/foldhaus_panel_sculpture_view.h" #include "panels/foldhaus_panel_profiler.h" #include "panels/foldhaus_panel_dmx_view.h" #include "panels/foldhaus_panel_animation_timeline.h" #include "generated/foldhaus_panels_generated.h" -#include "foldhaus_panel.cpp" diff --git a/src/foldhaus_panel.cpp b/src/foldhaus_panel.cpp index 579ce8e..e69de29 100644 --- a/src/foldhaus_panel.cpp +++ b/src/foldhaus_panel.cpp @@ -1,313 +0,0 @@ - -///////////////////////////////// -// -// Book-Keeping -// -///////////////////////////////// - -internal void -InitializePanelLayout(panel_layout* Layout) -{ - Layout->FreeList.Free.Next = &Layout->FreeList; -} - -internal panel_entry* -TakeNewPanelEntry(panel_layout* Layout) -{ - panel_entry* FreeEntry = 0; - if (Layout->FreeList.Free.Next != &Layout->FreeList) - { - FreeEntry = Layout->FreeList.Free.Next; - Layout->FreeList.Free.Next = FreeEntry->Free.Next; - } - else - { - Assert(Layout->PanelsUsed < PANELS_MAX); - FreeEntry = Layout->Panels + Layout->PanelsUsed++; - } - return FreeEntry; -} - -internal panel* -TakeNewPanel(panel_layout* Layout) -{ - panel* Result = 0; - panel_entry* FreeEntry = TakeNewPanelEntry(Layout); -Result = &FreeEntry->Panel; - *Result = {0}; - return Result; -} - -internal void -FreePanelEntry(panel_entry* Entry, panel_layout* Layout) -{ - Assert(Entry >= Layout->Panels && Entry <= Layout->Panels + PANELS_MAX); - Entry->Panel = {0}; - Entry->Free.Next = Layout->FreeList.Free.Next; - Layout->FreeList.Free.Next = Entry; -} - -internal void -FreePanelAtIndex(s32 Index, panel_layout* Layout) -{ - Assert(Index > 0 && Index < (s32)Layout->PanelsUsed); - panel_entry* EntryToFree = Layout->Panels + Index; - EntryToFree->Free.Next = Layout->FreeList.Free.Next; - Layout->FreeList.Free.Next = EntryToFree; -} - -internal void -SplitPanelVertically(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) -{ - r32 SplitX = GSLerp(ParentMin.x, ParentMax.x, Percent); - if (SplitX > ParentMin.x && SplitX < ParentMax.x) - { -Parent->SplitDirection = PanelSplit_Vertical; - Parent->SplitPercent = Percent; - - Parent->Left = TakeNewPanelEntry(Layout); - Parent->Left->Panel.Render = Parent->Render; - -Parent->Right = TakeNewPanelEntry(Layout); - Parent->Right->Panel.Render = Parent->Render; - } -} - -internal void -SplitPanelHorizontally(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) -{ - r32 SplitY = GSLerp(ParentMin.y, ParentMax.y, Percent); - if (SplitY > ParentMin.y && SplitY < ParentMax.y) - { -Parent->SplitDirection = PanelSplit_Horizontal; - Parent->SplitPercent = Percent; - - Parent->Bottom = TakeNewPanelEntry(Layout); - Parent->Bottom->Panel.Render = Parent->Render; - -Parent->Top = TakeNewPanelEntry(Layout); - Parent->Top->Panel.Render = Parent->Render; - } -} - -internal void -ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_layout* Layout) -{ - panel_entry* LeftChild = Parent->Left; - panel_entry* RightChild = Parent->Right; - - *Parent = PanelEntryToKeep->Panel; - Parent->SplitDirection = PanelSplit_NoSplit; - - FreePanelEntry(LeftChild, Layout); - FreePanelEntry(RightChild, Layout); -} - -internal void -SetPanelDefinition(panel* Panel, panel_definition Def) -{ - if(Panel->Cleanup) - { - Panel->Cleanup(Panel); - } - -Panel->Cleanup = Def.Cleanup; - Panel->Render = Def.Render; - Def.Init(Panel); -} - -///////////////////////////////// -// -// Rendering And Interaction -// -///////////////////////////////// - -internal void -HandleMousePanelInteractionOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, panel_layout* PanelLayout, mouse_state Mouse) -{ - r32 PanelEdgeClickMaxDistance = 4; - - // TODO(Peter): Need a way to calculate this button's position more systemically - if (Panel->SplitDirection == PanelSplit_NoSplit - && PointIsInRange(Mouse.DownPos, PanelMin, PanelMin + v2{25, 25})) - { - r32 XDistance = GSAbs(Mouse.Pos.x - Mouse.DownPos.x); - r32 YDistance = GSAbs(Mouse.Pos.y - Mouse.DownPos.y); - - if (XDistance > YDistance) - { - r32 XPercent = (Mouse.Pos.x - PanelMin.x) / (PanelMax.x - PanelMin.x); - SplitPanelVertically(Panel, XPercent, PanelMin, PanelMax, PanelLayout); - } - else - { - r32 YPercent = (Mouse.Pos.y - PanelMin.y) / (PanelMax.y - PanelMin.y); - SplitPanelHorizontally(Panel, YPercent, PanelMin, PanelMax, PanelLayout); - } - } - else if (Panel->SplitDirection == PanelSplit_Horizontal) - { - r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); - r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.y - SplitY); - if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) - { - r32 NewSplitY = Mouse.Pos.y; - if (NewSplitY <= PanelMin.y) - { - ConsolidatePanelsKeepOne(Panel, Panel->Top, PanelLayout); - } - else if (NewSplitY >= PanelMax.y) - { - ConsolidatePanelsKeepOne(Panel, Panel->Bottom, PanelLayout); - } - else - { - Panel->SplitPercent = (NewSplitY - PanelMin.y) / (PanelMax.y - PanelMin.y); - } - } - else - { - HandleMousePanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, PanelLayout, Mouse); - HandleMousePanelInteractionOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, PanelLayout, Mouse); - } - } - else if (Panel->SplitDirection == PanelSplit_Vertical) - { - r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); - r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.x - SplitX); - if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) - { - r32 NewSplitX = Mouse.Pos.x; - if (NewSplitX <= PanelMin.x) - { - ConsolidatePanelsKeepOne(Panel, Panel->Right, PanelLayout); - } - else if (NewSplitX >= PanelMax.x) - { - ConsolidatePanelsKeepOne(Panel, Panel->Left, PanelLayout); - } - else - { - Panel->SplitPercent = (NewSplitX - PanelMin.x) / (PanelMax.x - PanelMin.x); - } - } - else - { - HandleMousePanelInteractionOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, PanelLayout, Mouse); - HandleMousePanelInteractionOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, PanelLayout, Mouse); - } - } -} - -internal void -HandleMousePanelInteraction(panel_layout* PanelLayout, v2 WindowMin, v2 WindowMax, mouse_state Mouse) -{ - r32 PanelEdgeClickMaxDistance = 4; - - if (MouseButtonTransitionedUp(Mouse.LeftButtonState)) - { - Assert(PanelLayout->PanelsUsed > 0); - panel* FirstPanel = &PanelLayout->Panels[0].Panel; - HandleMousePanelInteractionOrRecurse(FirstPanel, WindowMin, WindowMax, PanelLayout, Mouse); - } -} - -internal void -DrawPanelFooter(panel* Panel, render_command_buffer* RenderBuffer, v2 FooterMin, v2 FooterMax, interface_config Interface, mouse_state Mouse) -{ - PushRenderQuad2D(RenderBuffer, FooterMin, v2{FooterMax.x, FooterMin.y + 25}, v4{.5f, .5f, .5f, 1.f}); - PushRenderQuad2D(RenderBuffer, FooterMin, FooterMin + v2{25, 25}, WhiteV4); - - v2 PanelSelectButtonMin = FooterMin + v2{30, 1}; - v2 PanelSelectButtonMax = PanelSelectButtonMin + v2{100, 23}; - - if (Panel->PanelSelectionMenuOpen) - { - v2 ButtonDimension = v2{100, 25}; - v2 ButtonMin = v2{PanelSelectButtonMin.x, FooterMax.y}; - - v2 MenuMin = ButtonMin; - v2 MenuMax = v2{ButtonMin.x + ButtonDimension.x, ButtonMin.y + (ButtonDimension.y * GlobalPanelDefsCount)}; - if (MouseButtonTransitionedDown(Mouse.LeftButtonState) - && !PointIsInRange(Mouse.DownPos, MenuMin, MenuMax)) - { - Panel->PanelSelectionMenuOpen = false; - } - - -for (s32 i = 0; i < GlobalPanelDefsCount; i++) - { - panel_definition Def = GlobalPanelDefs[i]; - string DefName = MakeString(Def.PanelName, Def.PanelNameLength); - button_result DefinitionButton = EvaluateButton(RenderBuffer, - ButtonMin, ButtonMin + ButtonDimension, - DefName, Interface, Mouse); - if (DefinitionButton.Pressed) - { - SetPanelDefinition(Panel, Def); - Panel->PanelSelectionMenuOpen = false; - } - - ButtonMin.y += ButtonDimension.y; - } - } - -button_result ButtonResult = EvaluateButton(RenderBuffer, - PanelSelectButtonMin, -PanelSelectButtonMax, - MakeStringLiteral("Select"), Interface, Mouse); - if (ButtonResult.Pressed) - { - Panel->PanelSelectionMenuOpen = !Panel->PanelSelectionMenuOpen; - } - -} - -internal void -DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, render_command_buffer* RenderBuffer) -{ - PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color); -} - -internal void -DrawPanelOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) -{ - if (Panel->SplitDirection == PanelSplit_NoSplit) - { - v2 FooterMin = PanelMin; - v2 FooterMax = v2{PanelMax.x, PanelMin.y + 25}; - v2 PanelViewMin = v2{PanelMin.x, FooterMax.y}; - v2 PanelViewMax = PanelMax; - - Panel->Render(*Panel, PanelViewMin, PanelViewMax, RenderBuffer, State, Context, Mouse); - v4 BorderColor = v4{0, 1, 1, 1}; - if (PointIsInRange(Mouse.Pos, PanelMin, PanelMax)) - { - BorderColor = v4{1, 0, 1, 1}; - } - -PushRenderOrthographic(RenderBuffer, WindowMin.x, WindowMin.y, WindowMax.x, WindowMax.y); -DrawPanelBorder(*Panel, PanelMin, PanelMax, BorderColor, RenderBuffer); -DrawPanelFooter(Panel, RenderBuffer, FooterMin, FooterMax, Interface, Mouse); - } - else if (Panel->SplitDirection == PanelSplit_Horizontal) - { - r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); - DrawPanelOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); - DrawPanelOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); - } - else if (Panel->SplitDirection == PanelSplit_Vertical) - { - r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); - DrawPanelOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); - DrawPanelOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); - } -} - -internal void -DrawAllPanels(panel_layout PanelLayout, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) -{ - Assert(PanelLayout.PanelsUsed > 0); - panel* FirstPanel = &PanelLayout.Panels[0].Panel; - DrawPanelOrRecurse(FirstPanel, WindowMin, WindowMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); -} diff --git a/src/foldhaus_panel.h b/src/foldhaus_panel.h index 90a06bd..ee56081 100644 --- a/src/foldhaus_panel.h +++ b/src/foldhaus_panel.h @@ -1,15 +1,17 @@ +/* +File: foldhaus_panel.cpp +Description: a system for laying out panels on a screen +Author: Peter Slattery +Creation Date: 2019-12-26 + +Usage: +Include this file in ONE file in your project. +Define both RenderPanel and SetPanelDefinitionExternal + +*/ typedef struct panel panel; -#define PANEL_INIT_PROC(name) void name(panel* Panel) -typedef PANEL_INIT_PROC(panel_init_proc); - -#define PANEL_CLEANUP_PROC(name) void name(panel* Panel) -typedef PANEL_CLEANUP_PROC(panel_cleanup_proc); - -#define PANEL_RENDER_PROC(name) void name(panel Panel, v2 PanelMin, v2 PanelMax, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse) -typedef PANEL_RENDER_PROC(panel_render_proc); - enum panel_split_direction { PanelSplit_NoSplit, @@ -23,9 +25,8 @@ typedef struct panel_entry panel_entry; struct panel { -panel_render_proc* Render; -panel_cleanup_proc* Cleanup; - + s32 PanelDefinitionIndex; + panel_split_direction SplitDirection; r32 SplitPercent; @@ -63,11 +64,259 @@ panel_entry Panels[PANELS_MAX]; panel_entry FreeList; }; -struct panel_definition +internal void SetPanelDefinitionExternal(panel* Panel, s32 OldPanelDefinitionIndex, s32 NewPanelDefinitionIndex); +internal void RenderPanel(panel* Panel, v2 PanelMin, v2 PanelMax, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, app_state* State, context Context, mouse_state Mouse); + + +///////////////////////////////// +// +// Book-Keeping +// +///////////////////////////////// + +internal void +InitializePanelLayout(panel_layout* Layout) { - char* PanelName; - s32 PanelNameLength; - panel_init_proc* Init; - panel_cleanup_proc* Cleanup; - panel_render_proc* Render; -}; \ No newline at end of file + Layout->FreeList.Free.Next = &Layout->FreeList; +} + +internal panel_entry* +TakeNewPanelEntry(panel_layout* Layout) +{ + panel_entry* FreeEntry = 0; + if (Layout->FreeList.Free.Next != &Layout->FreeList) + { + FreeEntry = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = FreeEntry->Free.Next; + } + else + { + Assert(Layout->PanelsUsed < PANELS_MAX); + FreeEntry = Layout->Panels + Layout->PanelsUsed++; + } + return FreeEntry; +} + +internal panel* +TakeNewPanel(panel_layout* Layout) +{ + panel* Result = 0; + panel_entry* FreeEntry = TakeNewPanelEntry(Layout); +Result = &FreeEntry->Panel; + +*Result = {0}; + Result->PanelDefinitionIndex = -1; + + return Result; +} + +internal void +FreePanelEntry(panel_entry* Entry, panel_layout* Layout) +{ + Assert(Entry >= Layout->Panels && Entry <= Layout->Panels + PANELS_MAX); + Entry->Panel = {0}; + Entry->Free.Next = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = Entry; +} + +internal void +FreePanelAtIndex(s32 Index, panel_layout* Layout) +{ + Assert(Index > 0 && Index < (s32)Layout->PanelsUsed); + panel_entry* EntryToFree = Layout->Panels + Index; + EntryToFree->Free.Next = Layout->FreeList.Free.Next; + Layout->FreeList.Free.Next = EntryToFree; +} + +internal void +SplitPanelVertically(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) +{ + r32 SplitX = GSLerp(ParentMin.x, ParentMax.x, Percent); + if (SplitX > ParentMin.x && SplitX < ParentMax.x) + { +Parent->SplitDirection = PanelSplit_Vertical; + Parent->SplitPercent = Percent; + + Parent->Left = TakeNewPanelEntry(Layout); + Parent->Left->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex; + +Parent->Right = TakeNewPanelEntry(Layout); + Parent->Right->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex; + } +} + +internal void +SplitPanelHorizontally(panel* Parent, r32 Percent, v2 ParentMin, v2 ParentMax, panel_layout* Layout) +{ + r32 SplitY = GSLerp(ParentMin.y, ParentMax.y, Percent); + if (SplitY > ParentMin.y && SplitY < ParentMax.y) + { +Parent->SplitDirection = PanelSplit_Horizontal; + Parent->SplitPercent = Percent; + + Parent->Bottom = TakeNewPanelEntry(Layout); + Parent->Bottom->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex; + +Parent->Top = TakeNewPanelEntry(Layout); + Parent->Top->Panel.PanelDefinitionIndex = Parent->PanelDefinitionIndex; + } +} + +internal void +ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_layout* Layout) +{ + panel_entry* LeftChild = Parent->Left; + panel_entry* RightChild = Parent->Right; + + *Parent = PanelEntryToKeep->Panel; + Parent->SplitDirection = PanelSplit_NoSplit; + + FreePanelEntry(LeftChild, Layout); + FreePanelEntry(RightChild, Layout); +} + +internal void +SetPanelDefinition(panel* Panel, s32 NewDefinitionIndex) +{ + s32 OldDefinitionIndex = Panel->PanelDefinitionIndex; + Panel->PanelDefinitionIndex = NewDefinitionIndex; + SetPanelDefinitionExternal(Panel, OldDefinitionIndex, NewDefinitionIndex); +} +///////////////////////////////// +// +// Rendering And Interaction +// +///////////////////////////////// + +internal void +HandleMousePanelInteractionOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, panel_layout* PanelLayout, mouse_state Mouse) +{ + r32 PanelEdgeClickMaxDistance = 4; + + // TODO(Peter): Need a way to calculate this button's position more systemically + if (Panel->SplitDirection == PanelSplit_NoSplit + && PointIsInRange(Mouse.DownPos, PanelMin, PanelMin + v2{25, 25})) + { + r32 XDistance = GSAbs(Mouse.Pos.x - Mouse.DownPos.x); + r32 YDistance = GSAbs(Mouse.Pos.y - Mouse.DownPos.y); + + if (XDistance > YDistance) + { + r32 XPercent = (Mouse.Pos.x - PanelMin.x) / (PanelMax.x - PanelMin.x); + SplitPanelVertically(Panel, XPercent, PanelMin, PanelMax, PanelLayout); + } + else + { + r32 YPercent = (Mouse.Pos.y - PanelMin.y) / (PanelMax.y - PanelMin.y); + SplitPanelHorizontally(Panel, YPercent, PanelMin, PanelMax, PanelLayout); + } + } + else if (Panel->SplitDirection == PanelSplit_Horizontal) + { + r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); + r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.y - SplitY); + if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) + { + r32 NewSplitY = Mouse.Pos.y; + if (NewSplitY <= PanelMin.y) + { + ConsolidatePanelsKeepOne(Panel, Panel->Top, PanelLayout); + } + else if (NewSplitY >= PanelMax.y) + { + ConsolidatePanelsKeepOne(Panel, Panel->Bottom, PanelLayout); + } + else + { + Panel->SplitPercent = (NewSplitY - PanelMin.y) / (PanelMax.y - PanelMin.y); + } + } + else + { + HandleMousePanelInteractionOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, PanelLayout, Mouse); + HandleMousePanelInteractionOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, PanelLayout, Mouse); + } + } + else if (Panel->SplitDirection == PanelSplit_Vertical) + { + r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); + r32 ClickDistanceFromSplit = GSAbs(Mouse.DownPos.x - SplitX); + if (ClickDistanceFromSplit < PanelEdgeClickMaxDistance) + { + r32 NewSplitX = Mouse.Pos.x; + if (NewSplitX <= PanelMin.x) + { + ConsolidatePanelsKeepOne(Panel, Panel->Right, PanelLayout); + } + else if (NewSplitX >= PanelMax.x) + { + ConsolidatePanelsKeepOne(Panel, Panel->Left, PanelLayout); + } + else + { + Panel->SplitPercent = (NewSplitX - PanelMin.x) / (PanelMax.x - PanelMin.x); + } + } + else + { + HandleMousePanelInteractionOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, PanelLayout, Mouse); + HandleMousePanelInteractionOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, PanelLayout, Mouse); + } + } +} + +internal void +HandleMousePanelInteraction(panel_layout* PanelLayout, v2 WindowMin, v2 WindowMax, mouse_state Mouse) +{ + r32 PanelEdgeClickMaxDistance = 4; + + if (MouseButtonTransitionedUp(Mouse.LeftButtonState)) + { + Assert(PanelLayout->PanelsUsed > 0); + panel* FirstPanel = &PanelLayout->Panels[0].Panel; + HandleMousePanelInteractionOrRecurse(FirstPanel, WindowMin, WindowMax, PanelLayout, Mouse); + } +} + +internal void +DrawPanelBorder(panel Panel, v2 PanelMin, v2 PanelMax, v4 Color, render_command_buffer* RenderBuffer) +{ + PushRenderBoundingBox2D(RenderBuffer, PanelMin, PanelMax, 1, Color); +} + +internal void +DrawPanelOrRecurse(panel* Panel, v2 PanelMin, v2 PanelMax, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) +{ + if (Panel->SplitDirection == PanelSplit_NoSplit) + { + RenderPanel(Panel, PanelMin, PanelMax, WindowMin, WindowMax, RenderBuffer, State, Context, Mouse); + v4 BorderColor = v4{0, 1, 1, 1}; + if (PointIsInRange(Mouse.Pos, PanelMin, PanelMax)) + { + BorderColor = v4{1, 0, 1, 1}; + } + +PushRenderOrthographic(RenderBuffer, WindowMin.x, WindowMin.y, WindowMax.x, WindowMax.y); +DrawPanelBorder(*Panel, PanelMin, PanelMax, BorderColor, RenderBuffer); + } + else if (Panel->SplitDirection == PanelSplit_Horizontal) + { + r32 SplitY = GSLerp(PanelMin.y, PanelMax.y, Panel->SplitPercent); + DrawPanelOrRecurse(&Panel->Bottom->Panel, PanelMin, v2{PanelMax.x, SplitY}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + DrawPanelOrRecurse(&Panel->Top->Panel, v2{PanelMin.x, SplitY}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + } + else if (Panel->SplitDirection == PanelSplit_Vertical) + { + r32 SplitX = GSLerp(PanelMin.x, PanelMax.x, Panel->SplitPercent); + DrawPanelOrRecurse(&Panel->Left->Panel, PanelMin, v2{SplitX, PanelMax.y}, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + DrawPanelOrRecurse(&Panel->Right->Panel, v2{SplitX, PanelMin.y}, PanelMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); + } +} + +internal void +DrawAllPanels(panel_layout PanelLayout, v2 WindowMin, v2 WindowMax, render_command_buffer* RenderBuffer, interface_config Interface, mouse_state Mouse, app_state* State, context Context) +{ + Assert(PanelLayout.PanelsUsed > 0); + panel* FirstPanel = &PanelLayout.Panels[0].Panel; + DrawPanelOrRecurse(FirstPanel, WindowMin, WindowMax, WindowMin, WindowMax, RenderBuffer, Interface, Mouse, State, Context); +} diff --git a/src/panels/foldhaus_panel_animation_timeline.h b/src/panels/foldhaus_panel_animation_timeline.h index 9d965e0..45957aa 100644 --- a/src/panels/foldhaus_panel_animation_timeline.h +++ b/src/panels/foldhaus_panel_animation_timeline.h @@ -1,3 +1,12 @@ +// TODO +// [] - Moving animation blocks +// [] - dragging beginning and end of time blocks +// [] - creating a timeblock with a specific animation +// [x] - play, pause, stop, +// [] - setting the start and end of the animation system +// [] - displaying multiple layers +// [] - + PANEL_INIT_PROC(AnimationTimeline_Init) {