From fc8040d695644aaca4596adebeca4ea1369ef630 Mon Sep 17 00:00:00 2001 From: Fox Caminiti Date: Fri, 22 Jul 2022 20:45:08 -0400 Subject: first --- my_imgui_widgets.cpp | 1081 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1081 insertions(+) create mode 100644 my_imgui_widgets.cpp (limited to 'my_imgui_widgets.cpp') diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp new file mode 100644 index 0000000..5a0ab55 --- /dev/null +++ b/my_imgui_widgets.cpp @@ -0,0 +1,1081 @@ +#include "imgui/imgui.h" +#include "imgui_ops.h" + +// 0 for timeline keyframe, 1 for graph keyframe, 2 for left graph handle, 3 for right graph handle +internal void +ImGui_KeyframeDragging(project_data *File, project_state *State, ui *UI, property_channel *Property, int32 b, ImGuiIO io, int16 Type) +{ + keyframe *Keyframe = KeyframeLookupMemory(Property, b); + if (ImGui::IsItemActive()) { + + if (!Keyframe->IsSelected && ImGui::IsItemActivated()) + { + if (!io.KeyShift) { + temp_keyframe_list Bad = GetSelectedKeyframes(File); + for (int i = 0; i < Bad.Amount; i++) + Bad.SelectedKeyframe[i]->IsSelected = false; + } + Keyframe->IsSelected = true; + State->RecentSelectionType = selection_keyframe; + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); + if (Type == 0 || Type == 1) + { + UI->DraggingKeyframeThreshold += io.MouseDelta.x; + if (abs(UI->DraggingKeyframeThreshold) >= UI->TimelineZoom) { + int16 Increment = UI->DraggingKeyframeThreshold/UI->TimelineZoom; + // temp_keyframe_list Bad = GetSelectedKeyframes(File); + // for (int b = 0; b < Bad.Amount; b++) { + // keyframe *SelectedKeyframe = Bad.SelectedKeyframe[b]; + if (!(Keyframe->FrameNumber == 0 && Increment == -1)) { + Keyframe->FrameNumber += Increment; + CheckKeyframeSort(Property, Increment, b); + // SortAndCacheKeyframeAtFrame(SelectedKeyframe->FrameNumber, &File.LayerPTR[i]->Property[a], &Cache); + ClampSurroundingKeyframeHandles(Property, b); + } + // } + UI->DraggingKeyframeThreshold += -1*Increment*UI->TimelineZoom; + State->UpdateFrame = true; + State->UpdateKeyframes = true; + // Cache.Frame[File.CurrentFrame].Cached = false; + } + } + if (Type != 0) + { + if (Type == 1) + { + real32 IncrementsPerPixel = (Property->LocalMaxVal.f - Property->LocalMinVal.f)/Property->GraphLength; + Keyframe->Value.f -= io.MouseDelta.y*IncrementsPerPixel; + CalculatePropertyMinMax(Property); + } + if (Type == 2) + { + Keyframe->TangentLeft.x += io.MouseDelta.x/UI->TimelineZoom; + Keyframe->TangentLeft.y -= io.MouseDelta.y; + ClampKeyframeHandles(Property, b, 0); + } + if (Type == 3) + { + Keyframe->TangentRight.x += io.MouseDelta.x/UI->TimelineZoom; + Keyframe->TangentRight.y -= io.MouseDelta.y; + ClampKeyframeHandles(Property, b, 1); + } + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } + } + } +} + +internal void +ImGui_PropertiesPanel(project_data *File, project_state *State, ui *UI, memory *Memory) +{ + if (State->MostRecentlySelectedLayer > -1) { + project_layer *Layer = File->Layer[State->MostRecentlySelectedLayer]; + char buf[256]; + sprintf(buf, "Properties: %s###Properties", Layer->Name); + ImGui::Begin(buf); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_properties; + ImGui::Text("Transform"); + for (int h = 0; h < AmountOf(Layer->Property); h++) { + property_channel *Property = &Layer->Property[h]; + ImGui::PushID(Property); + if (ImGui::Button("K")) + ManualKeyframeInsertF(Property, Memory, File->CurrentFrame, Property->CurrentValue.f); + ImGui::SameLine(); + if (ImGui::DragScalar(Property->Name, ImGuiDataType_Float, &Property->CurrentValue.f, + Property->ScrubVal.f, &Property->MinVal.f, &Property->MaxVal.f, "%f")) + { + State->UpdateFrame = true; + } + ImGui::PopID(); + } + for (int h = 0; h < Layer->NumberOfEffects; h++) { + effect *Effect = Layer->Effect[h]; + ImGui::Button("V"); ImGui::SameLine(); + ImGui::Text(Effect->Name); + for (int i = 0; i < Effect->NumberOfProperties; i++) { + property_channel *Property = &Effect->Property[i]; + ImGui::PushID(Property); + if (Property->VarType == type_real) + ImGui::DragScalar(Property->Name, ImGuiDataType_Float, &Property->CurrentValue.f, 0.005f, &Property->MaxVal.f, &Property->MaxVal.f, "%f"); + if (Property->VarType == type_color) + if (ImGui::ColorEdit4("color 1", &Property->CurrentValue.f, ImGuiColorEditFlags_Float)) + State->UpdateFrame = true; + if (Property->VarType == type_blendmode) + { + uint32 *item_current_idx = (uint32 *)&Property->CurrentValue.blendmode; // Here we store our selection data as an index. + if (ImGui::BeginListBox("Blend mode")) + { + for (int n = 0; n < IM_ARRAYSIZE(BlendmodeNames); n++) + { + const bool is_selected = (*item_current_idx == n); + if (ImGui::Selectable(BlendmodeNames[n], is_selected)) { + *item_current_idx = n; + State->UpdateFrame = true; + } + + // Set the initial focus when opening the combo (scrolling + keyboard navigation focus) + if (is_selected) + ImGui::SetItemDefaultFocus(); + } + ImGui::EndListBox(); + } + } + ImGui::PopID(); + } + } + } else { + char buf[256]; + sprintf(buf, "Properties: empty###Properties"); + ImGui::Begin(buf); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_properties; + } + ImGui::End(); +} + +internal v2 +CalculateAnchorPointUV(project_layer *Layer, pixel_buffer *Buffer); + +internal void +ImGui_Viewport(project_data File, project_state *State, ui *UI, pixel_buffer CompBuffer, + ImGuiIO io, GLuint textureID) +{ + ImGui::Begin("Viewport"); + + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_viewport; + + // Primarily taken from the Custom Rendering section of the demo + ImVec2 ViewportMin = ImGui::GetCursorScreenPos(); + ImVec2 ViewportScale = ImGui::GetContentRegionAvail(); + ViewportScale.y -= ImGui::GetFontSize(); + if (ViewportScale.x < 50.0f) ViewportScale.x = 50.0f; + if (ViewportScale.y < 50.0f) ViewportScale.y = 50.0f; + ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y); + + if (UI->Initializing) { + UI->CompZoom = ImVec2(CompBuffer.Width, CompBuffer.Height); + UI->CompPos = ImVec2(ViewportMin.x + ((ViewportMax.x - ViewportMin.x)/2 - UI->CompZoom.x/2), + ViewportMin.y + ((ViewportMax.y - ViewportMin.y)/2 - UI->CompZoom.y/2)); + } + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(ViewportMin, ViewportMax, IM_COL32(50, 50, 50, 255)); + draw_list->AddRect(ViewportMin, ViewportMax, IM_COL32(255, 255, 255, 255)); + + ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + bool32 IsHovered = ImGui::IsItemHovered(); + bool32 IsActive = ImGui::IsItemActive(); + bool32 IsActivated = ImGui::IsItemActivated(); + + + if (IsHovered && IsActivated && ImGui::IsMouseDown(ImGuiMouseButton_Left)) { + v2 LocalMousePos = (V2(io.MousePos) - V2(ViewportMin)); + v2 LocalCompPos = V2(UI->CompPos) - V2(ViewportMin); + v2 MouseScreenUV = LocalMousePos - LocalCompPos; + UI->TempZoomRatio = MouseScreenUV / V2(UI->CompZoom); // AKA actual normalized UV of comp + if (!ImGui::IsKeyDown(ImGuiKey_Z)) { + for (int i = File.NumberOfLayers - 1; i >= 0; i--) { + if (!io.KeyShift) DeselectAllLayers(&File, State); + if (TestPointInLayer(File.Layer[i], &CompBuffer, UI->TempZoomRatio) && !File.Layer[i]->IsSelected) + { + SelectLayer(File.Layer[i], State, i); + break; + } + } + } + } + + if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1.0f)) + { + UI->CompPos.x += io.MouseDelta.x; + UI->CompPos.y += io.MouseDelta.y; + } + if (IsActive && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1.0f) && ImGui::IsKeyDown(ImGuiKey_Z)) + { + real32 Distance = io.MouseDelta.x + io.MouseDelta.y; + UI->CompZoom.x += (Distance)*(real32)CompBuffer.Width/CompBuffer.Height; + UI->CompZoom.y += (Distance); + UI->CompPos.x -= ((Distance)*(real32)CompBuffer.Width/CompBuffer.Height)*UI->TempZoomRatio.x; + UI->CompPos.y -= Distance*UI->TempZoomRatio.y; + } + + draw_list->PushClipRect(ViewportMin, ViewportMax, true); + draw_list->AddImage((void *)(intptr_t)textureID, ImVec2(UI->CompPos.x, UI->CompPos.y), + ImVec2(UI->CompPos.x + UI->CompZoom.x, UI->CompPos.y + UI->CompZoom.y)); + + if (State->MostRecentlySelectedLayer > -1) { + project_layer *Layer = File.Layer[State->MostRecentlySelectedLayer]; + ImVec2 AUV = ImVec2(Layer->x.CurrentValue.f / CompBuffer.Width, Layer->y.CurrentValue.f / CompBuffer.Height); + ImVec2 ScreenAP = ImVec2(UI->CompPos.x + AUV.x * UI->CompZoom.x, UI->CompPos.y + AUV.y * UI->CompZoom.y); + draw_list->AddNgon(ScreenAP, 20, ImGui::GetColorU32(ImGuiCol_ScrollbarGrab), 8, 10.0f); + } + + draw_list->PopClipRect(); + + ImGui::Text("%.1f", 100.0f * (UI->CompZoom.x / CompBuffer.Width)); + if (State->MsgTime > 0) { + ImGui::SameLine(); + ImGui::SetCursorPosX((ViewportScale.x / 5)*4); + ImGui::Text(State->Msg); + State->MsgTime--; + } + + ImGui::End(); +} + +// 1 for left, 2 for right, 3 for both +internal bool32 +ImGui_SlidingLayer(project_layer *Layer, real32 *DraggingThreshold, real32 Delta, int16 TimelineZoom, int16 Side) +{ + bool32 Result = 0; + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + *DraggingThreshold += Delta; + if (abs(*DraggingThreshold) >= TimelineZoom) { + int16 Increment = *DraggingThreshold/TimelineZoom; + + // TODO(fox): Properly handle the start and end points wrapping. + + if (!(Increment < 0 && Layer->StartFrame == 0 && Side & 1)) + { + if (Side & 1) + Layer->StartFrame += Increment; + if (Side & 2) + Layer->EndFrame += Increment; + if (Side == 3) { + IncrementKeyframesInLayer(Layer, Increment); + if (Layer->SourceType == source_video) { + video_source *Source = (video_source *)Layer->RenderInfo; + Source->VideoFrameOffset += Increment; + } + } + } + *DraggingThreshold += -1*Increment*TimelineZoom; + } + Result = 1; + } + return Result; +} + +internal void +AddSource(project_data *File, memory *Memory, char * = NULL); + +internal void +ImGui_File(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io) +{ + ImGui::Begin("Files"); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + if (ImGui::Button("Add source")) { + AddSource(File, Memory); + } + for (int16 i = 0; i < File->NumberOfSources; i++) { + ImGui::PushID(i); + ImGui::InputText("##source", File->Source[i], STRING_SIZE); + ImGui::SameLine(); + if (ImGui::Button("Create Layer")) { + CreateLayerFromSource(File, State, Memory, File->Source[i]); + } + ImGui::PopID(); + } +#if DEBUG + for (int i = 0; i < Debug.WatchedProperties; i++) { + if (Debug.DebugPropertyType[i] == d_float) { + ImGui::Text("%s: %f", Debug.String[i], Debug.Val[i].f); + } else if (Debug.DebugPropertyType[i] == d_int) { + ImGui::Text("%s: %i", Debug.String[i], Debug.Val[i].i); + } else if (Debug.DebugPropertyType[i] == d_uint) { + ImGui::Text("%s: %u", Debug.String[i], Debug.Val[i].u); + } + } +#endif + ImGui::End(); +} + + +internal void +ImGui_Timeline(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io) +{ + ImVec2 FramePadding = ImGui::GetStyle().FramePadding; + ImVec2 ItemSpacing = ImGui::GetStyle().ItemSpacing; + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); // makes setting up the layout easier + ImGui::Begin("Timeline", NULL); + + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_timeline; + + real32 FontHeight = ImGui::GetFontSize(); + + ImVec2 WindowSize = ImGui::GetWindowSize(); + if (WindowSize.x < 50.0f) WindowSize.x = 50.0f; // prevent crashing if the window gets too small + if (WindowSize.y < 50.0f) WindowSize.y = 50.0f; // (still crashes) + + ImVec2 WindowMinAbs = ImGui::GetWindowPos(); + ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize; + + ImVec2 ButtonSize = ImVec2(FontHeight*2, FontHeight*2); + + real32 TopbarHeight = FontHeight*4; + ImVec2 TopbarMax = ImVec2(WindowMaxAbs.x, WindowMinAbs.y + TopbarHeight); + + ImVec2 TimelineBorderPadding = ImVec2(FontHeight, FontHeight); + + ImVec2 TopbarSize = ImVec2(WindowSize.x, TopbarHeight); + ImVec2 TopbarButtonSize = ImVec2(TopbarHeight, TopbarHeight); + + // NOTE(fox): StartingPos values include X and Y scroll, primarily used for + // the keyframes/layers. Absolute doesn't include scroll, primarily used + // for the clip rects. + + ImVec2 SidebarSize = ImVec2(UI->TimelineSplit, WindowSize.y - TopbarHeight); + ImVec2 SidebarSizeWithBorder = SidebarSize - TimelineBorderPadding*2; + ImVec2 SidebarAbsolutePos = WindowMinAbs + ImVec2(0, TopbarSize.y) + TimelineBorderPadding; + ImVec2 SidebarStartingPos = SidebarAbsolutePos + ImVec2(0, UI->ScrollYOffset); + + ImVec2 TimelineSize = ImVec2(WindowSize.x - SidebarSize.x, SidebarSize.y); + ImVec2 TimelineSizeWithBorder = TimelineSize - TimelineBorderPadding*2; + ImVec2 TimelineAbsolutePos = WindowMinAbs + ImVec2(SidebarSize.x, TopbarSize.y) + TimelineBorderPadding; + ImVec2 TimelineStartingPos = SidebarStartingPos + ImVec2(SidebarSize.x + UI->ScrollXOffset, 0); + + // Timeline and sidebar size including the padding between them + ImVec2 TimelineFullSize = TimelineSizeWithBorder + SidebarSizeWithBorder + ImVec2(TimelineBorderPadding.x*2, 0); + + ImVec2 KeyframeSize = ImVec2(FontHeight, FontHeight); + + ImVec2 PlayheadPos = ImVec2(TimelineStartingPos.x + UI->TimelineZoom * File->CurrentFrame, WindowMinAbs.y + TopbarSize.y/2); + + // NOTE(fox): The InvisibleButton hitbox that handles mouse inputs on the + // graph occludes the hitbox that handles box drag selection, so I'm using + // this struct to carry over the state from the former to the latter. + imgui_buttonstate AnimationCurves = {}; + + + if (UI->Initializing) { + UI->TimelineZoom = TimelineSizeWithBorder.x / (File->NumberOfFrames + 1); + } + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(WindowMinAbs, WindowMaxAbs, + IM_COL32(255, 255, 255, 50)); + draw_list->AddRectFilled(WindowMinAbs, TopbarMax, + IM_COL32(255, 255, 255, 50)); + + + // + + + ImGui::BeginChild("Topbar", TopbarSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); + ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); + ImGui::Button("V", TopbarButtonSize); ImGui::SameLine(); + + ImGui::SetCursorScreenPos(PlayheadPos); + ImGui::Button("P", ButtonSize); + if (ImGui::IsItemActive()) { + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->DraggingKeyframeThreshold += io.MouseDelta.x; + if (abs(UI->DraggingKeyframeThreshold) >= UI->TimelineZoom) { + int16 Increment = UI->DraggingKeyframeThreshold/UI->TimelineZoom; + if (File->CurrentFrame <= 0 && Increment < File->StartFrame) + File->CurrentFrame = 0; + else if (File->CurrentFrame >= File->EndFrame && Increment > File->EndFrame) { + File->CurrentFrame = File->EndFrame; + } else { + File->CurrentFrame += Increment; + } + State->UpdateFrame = true; + State->UpdateKeyframes = true; + UI->DraggingKeyframeThreshold += -1*Increment*UI->TimelineZoom; + } + } + } + + ImGui::EndChild(); + + /// + + ImGui::BeginChild("Sidebar", SidebarSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + + ImGui::SetCursorScreenPos(SidebarStartingPos); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ItemSpacing); + + ImGui::PushClipRect(SidebarAbsolutePos, SidebarAbsolutePos + SidebarSizeWithBorder, true); + + for (int i = File->NumberOfLayers - 1; i >= 0; i--) + { + project_layer *Layer = File->Layer[i]; + ImGui::PushID(i); + + ImGui::SetCursorScreenPos(ImVec2(SidebarStartingPos.x, ImGui::GetCursorScreenPos().y)); + + draw_list->PushClipRect(SidebarAbsolutePos, SidebarAbsolutePos + TimelineFullSize, true); + if (Layer->IsSelected) { + real32 Y = ImGui::GetCursorScreenPos().y; + draw_list->AddRectFilled(ImVec2(SidebarAbsolutePos.x, Y), + ImVec2(TimelineAbsolutePos.x + TimelineSize.x, Y + FontHeight + FramePadding.y*2), + IM_COL32(255, 255, 255, 50)); + } + draw_list->PopClipRect(); + + ImGui::Button("V"); ImGui::SameLine(); + ImGui::Button("I"); ImGui::SameLine(); + ImGui::Text(Layer->Name); ImGui::SameLine(); + + ImGui::SetCursorScreenPos(ImVec2(SidebarStartingPos.x, ImGui::GetCursorScreenPos().y)); + ImGui::Button("##mover", ImVec2(SidebarSizeWithBorder.x, FontHeight + FramePadding.y*2)); + + // Layer dragging interaction + + if (ImGui::IsItemActive()) { + if (ImGui::IsItemActivated() && !Layer->IsSelected) + { + if (!io.KeyShift) DeselectAllLayers(File, State); + SelectLayer(Layer, State, i); + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1) ) + { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + UI->DraggingLayerThreshold -= io.MouseDelta.y; + real32 Threshold = FontHeight + FramePadding.y*2; + if (abs(UI->DraggingLayerThreshold) >= Threshold) + { + int16 Increment = UI->DraggingLayerThreshold/abs(UI->DraggingLayerThreshold); + MoveLayersByIncrement(File, State, Increment); + UI->DraggingLayerThreshold += -1*Increment*Threshold; + State->UpdateFrame = true; + // Cache.Frame[File->CurrentFrame].Cached = false; + } + } + } + + // Properties gap + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y * UI->KeyframeSpacing)); + for (int a = 0; a < AmountOf(Layer->Property); a++) { + if (Layer->Property[a].IsToggled) + { + property_channel *Property = &Layer->Property[a]; + ImGui::PushID(Property); + // if (Property->IsSelected) { + // real32 Y = ImGui::GetCursorScreenPos().y; + // draw_list->AddRectFilled(ImVec2(SidebarAbsolutePos.x, Y), + // ImVec2(TimelineAbsolutePos.x, Y + FontHeight + FramePadding.y*2), + // IM_COL32(100, 0, 255, 50)); + // } + ImGui::SetCursorScreenPos(ImVec2(SidebarStartingPos.x, ImGui::GetCursorScreenPos().y)); + ImGui::Text(Property->Name); + real32 YInit = ImGui::GetCursorScreenPos().y; + ImGui::SameLine(); + if (ImGui::Button("K")) + ManualKeyframeInsertF(Property, Memory, File->CurrentFrame, Property->CurrentValue.f); + ImGui::SameLine(); + if (ImGui::Button("G")) { + SwitchBool(Property->IsGraphToggled); + // TODO(fox): Make system to init things like these automatically? + if (!Property->GraphLength) { + Property->GraphLength = 150; + Property->GraphYOffset = (Property->GraphWindowHeight - Property->GraphLength)/2; + } + } + ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, YInit)); + if (Property->IsGraphToggled) + { + ImGui::Dummy(ImVec2(5, Property->GraphWindowHeight)); + } + ImGui::PopID(); + } + } + ImGui::PopStyleVar(); + + ImGui::PopID(); + } + + ImGui::PopClipRect(); + + /// Split size adjuster + + ImGui::SetCursorScreenPos(ImVec2(WindowMinAbs.x + UI->TimelineSplit - TimelineBorderPadding.x, TimelineAbsolutePos.y)); + ImGui::InvisibleButton("##SplitMove", ImVec2(TimelineBorderPadding.x, SidebarSizeWithBorder.y), ImGuiButtonFlags_MouseButtonLeft); + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelineSplit += io.MouseDelta.x; + } + + + ImGui::PopStyleVar(); + + ImGui::EndChild(); + ImGui::SameLine(); + + /// + + ImGui::BeginChild("Timeline", TimelineSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y)); + + ImGui::SetCursorScreenPos(TimelineStartingPos); + + ImGui::PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); + draw_list->PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); + + for (int i = File->NumberOfLayers - 1; i >= 0; i--) + { + // The actual layer bars + + project_layer *Layer = File->Layer[i]; + ImGui::PushID(i); + uint16 LayerTLSpan = Layer->EndFrame - Layer->StartFrame; + + // if (Layer->SourceType == video) { + // video_source *Source = (video_source *)Layer->RenderInfo; + // real32 XMin = TimelineMinX + UI->TimelineZoom*Source->VideoFrameOffset; + // // real32 YMin = StartingCursorPosAbs.y + (FontHeight + FramePadding.y*2 + ItemSpacing.y)*i; + // real32 YMin = ImGui::GetCursorScreenPos().y; + // draw_list->AddRect(ImVec2(WindowMin.x, YMin), + // ImVec2(WindowMaxAbs.x, YMin + FontHeight + FramePadding.y*2), + // IM_COL32(255, 255, 255, 50), 2); + // } + + ImGui::SetCursorScreenPos(ImVec2(TimelineStartingPos.x + UI->TimelineZoom*Layer->StartFrame, ImGui::GetCursorScreenPos().y)); + ImGui::Button("##leftbound", ImVec2(0.5 * UI->TimelineZoom, 0)); ImGui::SameLine(); + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + ImGui_SlidingLayer(Layer, &UI->DraggingKeyframeThreshold, io.MouseDelta.x, UI->TimelineZoom, 1); + + ImGui::Button("##layer", ImVec2((LayerTLSpan * UI->TimelineZoom), 0)); ImGui::SameLine(); + if (ImGui::IsItemClicked()) { + if (!io.KeyShift) DeselectAllLayers(File, State); + SelectLayer(Layer, State, i); + } + if (ImGui_SlidingLayer(Layer, &UI->DraggingLayerThreshold, io.MouseDelta.x, UI->TimelineZoom, 3)) { + // TODO(fox): This will be removed once video caching is implemented. + UI->TemporaryUpdateOverride = true; + } + + ImGui::Button("##rightbound", ImVec2(0.5 * UI->TimelineZoom, 0)); + + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + ImGui_SlidingLayer(Layer, &UI->DraggingKeyframeThreshold, io.MouseDelta.x, UI->TimelineZoom, 2); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y * UI->KeyframeSpacing)); + ImGui::SetCursorPosY(ImGui::GetCursorPos().y + (ItemSpacing.y * UI->KeyframeSpacing / 2)); + + for (int a = 0; a < AmountOf(Layer->Property); a++) { + if (Layer->Property[a].IsToggled) + { + real32 InitialY = ImGui::GetCursorScreenPos().y; + ImGui::NewLine(); + real32 NextY = ImGui::GetCursorScreenPos().y; + + property_channel *Property = &Layer->Property[a]; + ImGui::PushID(Property); + + for (int b = 0; b < Layer->Property[a].NumberOfTotalKeyframes; b++) { + keyframe *Keyframe = KeyframeLookupMemory(Property, b); + real32 KeyframeOrigin = TimelineStartingPos.x + UI->TimelineZoom*Keyframe->FrameNumber; + ImVec2 KeyframePosition = ImVec2(KeyframeOrigin - FontHeight/2, InitialY); + + ImGui::PushID(Keyframe); + + ImGui::SetCursorScreenPos(KeyframePosition); + + // sadly ImGui::Selectable doesn't work here + if (Keyframe->IsSelected) + ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetColorU32(ImGuiCol_ButtonHovered)); + + ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); + ImGui::SameLine(); + + if (Keyframe->IsSelected) + ImGui::PopStyleColor(); + + if (UI->BoxSelectActive && UI->BoxStart.y < NextY) { + if (IsRectTouching(UI->BoxStart, UI->BoxEnd, KeyframePosition, KeyframePosition + KeyframeSize)) { + SelectKeyframe(File, Layer, Property, Keyframe); + State->RecentSelectionType = selection_keyframe; + } else if (!io.KeyShift) { + Keyframe->IsSelected = false; + } + } + + ImGui_KeyframeDragging(File, State, UI, Property, b, io, 0); + + + ImGui::PopID(); + } + + ImGui::SetCursorScreenPos(ImVec2(ImGui::GetCursorScreenPos().x, NextY)); + + if (Property->IsGraphToggled) + { + uint16 GraphWindowHeight = File->Layer[i]->Property[a].GraphWindowHeight; + real32 GraphWindowLocalYMin = ImGui::GetCursorPosY(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + real32 ScreenY = NextY; + ImVec2 MinPos = ImVec2(TimelineAbsolutePos.x, ScreenY); + ImVec2 MaxPos = ImVec2(TimelineAbsolutePos.x + TimelineSizeWithBorder.x, ScreenY + GraphWindowHeight); + draw_list->AddRectFilled(MinPos, MaxPos, + IM_COL32(00, 00, 30, 65)); + + draw_list->PushClipRect(MinPos, MaxPos, true); + + ImVec2 LeftPos[2]; + ImVec2 MidPos[2]; + ImVec2 RightPos[2]; + ImU32 col = ImGui::GetColorU32(ImGuiCol_ScrollbarGrab); + + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { + keyframe *Keyframe = KeyframeLookupMemory(Property, b); + // int32 Index = KeyframeMemoryToIndex(Property, b); + + ImGui::PushID(Keyframe); + + real32 MinVal = Property->LocalMinVal.f; + real32 MaxVal = Property->LocalMaxVal.f; + + // Normalized ratio between the smallest and largest value + real32 HandleYRatio = (Keyframe->Value.f - MaxVal) / (MaxVal - MinVal); + real32 HandleYRatio_L = (Keyframe->Value.f + Keyframe->TangentLeft.y - MaxVal) / (MaxVal - MinVal); + real32 HandleYRatio_R = (Keyframe->Value.f + Keyframe->TangentRight.y - MaxVal) / (MaxVal - MinVal); + + real32 LocalHandlePosX = UI->TimelineZoom*Keyframe->FrameNumber; + real32 LocalHandlePosX_L = LocalHandlePosX + UI->TimelineZoom*Keyframe->TangentLeft.x; + real32 LocalHandlePosX_R = LocalHandlePosX + UI->TimelineZoom*Keyframe->TangentRight.x; + + real32 HandlePosX = TimelineStartingPos.x + LocalHandlePosX - FontHeight*0.5; + real32 HandlePosX_L = TimelineStartingPos.x + LocalHandlePosX_L - FontHeight*0.5; + real32 HandlePosX_R = TimelineStartingPos.x + LocalHandlePosX_R - FontHeight*0.5; + + real32 LocalHandlePosY = HandleYRatio * Property->GraphLength; + real32 LocalHandlePosY_L = HandleYRatio_L * Property->GraphLength; + real32 LocalHandlePosY_R = HandleYRatio_R * Property->GraphLength; + + real32 HandlePosY = MinPos.y - LocalHandlePosY + Property->GraphYOffset; + real32 HandlePosY_L = MinPos.y - LocalHandlePosY_L + Property->GraphYOffset; + real32 HandlePosY_R = MinPos.y - LocalHandlePosY_R + Property->GraphYOffset; + + ImVec2 HandlePos = ImVec2(HandlePosX, HandlePosY); + ImVec2 HandlePos_L = ImVec2(HandlePosX_L, HandlePosY_L); + ImVec2 HandlePos_R = ImVec2(HandlePosX_R, HandlePosY_R); + + if (UI->BoxSelectActive && UI->BoxStart.y >= NextY) { + if (IsRectTouching(UI->BoxStart, UI->BoxEnd, HandlePos, HandlePos + KeyframeSize)) { + Keyframe->IsSelected = true; + State->RecentSelectionType = selection_keyframe; + } else if (!io.KeyShift) { + Keyframe->IsSelected = false; + } + } + + ImGui::PushStyleColor(ImGuiCol_Button, col); + + ImGui::SetCursorScreenPos(ImVec2(HandlePosX - FontHeight*1.5, HandlePosY - FontHeight*1.5)); + ImGui::Text("%.02f", Keyframe->Value.f); + + ImGui::SetCursorScreenPos(ImVec2(HandlePosX - FontHeight*1.0, HandlePosY - FontHeight*1.0)); + ImGui::InvisibleButton("##keyframepoint", ImVec2(FontHeight*2, FontHeight*2), ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + + draw_list->AddRect(ImVec2(HandlePosX - FontHeight*0.5, HandlePosY - FontHeight*0.5), + ImVec2(HandlePosX + FontHeight*0.5, HandlePosY + FontHeight*0.5), + ImGui::GetColorU32(ImGuiCol_ButtonHovered)); + + ImGui_KeyframeDragging(File, State, UI, Property, b, io, 1); + + if (Keyframe->IsSelected && Keyframe->Type == bezier) { + + ImGui::SetCursorScreenPos(ImVec2(HandlePosX_L, HandlePosY_L)); + draw_list->AddCircle(ImVec2(HandlePosX_L, HandlePosY_L), 2, col, 16, 1); + ImGui::Button("##keyframehandleleft", ImVec2(FontHeight, FontHeight)); + + ImGui_KeyframeDragging(File, State, UI, Property, b, io, 2); + + ImGui::SetCursorScreenPos(ImVec2(HandlePosX_R, HandlePosY_R)); + ImGui::Button("##keyframehandleright", ImVec2(FontHeight, FontHeight)); + + ImGui_KeyframeDragging(File, State, UI, Property, b, io, 3); + + draw_list->AddLine(MidPos[b & 1], RightPos[b & 1], col, 1.0f); + draw_list->AddLine(MidPos[b & 1], LeftPos[b & 1], col, 1.0f); + } + + ImGui::PopStyleColor(); + + ImGui::PopID(); + } + + // TODO(fox): Reformat this so it's all done in one loop. + + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { + keyframe *Keyframe = KeyframeLookupIndex(Property, b); + + real32 MinVal = Property->LocalMinVal.f; + real32 MaxVal = Property->LocalMaxVal.f; + + real32 HandleYRatio = (Keyframe->Value.f - MaxVal) / (MaxVal - MinVal); + real32 HandleYRatio_L = (Keyframe->Value.f + Keyframe->TangentLeft.y - MaxVal) / (MaxVal - MinVal); + real32 HandleYRatio_R = (Keyframe->Value.f + Keyframe->TangentRight.y - MaxVal) / (MaxVal - MinVal); + + real32 LocalHandlePosX = UI->TimelineZoom*Keyframe->FrameNumber; + real32 LocalHandlePosX_L = LocalHandlePosX + UI->TimelineZoom*Keyframe->TangentLeft.x; + real32 LocalHandlePosX_R = LocalHandlePosX + UI->TimelineZoom*Keyframe->TangentRight.x; + + real32 HandlePosX = TimelineStartingPos.x + LocalHandlePosX - FontHeight*0.5; + real32 HandlePosX_L = TimelineStartingPos.x + LocalHandlePosX_L - FontHeight*0.5; + real32 HandlePosX_R = TimelineStartingPos.x + LocalHandlePosX_R - FontHeight*0.5; + + real32 LocalHandlePosY = HandleYRatio * Property->GraphLength; + real32 LocalHandlePosY_L = HandleYRatio_L * Property->GraphLength; + real32 LocalHandlePosY_R = HandleYRatio_R * Property->GraphLength; + + real32 HandlePosY = MinPos.y - LocalHandlePosY + Property->GraphYOffset; + real32 HandlePosY_L = MinPos.y - LocalHandlePosY_L + Property->GraphYOffset; + real32 HandlePosY_R = MinPos.y - LocalHandlePosY_R + Property->GraphYOffset; + + ImVec2 HandlePos = ImVec2(HandlePosX, HandlePosY); + ImVec2 HandlePos_L = ImVec2(HandlePosX_L, HandlePosY_L); + ImVec2 HandlePos_R = ImVec2(HandlePosX_R, HandlePosY_R); + + MidPos[b & 1] = HandlePos; + LeftPos[b & 1] = HandlePos_L; + RightPos[b & 1] = HandlePos_R; + + if (b != 0) + { + if (b & 1) { + if (Keyframe->Type == linear) + draw_list->AddLine(MidPos[0], MidPos[1], col, 1.0f); + else if (Keyframe->Type == bezier) + draw_list->AddBezierCubic(MidPos[0], RightPos[0], LeftPos[1], MidPos[1], col, 1.0f, 8); + } else { + if (Keyframe->Type == linear) + draw_list->AddLine(MidPos[1], MidPos[0], col, 1.0f); + else if (Keyframe->Type == bezier) + draw_list->AddBezierCubic(MidPos[1], RightPos[1], LeftPos[0], MidPos[0], col, 1.0f, 8); + } + } + } + // Horiziontal value lines + + // uint32 LineColor = IM_COL32(200, 200, 200, 40); + // for (int i = 0; i < 10; i++) { + // real32 YPos = MinPos.y + (UI->TimelineZoom/2 * i) + 5; + // ImVec2 Min = ImVec2(TimelineStartingPos.x, YPos); + // ImVec2 Max = ImVec2(TimelineStartingPos.x + TimelineSize.x, YPos); + // draw_list->AddLine(Min, Max, LineColor); + // } + + draw_list->PopClipRect(); + + ImGui::SetCursorScreenPos(ImVec2(MinPos.x, MinPos.y)); + ImGui::Button("##SplitMove", ImVec2(TimelineBorderPadding.x, SidebarSizeWithBorder.y)); + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelineSplit += io.MouseDelta.x; + } + + ImGui::SetCursorScreenPos(ImVec2(MinPos.x, MinPos.y)); + ImGui::InvisibleButton("AnimationCurves", ImVec2(TimelineSize.x - 20, GraphWindowHeight), ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + // NOTE(fox): I'm reusing this struct for the other + // channels, so I'm OR'ing it. Also persists across layers. + AnimationCurves.IsItemHovered |= ImGui::IsItemHovered(); + AnimationCurves.IsItemActive |= ImGui::IsItemActive(); + AnimationCurves.IsItemActivated |= ImGui::IsItemActivated(); + AnimationCurves.IsItemDeactivated |= ImGui::IsItemDeactivated(); + AnimationCurves.LeftClick |= ImGui::IsMouseDown(ImGuiMouseButton_Left); + AnimationCurves.RightClick |= ImGui::IsMouseDown(ImGuiMouseButton_Right); + + if (AnimationCurves.IsItemHovered && AnimationCurves.IsItemActivated && + ImGui::IsMouseDown(ImGuiMouseButton_Right)) { + real32 LocalMousePos = io.MousePos.y - MinPos.y - Property->GraphYOffset; + UI->TempZoomRatioGraph = LocalMousePos / Property->GraphLength; + } + // DebugWatchVar("LocalMousePos", &LocalMousePos, d_float); + + if (AnimationCurves.IsItemActive && ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1)) + { + Property->GraphLength += io.MouseDelta.x; + Property->GraphYOffset -= io.MouseDelta.x*UI->TempZoomRatioGraph; + Property->GraphYOffset += io.MouseDelta.y; + } + } + ImGui::PopID(); + } + } + + ImGui::SetCursorPosY(ImGui::GetCursorPos().y - (ItemSpacing.y * UI->KeyframeSpacing / 2)); + ImGui::PopStyleVar(); + + ImGui::PopID(); + } + + // Timeline frame ticks + + ImGui::SetCursorScreenPos(TimelineStartingPos); + if (UI->TimelineZoom > 10) { + for (float x = 0; x < File->NumberOfFrames + 2; x += 1) { + uint32 LineColor = IM_COL32(200, 200, 200, 40); + ImVec2 Min = ImVec2(TimelineStartingPos.x + UI->TimelineZoom * x, TimelineStartingPos.y); + ImVec2 Max = ImVec2(Min.x + 2, WindowMaxAbs.y); + if (x == File->CurrentFrame) continue; + draw_list->AddLine(Min, Max, LineColor); + } + } + + + + draw_list->PopClipRect(); + ImGui::PopClipRect(); + + // Playhead line + + uint32 LineColor = IM_COL32(200, 200, 200, 200); + ImVec2 Min = PlayheadPos; + ImVec2 Max = ImVec2(Min.x + 2, Min.y + TimelineSizeWithBorder.y + TopbarSize.y/2); + draw_list->AddLine(Min, Max, LineColor); + + ImGui::PopStyleVar(); + + // General timeline interaction + + ImGui::SetCursorScreenPos(TimelineAbsolutePos); + ImGui::InvisibleButton("TimelineMoving", TimelineSizeWithBorder, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + bool32 IsHovered = ImGui::IsItemHovered(); + bool32 IsActive = ImGui::IsItemActive(); + bool32 IsItemActivated = ImGui::IsItemActivated(); + bool32 IsItemDeactivated = ImGui::IsItemDeactivated(); + bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left); + bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right); + + if (IsActive || AnimationCurves.IsItemActive) { + if (LeftClick) { + if (io.KeyCtrl && IsActive) { + real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x; + real32 ZoomRatio = LocalMousePos / UI->TimelineZoom; + File->CurrentFrame = (int32)(ZoomRatio + 0.5); + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } else { + if (IsItemActivated || AnimationCurves.IsItemActivated) + { + if (!io.KeyShift) { + // DeselectAllKeyframes(&State); + // DeselectAllLayers(File, State); + } + UI->BoxStart = ImGui::GetMousePos(); + UI->BoxSelectActive = true; + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1) ) + { + UI->BoxEnd = ImGui::GetMousePos(); + draw_list->AddRectFilled(UI->BoxStart, UI->BoxEnd, + IM_COL32(0, 0, 200, 50)); + } + } + // Timeline zooming interaction + } else if (RightClick && IsActive) { + if (IsItemActivated) + { + real32 LocalMousePos = io.MousePos.x - WindowMinAbs.x - UI->TimelineSplit; + UI->TempZoomRatioTimeline = LocalMousePos / TimelineSize.x; + } + if (ImGui::IsMouseDragging(ImGuiMouseButton_Right, -1) ) + { + UI->TimelineZoom += io.MouseDelta.x; + ImGui::SetScrollX(ImGui::GetScrollMaxX() * UI->TempZoomRatioTimeline); + } + } + } + if (IsItemDeactivated || AnimationCurves.IsItemDeactivated) { + UI->BoxStart = {0, 0}; + UI->BoxEnd = {0, 0}; + UI->BoxSelectActive = false; + } + + ImGui::EndChild(); + + ImGui::PopStyleVar(2); + + if (IsRectTouching(WindowMinAbs, WindowMaxAbs, io.MousePos, io.MousePos + 1)) { + if (io.KeyCtrl && io.MouseWheel) { + real32 ZoomAmount = io.MouseWheel*8; + real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x; + real32 ZoomRatio = LocalMousePos / UI->TimelineZoom; + UI->TimelineZoom += ZoomAmount; + UI->ScrollXOffset -= ZoomAmount*ZoomRatio; + } else { + UI->ScrollXOffset += io.MouseWheelH*8; + UI->ScrollYOffset += io.MouseWheel*8; + } + } + + + + ImGui::End(); +} + + +internal void +ImGui_ProcessInputs(project_data *File, project_state *State, pixel_buffer *CompBuffer, memory *Memory, ui *UI, ImGuiIO io) +{ + if (io.KeysData[ImGuiKey_Q].Down) + State->IsRunning = false; + + if (ImGui::IsKeyPressed(ImGuiKey_D)) { + IncrementFrame(File, -1); + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } + + if (ImGui::IsKeyPressed(ImGuiKey_F)) { + IncrementFrame(File, 1); + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } + + + if (ImGui::IsKeyPressed(ImGuiKey_Space)) { + SwitchBool(State->IsPlaying); + } + + if (State->IsPlaying && !IsRendering) { + IncrementFrame(File, 1); + State->UpdateFrame = true; + State->UpdateKeyframes = true; + } + + if (ImGui::IsKeyPressed(ImGuiKey_R) && State->NumberOfSelectedLayers) + TransformsInteract(File, State, UI, sliding_rotation); + if (ImGui::IsKeyPressed(ImGuiKey_S) && State->NumberOfSelectedLayers) + TransformsInteract(File, State, UI, sliding_scale); + if (ImGui::IsKeyPressed(ImGuiKey_G) && State->NumberOfSelectedLayers) + TransformsInteract(File, State, UI, sliding_position); + if (ImGui::IsKeyPressed(ImGuiKey_A) && State->NumberOfSelectedLayers) + TransformsInteract(File, State, UI, sliding_anchorpoint); + + if (ImGui::IsKeyPressed(ImGuiKey_Delete)) + { + switch (State->RecentSelectionType) + { + case selection_none: + { + } break; + case selection_layer: + { + } break; + case selection_effect: + { + } break; + case selection_keyframe: + { + DeleteSelectedKeyframes(File, Memory); + State->UpdateKeyframes = true; + State->UpdateFrame = true; + } break; + } + } + +#if DEBUG + if (ImGui::IsKeyPressed(ImGuiKey_E)) { + SwitchBool(AVXEnabled); + State->UpdateFrame = true; + } + if (ImGui::IsKeyPressed(ImGuiKey_M)) + { + Debug.Markers[Debug.MarkerIndex] = File->CurrentFrame; + Debug.MarkerIndex++; + } +#endif + + bool32 Ended = ImGui::IsMouseDown(ImGuiMouseButton_Left); + if (State->IsInteracting) { + ImVec2 MouseIncrement = io.MouseDelta * (ImVec2(CompBuffer->Width, CompBuffer->Height) / UI->CompZoom); + switch (State->TransformsHotkeyInteract) + { + case sliding_position: + { + InteractProperty(0, File, State, Ended, MouseIncrement.x, Memory); + InteractProperty(1, File, State, Ended, MouseIncrement.y, Memory); + } break; + case sliding_anchorpoint: + { + InteractProperty(2, File, State, Ended, MouseIncrement.x, Memory); + InteractProperty(3, File, State, Ended, MouseIncrement.y, Memory); + } break; + case sliding_rotation: + { + InteractProperty(4, File, State, Ended, MouseIncrement.x / 10.0, Memory); + } break; + case sliding_scale: + { + InteractProperty(5, File, State, Ended, MouseIncrement.x / 200.0, Memory); + } break; + } + } + + + if (!ImGui::IsMouseDown(ImGuiMouseButton_Left)) { + UI->DraggingLayerThreshold = 0; + UI->DraggingTimelineThreshold = 0; + UI->DraggingKeyframeThreshold = 0; + } +} + + +global_variable char ImGuiPrefs[] = "[Window][DockSpaceViewport_11111111]" +"\nSize=3153,1837" +"\nCollapsed=0" +"\n" +"\n[Window][Debug##Default]" +"\nPos=60,60" +"\nSize=400,400" +"\nCollapsed=0" +"\n" +"\n[Window][Viewport]" +"\nPos=528,0" +"\nSize=2121,1208" +"\nCollapsed=0" +"\nDockId=0x00000005,0" +"\n" +"\n[Window][###Properties]" +"\nSize=526,1208" +"\nCollapsed=0" +"\nDockId=0x00000003,0" +"\n" +"\n[Window][Timeline]" +"\nPos=0,1210" +"\nSize=3153,627" +"\nCollapsed=0" +"\nDockId=0x00000002,0" +"\n" +"\n[Window][Dear ImGui Demo]" +"\nPos=1881,692" +"\nSize=550,680" +"\nCollapsed=0" +"\n" +"\n[Window][Files]" +"\nPos=2651,0" +"\nSize=502,1208" +"\nCollapsed=0" +"\nDockId=0x00000006,0" +"\n" +"\n[Docking][Data]" +"\nDockSpace ID=0x8B93E3BD Pos=0,0 Size=3153,1837 Split=Y Selected=0x13926F0B" +"\n DockNode ID=0x00000001 Parent=0x8B93E3BD SizeRef=3200,1171 Split=X Selected=0x13926F0B" +"\n DockNode ID=0x00000003 Parent=0x00000001 SizeRef=526,1171 Selected=0xDBB8CEFA" +"\n DockNode ID=0x00000004 Parent=0x00000001 SizeRef=2672,1171 Split=X Selected=0x13926F0B" +"\n DockNode ID=0x00000005 Parent=0x00000004 SizeRef=2115,1171 CentralNode=1 Selected=0x13926F0B" +"\n DockNode ID=0x00000006 Parent=0x00000004 SizeRef=502,1171 Selected=0x86FA2F90" +"\n DockNode ID=0x00000002 Parent=0x8B93E3BD SizeRef=3200,627 HiddenTabBar=1 Selected=0x0F18B61B" +"\n"; -- cgit v1.2.3