//- sixten: Managing nodes static workspace_editor_node *Workspace_GetNewEditorNode(workspace_view *View) { Assert(View->Type == Workspace_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_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); PushQuad(Group, Box->Rect.Min + V2(OffsetX, 0), V2(1.5, Dim.y), 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); PushQuad(Group, Box->Rect.Min + V2(0, OffsetY), V2(Dim.x, 1.5), LineColor, 0, 1.2, 0); } } static void Workspace_BuildEditor(workspace *Workspace, workspace_view *View) { workspace_view_editor *Editor = (workspace_view_editor *)View->Data; // sixten(TODO): These should be able to have a strictness of 1, but they can't. Fix that. UI_SetNextWidth(UI_Percent(1, 0)); UI_SetNextHeight(UI_Percent(1, 0)); ui_box *EditorBox = UI_MakeBoxF(UI_BoxFlag_Clip|UI_BoxFlag_Clickable, "Workspace Editor %p", View); EditorBox->DrawCallback = Workspace_EditorDrawCallback; EditorBox->DrawCallbackData = Editor; r32 AnimatedZoomLevel = AnimationCurve_AnimateValueF(Editor->ZoomLevel, 0, 0.25, "Workspace Editor Zoom"); Editor->Scale = Workspace_CalculateScaleFromZoomLevel(AnimatedZoomLevel); v2 EditorDim = DimOfRange(EditorBox->Rect); // 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_SetNextSize(UI_Percent(1, 1), UI_Pixels(20, 1)); Node->TitleBox = UI_MakeBoxF(UI_BoxFlag_DrawBackground | UI_BoxFlag_DrawBorder | UI_BoxFlag_Clickable, "Workspace Editor Node Title"); UI_Parent(Node->TitleBox) { UI_Width(UI_TextContent(7, 0)) UI_Font(Font_Bold) UI_LabelF("Node"); } } } // 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 the movement of the node. { } // 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_GetNewEditorNode(View); } }