Lumenarium/src/app/editor/foldhaus_panel.h

448 lines
13 KiB
C
Raw Normal View History

2020-01-02 02:41:43 +00:00
//
// File: foldhaus_panel.h
// Author: Peter Slattery
// Creation Date: 2019-12-26
//
// Usage:
// Include this file in ONE file in your project.
2020-01-02 02:41:43 +00:00
// Define SetPanelDefinitionExternal
//
#ifndef FOLDHAUS_PANEL_H
enum panel_split_direction
{
PanelSplit_NoSplit,
PanelSplit_Horizontal,
PanelSplit_Vertical,
PanelSplit_Count,
};
typedef struct panel_entry panel_entry;
typedef struct panel panel;
#define PANEL_MODAL_OVERRIDE_CALLBACK(name) void name(panel* ReturningFrom, app_state* State, context Context)
typedef PANEL_MODAL_OVERRIDE_CALLBACK(panel_modal_override_callback);
struct panel
{
s32 TypeIndex;
gs_data StateMemory;
panel_entry* ModalOverride;
panel* IsModalOverrideFor; // TODO(pjs): I don't like that this is panel* but ModalOverride is panel_entry*
panel_modal_override_callback* ModalOverrideCB;
panel_split_direction SplitDirection;
r32 SplitPercent;
// TODO(Peter): This REALLY doesn't want to live here
// Probably belongs in a more generalized PanelInterfaceState or something
b32 PanelSelectionMenuOpen;
union{
panel_entry* Left;
panel_entry* Top;
};
union{
panel_entry* Right;
panel_entry* Bottom;
};
};
struct free_panel
{
panel_entry* Next;
};
struct panel_entry
{
panel Panel;
free_panel Free;
};
#define PANEL_INIT_PROC(name) void name(panel* Panel, app_state* State, context Context)
typedef PANEL_INIT_PROC(panel_init_proc);
#define PANEL_CLEANUP_PROC(name) void name(panel* Panel, app_state* State)
typedef PANEL_CLEANUP_PROC(panel_cleanup_proc);
#define PANEL_RENDER_PROC(name) void name(panel* Panel, rect2 PanelBounds, render_command_buffer* RenderBuffer, app_state* State, context Context)
typedef PANEL_RENDER_PROC(panel_render_proc);
// NOTE(Peter): This is used by the meta system to generate panel type info
struct panel_definition
{
char* PanelName;
s32 PanelNameLength;
panel_init_proc* Init;
panel_cleanup_proc* Cleanup;
panel_render_proc* Render;
input_command* InputCommands;
s32 InputCommandsCount;
};
#define PANELS_MAX 16
struct panel_system
{
panel_definition* PanelDefs;
u32 PanelDefsCount;
panel_entry Panels[PANELS_MAX];
u32 PanelsUsed;
panel_entry FreeList;
};
// NOTE(Peter): This representation is used to let external code render and interact
// with panels. It shouldn't be stored across frame boundaries as the pointers to
// Panel's are liable to change.
struct panel_with_layout
{
panel* Panel;
rect2 Bounds;
};
struct panel_layout
{
panel_with_layout* Panels;
u32 PanelsCount;
u32 PanelsMax;
};
/////////////////////////////////
//
// Book-Keeping
//
/////////////////////////////////
internal void
InitializePanelSystem(panel_system* PanelSystem, panel_definition* PanelDefs, u32 PanelDefsCount)
{
PanelSystem->FreeList.Free.Next = &PanelSystem->FreeList;
PanelSystem->PanelDefs = PanelDefs;
PanelSystem->PanelDefsCount = PanelDefsCount;
}
internal panel_entry*
TakeNewPanelEntry(panel_system* PanelSystem)
{
panel_entry* FreeEntry = 0;
if (PanelSystem->FreeList.Free.Next != &PanelSystem->FreeList)
{
FreeEntry = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = FreeEntry->Free.Next;
}
else
{
Assert(PanelSystem->PanelsUsed < PANELS_MAX);
FreeEntry = PanelSystem->Panels + PanelSystem->PanelsUsed++;
}
return FreeEntry;
}
internal void
FreePanelEntry(panel_entry* Entry, panel_system* PanelSystem)
{
Assert(Entry >= PanelSystem->Panels && Entry <= PanelSystem->Panels + PANELS_MAX);
Entry->Panel = {0};
Entry->Free.Next = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = Entry;
}
internal void
FreePanelEntryRecursive(panel_entry* Entry, panel_system* PanelSystem)
{
if (Entry->Panel.SplitDirection != PanelSplit_NoSplit)
{
FreePanelEntryRecursive(Entry->Panel.Left, PanelSystem);
FreePanelEntryRecursive(Entry->Panel.Right, PanelSystem);
}
FreePanelEntry(Entry, PanelSystem);
}
internal void
FreePanelAtIndex(s32 Index, panel_system* PanelSystem)
{
Assert(Index > 0 && Index < (s32)PanelSystem->PanelsUsed);
panel_entry* EntryToFree = PanelSystem->Panels + Index;
EntryToFree->Free.Next = PanelSystem->FreeList.Free.Next;
PanelSystem->FreeList.Free.Next = EntryToFree;
}
internal panel_entry*
Panel_GetModalOverride(panel_entry* PanelEntry)
{
panel_entry* Result = PanelEntry;
if (PanelEntry->Panel.ModalOverride != 0)
{
Result = Panel_GetModalOverride(PanelEntry->Panel.ModalOverride);
}
return Result;
}
internal panel*
Panel_GetModalOverride(panel* Panel)
{
panel* Result = Panel;
if (Panel->ModalOverride != 0)
{
Result = &Panel_GetModalOverride(Panel->ModalOverride)->Panel;
}
return Result;
}
internal void
Panel_PushModalOverride(panel* Root, panel_entry* Override, panel_modal_override_callback* Callback)
{
Root->ModalOverride = Override;
Root->ModalOverrideCB = Callback;
Override->Panel.IsModalOverrideFor = Root;
}
internal void
Panel_PopModalOverride(panel* Parent, panel_system* System)
{
// TODO(pjs): Free the overrided panel
FreePanelEntry(Parent->ModalOverride, System);
Parent->ModalOverride = 0;
}
internal void
Panel_SetCurrentType(panel* Panel, panel_system* System, s32 NewPanelType, gs_data TypeStateMemory, app_state* State, context Context)
{
s32 OldTypeIndex = Panel->TypeIndex;
Panel->TypeIndex = NewPanelType;
Panel->StateMemory = TypeStateMemory;
if(OldTypeIndex >= 0)
{
System->PanelDefs[OldTypeIndex].Cleanup(Panel, State);
}
}
internal void
SetAndInitPanelType(panel* Panel, panel_system* System, s32 NewPanelTypeIndex, app_state* State, context Context)
{
gs_data EmptyStateData = {0};
Panel_SetCurrentType(Panel, System, NewPanelTypeIndex, EmptyStateData, State, Context);
System->PanelDefs[NewPanelTypeIndex].Init(Panel, State, Context);
}
#define Panel_GetStateStruct(p, type) (type*)Panel_GetStateMemory((p), sizeof(type)).Memory
internal gs_data
Panel_GetStateMemory(panel* Panel, u64 Size)
{
Assert(Panel->StateMemory.Size == Size);
gs_data Result = Panel->StateMemory;
return Result;
}
internal panel_entry*
PanelSystem_PushPanel(panel_system* PanelSystem, s32 PanelTypeIndex, app_state* State, context Context)
{
panel_entry* PanelEntry = TakeNewPanelEntry(PanelSystem);
SetAndInitPanelType(&PanelEntry->Panel, PanelSystem, PanelTypeIndex, State, Context);
return PanelEntry;
}
internal void
SplitPanel(panel* Parent, r32 Percent, panel_split_direction SplitDirection, panel_system* PanelSystem, app_state* State, context Context)
{
if (Percent >= 0.0f && Percent <= 1.0f)
{
Parent->SplitDirection = SplitDirection;
Parent->SplitPercent = Percent;
s32 ParentTypeIndex = Parent->TypeIndex;
gs_data ParentStateMemory = Parent->StateMemory;
Parent->Left = TakeNewPanelEntry(PanelSystem);
Panel_SetCurrentType(&Parent->Left->Panel, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
Parent->Right = TakeNewPanelEntry(PanelSystem);
Panel_SetCurrentType(&Parent->Right->Panel, PanelSystem, ParentTypeIndex, ParentStateMemory, State, Context);
}
}
internal void
SplitPanelVertically(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
{
SplitPanel(Parent, Percent, PanelSplit_Vertical, PanelSystem, State, Context);
}
internal void
SplitPanelHorizontally(panel* Parent, r32 Percent, panel_system* PanelSystem, app_state* State, context Context)
{
SplitPanel(Parent, Percent, PanelSplit_Horizontal, PanelSystem, State, Context);
}
internal void
ConsolidatePanelsKeepOne(panel* Parent, panel_entry* PanelEntryToKeep, panel_system* PanelSystem)
{
panel_entry* LeftChild = Parent->Left;
panel_entry* RightChild = Parent->Right;
panel_entry* PanelEntryToDestroy = PanelEntryToKeep == LeftChild ? RightChild : LeftChild;
*Parent = PanelEntryToKeep->Panel;
FreePanelEntry(PanelEntryToKeep, PanelSystem);
FreePanelEntryRecursive(PanelEntryToDestroy, PanelSystem);
}
/////////////////////////////////
//
// Rendering And Interaction
//
/////////////////////////////////
internal rect2
GetTopPanelBounds(panel* Panel, rect2 PanelBounds)
{
rect2 Result = {};
Result.Min = v2{
PanelBounds.Min.x,
LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
};
Result.Max = PanelBounds.Max;
return Result;
}
internal rect2
GetBottomPanelBounds(panel* Panel, rect2 PanelBounds)
{
rect2 Result = {};
Result.Min = PanelBounds.Min;
Result.Max = v2{
PanelBounds.Max.x,
LerpR32(Panel->SplitPercent, PanelBounds.Min.y, PanelBounds.Max.y)
};
return Result;
}
internal rect2
GetRightPanelBounds(panel* Panel, rect2 PanelBounds)
{
rect2 Result = {};
Result.Min = v2{
LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
PanelBounds.Min.y
};
Result.Max = PanelBounds.Max;
return Result;
}
internal rect2
GetLeftPanelBounds(panel* Panel, rect2 PanelBounds)
{
rect2 Result = {};
Result.Min = PanelBounds.Min;
Result.Max = v2{
LerpR32(Panel->SplitPercent, PanelBounds.Min.x, PanelBounds.Max.x),
PanelBounds.Max.y
};
return Result;
}
internal void
LayoutPanel(panel* Panel, rect2 PanelBounds, panel_layout* Layout)
{
if (Panel->SplitDirection == PanelSplit_NoSplit)
{
panel_with_layout* WithLayout = Layout->Panels + Layout->PanelsCount++;
WithLayout->Panel = Panel_GetModalOverride(Panel);
WithLayout->Bounds = PanelBounds;
}
else if (Panel->SplitDirection == PanelSplit_Horizontal)
{
rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
panel* TopPanel = Panel_GetModalOverride(&Panel->Top->Panel);
panel* BottomPanel = Panel_GetModalOverride(&Panel->Bottom->Panel);
LayoutPanel(&Panel->Top->Panel, TopPanelBounds, Layout);
LayoutPanel(&Panel->Bottom->Panel, BottomPanelBounds, Layout);
}
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
panel* LeftPanel = Panel_GetModalOverride(&Panel->Top->Panel);
panel* RightPanel = Panel_GetModalOverride(&Panel->Bottom->Panel);
LayoutPanel(&Panel->Left->Panel, LeftPanelBounds, Layout);
LayoutPanel(&Panel->Right->Panel, RightPanelBounds, Layout);
}
}
internal panel_layout
GetPanelLayout(panel_system* System, rect2 WindowBounds, gs_memory_arena* Storage)
{
panel_layout Result = {};
Result.PanelsMax = System->PanelsUsed;
Result.Panels = PushArray(Storage, panel_with_layout, Result.PanelsMax);
LayoutPanel(&System->Panels[0].Panel, WindowBounds, &Result);
return Result;
}
internal panel_with_layout
GetPanelContainingPoint(v2 Point, panel* Panel, rect2 PanelBounds)
{
panel_with_layout Result = {0};
if (Panel->SplitDirection == PanelSplit_NoSplit)
{
Result.Panel = Panel;
Result.Bounds = PanelBounds;
}
else if (Panel->SplitDirection == PanelSplit_Horizontal)
{
rect2 TopPanelBounds = GetTopPanelBounds(Panel, PanelBounds);
rect2 BottomPanelBounds = GetBottomPanelBounds(Panel, PanelBounds);
if (PointIsInRect(TopPanelBounds, Point))
{
Result = GetPanelContainingPoint(Point, &Panel->Top->Panel, TopPanelBounds);
}
else if (PointIsInRect(BottomPanelBounds, Point))
{
Result = GetPanelContainingPoint(Point, &Panel->Bottom->Panel, BottomPanelBounds);
}
}
else if (Panel->SplitDirection == PanelSplit_Vertical)
{
rect2 LeftPanelBounds = GetLeftPanelBounds(Panel, PanelBounds);
rect2 RightPanelBounds = GetRightPanelBounds(Panel, PanelBounds);
if (PointIsInRect(LeftPanelBounds, Point))
{
Result = GetPanelContainingPoint(Point, &Panel->Left->Panel, LeftPanelBounds);
}
else if (PointIsInRect(RightPanelBounds, Point))
{
Result = GetPanelContainingPoint(Point, &Panel->Right->Panel, RightPanelBounds);
}
}
return Result;
}
internal panel_with_layout
GetPanelContainingPoint(v2 Point, panel_system* PanelSystem, rect2 WindowBounds)
{
panel_with_layout Result = {0};
if (PanelSystem->PanelsUsed > 0)
{
Result = GetPanelContainingPoint(Point, &PanelSystem->Panels[0].Panel, WindowBounds);
}
return Result;
}
2020-01-02 02:41:43 +00:00
#define FOLDHAUS_PANEL_H
#endif // FOLDHAUS_PANEL_H