vn/code/vn_workspace_editor.cpp

378 lines
13 KiB
C++

//- sixten: Managing nodes
static workspace_editor_node *Workspace_GetNewEditorNode(workspace_view *View)
{
Assert(View->Type == W_View_Editor);
workspace_view_editor *Editor = (workspace_view_editor *)View->Data;
workspace_editor_node *Result = 0;
if(DLLIsEmpty(Editor->FirstFreeNode))
{
Result = PushStruct(View->Arena, workspace_editor_node);
}
else
{
Result = Editor->FirstFreeNode;
DLLRemove(Editor->FirstFreeNode, Editor->LastFreeNode, Result);
}
if(Result)
{
*Result = {};
DLLInsertLast(Editor->FirstNode, Editor->LastNode, Result);
}
return(Result);
}
//- sixten: Transformations
inline r32 Workspace_ViewToWorld(r32 Offset, r32 Scale, r32 Dim, r32 P)
{
r32 Result = (P - Dim*0.5)*(1.0/Scale) - Offset;
return(Result);
}
inline r32 Workspace_WorldToView(r32 Offset, r32 Scale, r32 Dim, r32 P)
{
r32 Result = (P + Offset)*Scale + Dim*0.5;
return(Result);
}
inline v2 Workspace_ViewToWorld(v2 Offset, r32 Scale, v2 Dim, v2 P)
{
v2 Result = (P - Dim*0.5)*(1.0/Scale) - Offset;
return(Result);
}
inline v2 Workspace_WorldToView(v2 Offset, r32 Scale, v2 Dim, v2 P)
{
v2 Result = (P + Offset)*Scale + Dim*0.5;
return(Result);
}
inline r32 Workspace_CalculateScaleFromZoomLevel(r32 ZoomLevel)
{
r32 PixelsPerUnit = 100.0;
r32 Scale = PixelsPerUnit*Pow(1.25, ZoomLevel);
return(Scale);
}
//- sixten: Commands
//- sixten: Builder code
static void Workspace_BuildEditorListerDropdown(workspace_editor_lister_dropdown *ListerDropdown)
{
b32 ActiveInDropdown = false;
UI_PushBackgroundColor(SetAlpha(Color_Black, 0.3));
UI_PushBorderColor(SetAlpha(Color_Black, 0.7));
r32 HeightTransition = AC_AnimateValueF(1, 0, 0.3, "Editor Lister Dropdown %p", ListerDropdown);
UI_SetNextSize(UI_Em(15, 1), UI_Em(25*HeightTransition, 1));
UI_SetNextCornerRadius(4.0);
UI_SetNextFixedP(ListerDropdown->P);
UI_SetNextLayoutAxis(Axis2_Y);
ui_box *Box = UI_MakeBox(UI_BoxFlag_DrawBackground |
UI_BoxFlag_FloatingX |
UI_BoxFlag_FloatingY |
UI_BoxFlag_Clickable |
UI_BoxFlag_Clip,
StrLit("Editor Lister Dropdown"));
UI_Parent(Box)
{
UI_BorderColor(SetAlpha(Color_Grey, 0.7))
UI_Row() UI_Column()
{
UI_Size(UI_TextContent(15, 1), UI_TextContent(12, 1)) UI_LabelF("Create new node");
// sixten: Build search bar
UI_SetNextWidth(UI_Percent(1, 1));
UI_SetNextHeight(UI_Em(2, 1));
UI_SetNextCornerRadius(4.0);
UI_SetNextLayoutAxis(Axis2_X);
UI_Parent(UI_MakeBox(UI_BoxFlag_DrawBorder|UI_BoxFlag_DrawBackground, StrLit("Editor Lister Field")))
{
UI_Spacer(UI_Em(0.8, 1));
UI_Width(UI_TextContent(1, 1)) UI_Font(Font_Icons) UI_LabelF("%U", FontIcon_Filter);
UI_Width(UI_Percent(1, 0))
{
UI_MakeBox(0, StrLit(""));
}
}
struct lister_option
{
char *Text;
u32 Icons;
};
lister_option Options[] =
{
{"Text", FontIcon_Pencil},
{"Branch", FontIcon_Menu},
};
for(s32 OptionIndex = 0;
OptionIndex < ArrayCount(Options);
++OptionIndex)
{
lister_option *Option = Options + OptionIndex;
UI_SetNextSize(UI_Percent(1, 1), UI_Em(2, 1));
UI_SetNextLayoutAxis(Axis2_X);
ui_box *ClickableBox = UI_MakeBoxF(UI_BoxFlag_Clickable, "Editor Lister %s", Option->Text);
ui_signal Signal = UI_SignalFromBox(ClickableBox);
if(AreEqual(UI_GetActive(), ClickableBox->Key))
{
ActiveInDropdown = true;
}
UI_Parent(ClickableBox)
UI_Height(UI_Percent(1, 1))
{
b32 IsHot = AreEqual(UI_GetHot(), ClickableBox->Key);
r32 TargetHot = IsHot*0.8;
UI_SetNextCornerRadius(3.0);
UI_SetNextSize(UI_Percent(1, 1), UI_Percent(1, 1));
UI_SetNextBackgroundColor(SetAlpha(Theme_HighlightBorderColor,
AC_AnimateValueF(TargetHot, TargetHot, 0.1, "Editor Lister %s%p",
Option->Text, ListerDropdown)));
UI_SetNextLayoutAxis(Axis2_X);
ui_box *HighlightBox = UI_MakeBox(UI_BoxFlag_DrawBackground, StrLit(""));
UI_Parent(HighlightBox)
UI_Padding(UI_Em(0.8, 1))
{
UI_Width(UI_TextContent(0, 1))
UI_Font(Font_Icons) UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", Option->Icons);
UI_Spacer(UI_Em(0.6, 1));
UI_Width(UI_TextContent(0, 1)) UI_MakeBoxF(UI_BoxFlag_DrawText, "%s", Option->Text);
if(Signal.Clicked)
{
// sixten(TODO): Issue the requested command.
ListerDropdown->Open = false;
}
}
}
}
}
ui_signal Signal = UI_SignalFromBox(Box);
if(AreEqual(UI_GetActive(), Box->Key))
{
ActiveInDropdown = true;
}
}
UI_PopBackgroundColor();
UI_PopBorderColor();
if(!ActiveInDropdown && !AreEqual(UI_GetActive(), UI_EmptyKey()))
{
ListerDropdown->Open = false;
}
}
static void Workspace_EditorDrawCallback(render_group *Group, glyph_atlas *Atlas, ui_box *Box, void *Data)
{
workspace_view_editor *Editor = (workspace_view_editor *)Data;
r32 Scale = Editor->Scale;
v4 LineColor = Theme_BorderColor;
v2 Dim = DimOfRange(Box->Rect);
s32 VerticalLineCount = Dim.x / Scale + 4;
for(s32 LineIndex = -VerticalLineCount/2;
LineIndex < VerticalLineCount/2;
++LineIndex)
{
r32 OffsetX = Workspace_WorldToView(Editor->Offset.x, Scale, Dim.x, LineIndex - (s32)Editor->Offset.x);
v2 Min = Box->Rect.Min + V2(OffsetX, 0);
v2 Max = Min + V2(1.5, Dim.y);
PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0);
}
s32 HorizontalLineCount = Dim.y / Scale + 4;
for(s32 LineIndex = -HorizontalLineCount/2;
LineIndex < HorizontalLineCount/2;
++LineIndex)
{
r32 OffsetY = Workspace_WorldToView(Editor->Offset.y, Scale, Dim.y, LineIndex - (s32)Editor->Offset.y);
v2 Min = Box->Rect.Min + V2(0, OffsetY);
v2 Max = Min + V2(Dim.x, 1.5);
PushQuad(Group, Range2R32(Min, Max), LineColor, 0, 1.2, 0);
}
}
static void Workspace_BuildEditor(workspace_view *View)
{
workspace *Workspace = W_GetState();
workspace_view_editor *Editor = (workspace_view_editor *)View->Data;
UI_SetNextWidth(UI_Percent(1, 1));
UI_SetNextHeight(UI_Percent(1, 1));
ui_box *EditorBox = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "Workspace Editor %p", View);
UI_EquipBoxCustomDrawCallback(EditorBox, Workspace_EditorDrawCallback, Editor);
r32 AnimatedZoomLevel = AC_AnimateValueF(Editor->ZoomLevel, 0, 0.25, "Workspace Editor Zoom");
Editor->Scale = Workspace_CalculateScaleFromZoomLevel(AnimatedZoomLevel);
v2 EditorDim = DimOfRange(EditorBox->Rect);
UI_Parent(EditorBox)
{
// sixten: Build the node boxes.
for(workspace_editor_node *Node = Editor->FirstNode;
Node != 0;
Node = Node->Next)
{
v2 ViewDim = V2(2, 1.5)*Editor->Scale;
v2 ViewP = Workspace_WorldToView(Editor->Offset, Editor->Scale, EditorDim, Node->P) - ViewDim*0.5;
UI_SetNextSize(UI_Pixels(ViewDim.x, 1), UI_Pixels(ViewDim.y, 1));
UI_SetNextFixedP(ViewP);
UI_SetNextLayoutAxis(Axis2_Y);
Node->Box = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
UI_BoxFlag_DrawBorder |
UI_BoxFlag_FloatingX |
UI_BoxFlag_FloatingY |
UI_BoxFlag_DrawDropShadow,
"Workspace Editor Node %p", Node);
UI_Parent(Node->Box)
{
UI_SetNextBackgroundColor(LinearBlend(Theme_BackgroundColor, Color_Black, 0.3));
UI_SetNextLayoutAxis(Axis2_X);
UI_SetNextSize(UI_Percent(1, 1), UI_Em(1.8, 1));
Node->TitleBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground |
UI_BoxFlag_DrawBorder |
UI_BoxFlag_Clickable,
"Workspace Editor Node Title");
UI_Parent(Node->TitleBox)
{
UI_Spacer(UI_Em(0.5, 1));
UI_Width(UI_TextContent(0, 1)) UI_Font(Font_Bold) UI_LabelF("Node");
UI_Spacer(UI_Percent(1, 0));
UI_SetNextSize(UI_Em(1.8, 1), UI_Percent(1, 1));
UI_SetNextFont(Font_Icons);
Node->CloseBox = UI_MakeBoxF(UI_BoxFlag_DrawText, "%U", FontIcon_Cancel);
}
}
}
if(Editor->ListerDropdown.Open)
{
Workspace_BuildEditorListerDropdown(&Editor->ListerDropdown);
}
}
// sixten: Get input from boxes.
{
workspace_editor_node *Next = 0;
for(workspace_editor_node *Node = Editor->FirstNode;
Node != 0;
Node = Next)
{
Next = Node->Next;
ui_signal Signal = UI_SignalFromBox(Node->TitleBox);
if(Signal.Dragging)
{
if(Signal.Pressed)
{
UI_StoreDragV2(Node->P);
}
v2 StartP = UI_GetDragV2();
v2 EndP = StartP + Signal.DragDelta*(1.0 / Editor->Scale);
Node->P = EndP;
}
if(Signal.Dragging || Signal.Hovering)
{
Platform.SetCursor(PlatformCursor_ArrowAll);
}
}
}
// sixten: Process panning and zooming of the editor.
ui_signal Signal = UI_SignalFromBox(EditorBox);
{
if(Signal.Dragging)
{
if(Signal.Pressed)
{
UI_StoreDragV2(Editor->Offset);
}
v2 StartOffset = UI_GetDragV2();
v2 EndOffset = StartOffset + Signal.DragDelta*(1.0 / Editor->Scale);
Editor->Offset = EndOffset;
// sixten: Update node positions, as to not get a one frame delay.
for(workspace_editor_node *Node = 0;
Node != 0;
Node = Node->Next)
{
v2 ViewDim = V2(2, 1.5)*Editor->Scale;
v2 ViewP = Workspace_WorldToView(Editor->Offset, Editor->Scale, EditorDim, Node->P) - ViewDim*0.5;
Node->Box->FixedP = ViewP;
}
}
}
for(platform_event *Event = Workspace->EventList->First;
Event != 0;
Event = Event->Next)
{
if(Event->Type == PlatformEvent_MouseScroll)
{
if(Signal.Dragging)
{
UI_StoreDragV2(Editor->Offset);
UI_UpdateDragStartP();
}
Editor->ZoomLevel = Clamp(Editor->ZoomLevel + Event->Scroll.y, -4, 5);
}
}
// sixten: Process shortcuts.
if(Platform_KeyPress(Workspace->EventList, Key_Space, PlatformModifier_Ctrl))
{
workspace_editor_lister_dropdown *ListerDropdown = &Editor->ListerDropdown;
if(ListerDropdown->Open)
{
ListerDropdown->Open = false;
}
else if(InRange(EditorBox->Rect, UI_GetState()->MouseP))
{
ListerDropdown->Open = true;
ListerDropdown->P = UI_GetState()->MouseP - EditorBox->Rect.Min;
}
}
}