Consolidated all panel code in one file, and removed all external dependencies. ITS A LIBRARY NOW (still needs refinement)

This commit is contained in:
Peter Slattery 2019-12-26 13:14:00 -08:00
parent 9b9fe2f5e1
commit b83d718d37
6 changed files with 384 additions and 356 deletions

View File

@ -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) FOLDHAUS_INPUT_COMMAND_PROC(DeleteAnimationBlock)
{ {

View File

@ -1,6 +1,85 @@
#include "foldhaus_platform.h" #include "foldhaus_platform.h"
#include "foldhaus_app.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 internal v4
MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 WindowHeight) MouseToWorldRay(r32 MouseX, r32 MouseY, camera* Camera, r32 WindowWidth, r32 WindowHeight)
{ {
@ -271,9 +350,9 @@ INITIALIZE_APPLICATION(InitializeApplication)
{ // Panels Playground { // Panels Playground
InitializePanelLayout(&State->PanelLayout); InitializePanelLayout(&State->PanelLayout);
panel* Panel = TakeNewPanel(&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); 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 } // End Panels Playground
} }
@ -557,13 +636,12 @@ PushRenderOrthographic(RenderBuffer, 0, 0, Context.WindowWidth, Context.WindowHe
} }
} }
v2 TimelineMin = v2{0, 0}; DrawDebugInterface(RenderBuffer, 25,
v2 TimelineMax = v2{Context.WindowWidth, 125}; State->Interface, Context.WindowWidth, Context.WindowHeight - TopBarHeight,
animation_block_handle NewSelection = DrawAnimationPanel(&State->AnimationSystem, Context.DeltaTime, State, State->Camera, Mouse, &State->Transient);
TimelineMin, TimelineMax, #endif
State->SelectedAnimationBlockHandle, }
RenderBuffer, State->Interface, Mouse);
State->SelectedAnimationBlockHandle = NewSelection;
for (s32 m = 0; m < State->Modes.ActiveModesCount; m++) for (s32 m = 0; m < State->Modes.ActiveModesCount; 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 // Checking for overflows
{ {

View File

@ -17,6 +17,7 @@
typedef struct app_state app_state; typedef struct app_state app_state;
#include "foldhaus_panel.h" #include "foldhaus_panel.h"
#include "foldhaus_command_dispatch.h" #include "foldhaus_command_dispatch.h"
@ -171,10 +172,27 @@ r32 GreenSize = 20.0f;
#include "foldhaus_interface.cpp" #include "foldhaus_interface.cpp"
#include "animation/foldhaus_animation_interface.h" #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_sculpture_view.h"
#include "panels/foldhaus_panel_profiler.h" #include "panels/foldhaus_panel_profiler.h"
#include "panels/foldhaus_panel_dmx_view.h" #include "panels/foldhaus_panel_dmx_view.h"
#include "panels/foldhaus_panel_animation_timeline.h" #include "panels/foldhaus_panel_animation_timeline.h"
#include "generated/foldhaus_panels_generated.h" #include "generated/foldhaus_panels_generated.h"
#include "foldhaus_panel.cpp"

View File

@ -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);
}

View File

@ -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; 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 enum panel_split_direction
{ {
PanelSplit_NoSplit, PanelSplit_NoSplit,
@ -23,8 +25,7 @@ typedef struct panel_entry panel_entry;
struct panel struct panel
{ {
panel_render_proc* Render; s32 PanelDefinitionIndex;
panel_cleanup_proc* Cleanup;
panel_split_direction SplitDirection; panel_split_direction SplitDirection;
r32 SplitPercent; r32 SplitPercent;
@ -63,11 +64,259 @@ panel_entry Panels[PANELS_MAX];
panel_entry FreeList; 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; Layout->FreeList.Free.Next = &Layout->FreeList;
s32 PanelNameLength; }
panel_init_proc* Init;
panel_cleanup_proc* Cleanup; internal panel_entry*
panel_render_proc* Render; 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);
}

View File

@ -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) PANEL_INIT_PROC(AnimationTimeline_Init)
{ {