vn/code/vn_workspace_nav_editor.cpp

442 lines
15 KiB
C++

UI_CUSTOM_DRAW_CALLBACK(BuildNavViewDrawCallback)
{
scene_view *SceneView = (scene_view *)Data;
SV_DrawBackground(SceneView, Box, Group);
}
static void W_NavEditorSerializeItems(workspace_view_nav_editor *Editor)
{
temp Scratch = GetScratch();
//- sixten: find total size
u64 TotalSize = sizeof(u16); // item count
for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next)
{
string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]);
TotalSize += 1; TotalSize += sizeof(u16); TotalSize += AssetName.Count; // texture id
TotalSize += 1; TotalSize += sizeof(r32); // scale
TotalSize += 1; TotalSize += sizeof(v2_r32); // origin
TotalSize += 1; TotalSize += sizeof(v2_r32); // p
TotalSize += 1; TotalSize += sizeof(u16); TotalSize += Node->Item.HoverText.Count; // hover text
TotalSize += 1; TotalSize += sizeof(u16); TotalSize += sizeof(u8); TotalSize += Node->Item.Action.Content.Count; // action
TotalSize += 1; // end of item
}
u8 *FileData = PushArray(Scratch.Arena, u8, TotalSize);
u8 *Data = FileData;
*(u16 *)Data = Editor->Items.Count;
Data += sizeof(u16);
#define WriteByte(Op) do { *Data++ = Op; } while(0)
#define WriteField(Op, type, Member) do { *Data++ = Op; *(type *)Data = Member; Data += sizeof(type); } while(0)
#define WriteString(String) do { *(u16 *)Data = (u16)String.Count; Data += sizeof(u16); Copy(Data, String.Data, String.Count); Data += String.Count; } while(0)
for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next)
{
scene_nav_item *Item = &Node->Item;
string AssetName = MakeString(AssetNameLUT[Node->Item.TextureID]);
WriteByte(S_NavItemOp_TextureID); WriteString(AssetName);
WriteField(S_NavItemOp_Scale, r32, Item->Scale);
WriteField(S_NavItemOp_Origin, v2_r32, Item->Origin);
WriteField(S_NavItemOp_P, v2_r32, Item->P);
WriteByte(S_NavItemOp_HoverText); WriteString(Item->HoverText);
WriteByte(S_NavItemOp_Action); WriteByte(Item->Action.Kind); WriteString(Item->Action.Content);
WriteByte(S_NavItemOp_None);
}
#undef WriteByte
#undef WriteField
#undef WriteString
platform_file_handle File = Platform.OpenFile(PushFormat(Scratch.Arena, "%S/%S", Editor->FilePath, Editor->FileName), PlatformAccess_Write);
if(File.IsValid)
{
Platform.WriteFile(File, FileData, 0, TotalSize);
Platform.CloseFile(File);
}
ReleaseScratch(Scratch);
}
static workspace_string_chunk *W_StringChunkAlloc(arena *Arena, workspace_string_chunk_list *FreeList)
{
workspace_string_chunk *Result = FreeList->First;
if(Result)
{
DLLRemove(FreeList->First, FreeList->Last, Result);
*Result = {};
}
else
{
Result = PushStruct(Arena, workspace_string_chunk);
}
return(Result);
}
static void W_StringChunkRelease(workspace_string_chunk_list *FreeList, workspace_string_chunk *Chunk)
{
DLLInsertLast(FreeList->First, FreeList->Last, Chunk);
}
static scene_nav_item_node *W_SceneNavItemNodeAlloc(arena *Arena, workspace_view_nav_editor *Editor)
{
scene_nav_item_node *Result = Editor->FirstFree;
if(Result)
{
DLLRemove(Editor->FirstFree, Editor->LastFree, Result);
*Result = {};
}
else
{
Result = PushStruct(Arena, scene_nav_item_node);
}
Result->HoverTextStringChunk = W_StringChunkAlloc(Arena, &Editor->FreeStrings);
Result->ActionStringChunk = W_StringChunkAlloc(Arena, &Editor->FreeStrings);
Result->Item.HoverText.Data = Result->HoverTextStringChunk->Data;
Result->Item.Action.Content.Data = Result->ActionStringChunk->Data;
DLLInsertLast(Editor->Items.First, Editor->Items.Last, Result);
Editor->Items.Count += 1;
return(Result);
}
static void W_SceneNavItemNodeRelease(workspace_view_nav_editor *Editor, scene_nav_item_node *Node)
{
W_StringChunkRelease(&Editor->FreeStrings, Node->HoverTextStringChunk);
W_StringChunkRelease(&Editor->FreeStrings, Node->ActionStringChunk);
DLLRemove(Editor->Items.First, Editor->Items.Last, Node);
DLLInsertLast(Editor->FirstFree, Editor->LastFree, Node);
Editor->Items.Count -= 1;
}
static void W_NavEditorInspectNode(workspace_view_nav_editor *Editor, scene_nav_item_node *Node)
{
Editor->SelectedItem = Node;
Editor->TextureDropdownOpen = false;
Editor->NavActionDropdownOpen = false;
}
static void W_NavEditorSetup(workspace_view *View, string FilePath, string FileName, string FileContents)
{
workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data;
//- sixten: setup file info
Editor->FileName = PushString(View->Arena, FileName);
Editor->FilePath = PushString(View->Arena, FilePath);
//- sixten: deserialize data
if(FileContents.Count != 0)
{
u8 *DataBegin = FileContents.Data;
u8 *Byte = DataBegin;
u16 ItemCount = *(u16 *)Byte;
Byte += 2;
//- sixten: parse items
for(u64 ItemIndex = 0; ItemIndex < ItemCount; ItemIndex += 1)
{
scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor);
scene_nav_item *Item = &Node->Item;
for(;;)
{
switch(*Byte++)
{
case S_NavItemOp_TextureID:
{
string AssetName;
AssetName.Count = *(u16 *)Byte;
Byte += sizeof(u16);
AssetName.Data = Byte;
Byte += AssetName.Count;
Item->TextureID = AssetID_Error;
for(u64 AssetIndex = 0; AssetIndex < AssetID_COUNT; AssetIndex += 1)
{
if(AreEqual(MakeString(AssetNameLUT[AssetIndex]), AssetName))
{
Item->TextureID = AssetIndex;
break;
}
}
} goto Next;
case S_NavItemOp_Scale:
{
Item->Scale = *(r32 *)Byte;
Byte += sizeof(r32);
} goto Next;
case S_NavItemOp_Origin:
{
Item->Origin = *(v2_r32 *)Byte;
Byte += sizeof(v2_r32);
} goto Next;
case S_NavItemOp_P:
{
Item->P = *(v2_r32 *)Byte;
Byte += sizeof(v2_r32);
} goto Next;
case S_NavItemOp_HoverText:
{
Item->HoverText.Count = *(u16 *)Byte;
Byte += 2;
Assert(Item->HoverText.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *));
Copy(Item->HoverText.Data, Byte, Item->HoverText.Count);
Byte += Item->HoverText.Count;
} goto Next;
case S_NavItemOp_Action:
{
Item->Action.Kind = (scene_nav_action_kind)*Byte;
Byte += 1;
Item->Action.Content.Count = *(u16 *)Byte;
Byte += 2;
Assert(Item->Action.Content.Count <= W_STRING_CHUNK_SIZE-2*sizeof(void *));
Copy(Item->Action.Content.Data, Byte, Item->Action.Content.Count);
Byte += Item->Action.Content.Count;
} goto Next;
}
//- sixten: no op found, assume item done
break;
Next:;
}
}
}
}
static void W_BuildNavEditor(workspace_view *View)
{
workspace_view_nav_editor *Editor = (workspace_view_nav_editor *)View->Data;
scene_view *SceneView = SV_GetState();
UI_BackgroundColor(V4(0.25, 0.25, 0.25, 1))
UI_BorderColor(V4(0.45, 0.45, 0.45, 1))
UI_WidthFill UI_Height(UI_Em(2.0f, 1.0f))
UI_LayoutAxis(Axis2_X)
UI_Parent(UI_MakeBoxF(UI_BoxFlag_DrawBackground|UI_BoxFlag_DrawBorder, "Workspace Nav Editor Toolbar"))
{
UI_Width(UI_Em(2.0f, 1.0f)) UI_Font(Font_Icons) UI_CornerRadius(4.0f)
{
//- sixten: create nav item button
if(UI_ButtonF("%U", FontIcon_UserPlus).Clicked)
{
scene_nav_item_node *Node = W_SceneNavItemNodeAlloc(View->Arena, Editor);
//- sixten: apply default options
Node->Item.Scale = 0.01f;
Node->Item.Origin = V2R32(0.5, 1.0f);
Node->Item.P = V2R32(0.0, 0.0f);
Node->Item.TextureID = AssetID_ArthurNormal;
}
//- sixten: create save nav items button
if(UI_ButtonF("%U", FontIcon_Floppy).Clicked)
{
W_NavEditorSerializeItems(Editor);
}
//- sixten: create force reload
if(UI_ButtonF("%U", FontIcon_ArrowsCW).Clicked)
{
SV_LoadNavItems();
}
}
}
UI_Row()
{
//- sixten: build inspector panel
scene_nav_item_node *SelectedItem = Editor->SelectedItem;
UI_SetNextLayoutAxis(Axis2_X);
UI_Width(UI_Em(24.0f*AC_AnimateValueF(SelectedItem != 0, 0, 0.3f, "Workspace Nav Editor Inspector %p", Editor), 1.0f)) UI_HeightFill
UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder, StrLit("Workspace Nav Editor Inspector")))
UI_Padding(UI_Em(1, 1)) UI_Width(UI_Percent(1.0f, 0.0f)) UI_Column()
{
if(SelectedItem)
{
UI_CornerRadius(4.0f) UI_Width(UI_Percent(1, 1)) UI_Height(UI_TextContent(15, 1))
{
//- sixten: inspect position
UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position X:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.x);}
UI_Row() UI_Width(UI_Percent(0.5f, 1.0f)) {UI_LabelF("Position Y:"); UI_Font(Font_Monospace) UI_LabelF("%f", SelectedItem->Item.P.y);}
//- sixten: inspect scale
UI_Row() UI_Width(UI_Percent(0.5f, 1.0f))
{
UI_LabelF("Scale:");
UI_Font(Font_Monospace)
{
UI_SetNextHoverCursor(PlatformCursor_ArrowHorizontal);
ui_box *ScaleBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_DrawText, "%f##Scale", SelectedItem->Item.Scale);
ui_signal ScaleSignal = UI_SignalFromBox(ScaleBox);
if(ScaleSignal.Dragging)
{
if(ScaleSignal.Pressed)
{
UI_StoreDragR32(SelectedItem->Item.Scale);
}
SelectedItem->Item.Scale = UI_GetDragR32() + ScaleSignal.DragDelta.x*0.001f;
}
}
}
//- sixten: inspect texture
UI_Row(0, StrLit("Texture")) UI_Width(UI_Percent(0.5f, 1.0f))
{
UI_LabelF("Texture:");
UI_DropdownSelection(AssetNameLUT, ArrayCount(AssetNameLUT), &Editor->TextureDropdownOpen, &SelectedItem->Item.TextureID);
}
//- sixten: inspect nav action kind
UI_Row(0, StrLit("Nav Action")) UI_Width(UI_Percent(0.5f, 1.0f))
{
UI_LabelF("Nav Action Type:");
char *Actions[] = { "None", "Trigger Proc", "Change Scene" };
UI_DropdownSelection(Actions, ArrayCount(Actions), &Editor->NavActionDropdownOpen, (s32 *)&SelectedItem->Item.Action);
}
//- sixten: inspect nav action contents
UI_Row(0, StrLit("Nav Action String")) UI_Width(UI_Percent(0.5f, 1.0f))
{
UI_LabelF("Nav Action Contents:");
ui_signal Signal = UI_LineEdit(&Editor->NavActionStringEditState, ArrayCount(SelectedItem->ActionStringChunk->Data), SelectedItem->ActionStringChunk->Data, &SelectedItem->Item.Action.Content.Count, StrLit("Nav Action Text"), Editor->ActiveTextThing == 1);
if(AreEqual(UI_ActiveKey(), Signal.Box->Key))
{
Editor->ActiveTextThing = 1;
}
else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 1)
{
Editor->ActiveTextThing = 0;
}
}
//- sixten: inspect hover text
UI_Row(0, StrLit("Hover Text")) UI_Width(UI_Percent(0.5f, 1.0f))
{
UI_LabelF("Hover Text:");
ui_signal Signal = UI_LineEdit(&Editor->HoverTextEditState, ArrayCount(SelectedItem->HoverTextStringChunk->Data), SelectedItem->HoverTextStringChunk->Data, &SelectedItem->Item.HoverText.Count, StrLit("Hover Text Input"), Editor->ActiveTextThing == 2);
if(AreEqual(UI_ActiveKey(), Signal.Box->Key))
{
Editor->ActiveTextThing = 2;
}
else if(!AreEqual(UI_ActiveKey(), UI_EmptyKey()) && Editor->ActiveTextThing == 2)
{
Editor->ActiveTextThing = 0;
}
}
UI_Spacer(UI_TextContent(15, 1));
UI_Row() UI_FillPadding
{
UI_Width(UI_TextContent(15, 1)) if(UI_ButtonF("Remove Item").Pressed)
{
W_SceneNavItemNodeRelease(Editor, SelectedItem);
Editor->SelectedItem = 0;
}
}
}
}
}
//- sixten: build nav view
UI_Width(UI_Percent(1, 0)) UI_Height(UI_Percent(1, 0)) UI_Parent(UI_MakeBoxF(UI_BoxFlag_Clip, "Workspace View Nav Editor Thing"))
{
//- sixten: calculate nav view size
v2_r32 ParentDim = DimOfRange(UI_TopParent()->Rect);
v2_r32 BoxDim = ParentDim;
r32 TargetRatio = 16.0f/9.0f;
r32 ActualRatio = ParentDim.x/ParentDim.y;
if(ActualRatio>TargetRatio)
{
BoxDim.x = BoxDim.y*TargetRatio;
}
else
{
BoxDim.y = BoxDim.x/TargetRatio;
}
UI_SetNextWidth(UI_Pixels(BoxDim.x, 1));
UI_SetNextHeight(UI_Pixels(BoxDim.y, 1));
UI_SetNextLayoutAxis(Axis2_Y);
UI_SetNextFixedP((ParentDim-BoxDim)*0.5f);
ui_box *Box = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_DrawDropShadow|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_Clickable, "Nav View %p", View);
UI_EquipBoxCustomDrawCallback(Box, BuildNavViewDrawCallback, SceneView);
r32 GlobalScale = CalculateGlobalScaleFromDim(BoxDim);
//- sixten: build all nav items
s32 ItemIndex = 0;
UI_Parent(Box) for(scene_nav_item_node *Node = Editor->Items.First; Node != 0; Node = Node->Next, ItemIndex += 1)
{
scene_nav_item *Item = &Node->Item;
//- sixten: calculate item position
r32 AppliedScale = GlobalScale*Item->Scale;
render_handle Texture = TextureFromAssetID(Item->TextureID);
v2_r32 TextureDim = ConvertV2ToR32(DimFromTexture(Texture));
v2_r32 TextureOrigin = Hadamard(TextureDim, Item->Origin);
v2_r32 Dim = TextureDim*AppliedScale;
v2_r32 OriginP = TextureOrigin*AppliedScale;
//- sixten: build the item
scene_nav_item_info *Data = PushStruct(UI_FrameArena(), scene_nav_item_info);
Data->Item = Item;
UI_SetNextHoverCursor(PlatformCursor_Hand);
UI_SetNextSize(UI_Pixels(Dim.x, 1), UI_Pixels(Dim.y, 1));
ui_box *ItemBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|((Node==Editor->SelectedItem)?UI_BoxFlag_DrawBorder:0),
"View Item Box %i", ItemIndex);
UI_EquipBoxCustomDrawCallback(ItemBox, BuildNavItemDrawCallback, Data);
if(Node == Editor->SelectedItem)
{
// sixten(TODO): this:
//ui_box *OriginBox = UI_MakeBoxF(UI_BoxFlag_Clickable|UI_BoxFlag_FloatingX|UI_BoxFlag_FloatingY|UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, "View Item Origin Box %i", ItemIndex);
}
//- sixten: handle signals
{
ui_signal Signal = UI_SignalFromBox(ItemBox);
Data->Signal = Signal;
//- sixten: inspect pressed item
if(Signal.Clicked && Signal.DragDelta == V2R32(0, 0))
{
W_NavEditorInspectNode(Editor, Node);
}
//- sixten: handle dragging
if(Signal.Dragging)
{
if(Signal.Pressed)
{
UI_StoreDragV2(Item->P);
}
Item->P = UI_GetDragV2() + Signal.DragDelta/BoxDim*2;
}
}
//- sixten: apply the calculated position
v2_r32 OffsetP = BoxDim*(V2R32(1, 1) + Item->P)*0.5f;
ItemBox->FixedP = (OffsetP-OriginP);
}
ui_signal BackgroundSignal = UI_SignalFromBox(Box);
if(BackgroundSignal.Pressed)
{
W_NavEditorInspectNode(Editor, 0);
}
}
}
}