diff options
author | Fox Caminiti <fox@foxcam.net> | 2022-10-20 20:45:37 -0400 |
---|---|---|
committer | Fox Caminiti <fox@foxcam.net> | 2022-10-20 20:45:37 -0400 |
commit | 7804c6a96d26c2e757d09f1864eb73fb81eb280f (patch) | |
tree | 0b8e3f1b6f8a6fdf79fb5ae51844f269e47d5959 /my_imgui_widgets.cpp | |
parent | 0b0aa3b06fac0bcdeb31d5e2211d1ba149531692 (diff) |
precomp recursion!
Diffstat (limited to 'my_imgui_widgets.cpp')
-rw-r--r-- | my_imgui_widgets.cpp | 1773 |
1 files changed, 1057 insertions, 716 deletions
diff --git a/my_imgui_widgets.cpp b/my_imgui_widgets.cpp index e8159fe..179dff2 100644 --- a/my_imgui_widgets.cpp +++ b/my_imgui_widgets.cpp @@ -2,32 +2,1083 @@ #include "my_imgui_internal_widgets.h" #include "imgui_ops.h" +#include "imgui_helper_widgets.cpp" + +static void +ImGui_DebugMemoryViewer(project_state *State) +{ + ImGui::Begin("Memory viewer"); + cache_entry *EntryArray = State->Render.Entry; + char *Type[4] = { "unassigned", "comp", "source", "layer" }; + int c = 0; + while (EntryArray[c].CycleTime != 0) { + ImGui::Text("Type - %s, Start - %i, Info - %i", Type[EntryArray[c].Type], EntryArray[c].Block_StartIndex, EntryArray[c].TypeInfo); + c++; + } + ImGui::End(); +} + static void -ImGui_Viewport(block_composition *MainComp, GLuint textureID) +ImGui_File(project_data *File, project_state *State, memory *Memory, ImGuiIO io) +{ + ImGui::Begin("Files"); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); + + for (int c = 0; c < File->Comp_Count; c++) { + block_composition *Comp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, c); + block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Comp->Name_String_Index); + ImGui::Text(String->Char); + } + for (int c = 0; c < File->Source_Count; c++) { + block_source *Source = (block_source *)Memory_Block_AddressAtIndex(Memory, F_Sources, c); + block_string *String = (block_string *)Memory_Block_AddressAtIndex(Memory, F_Strings, Source->Path_String_Index); + ImGui::Text(String->Char); + } + +#if DEBUG + for (int i = 0; i < Debug.Temp.WatchedProperties; i++) { + if (Debug.Temp.DebugPropertyType[i] == d_float) { + ImGui::Text("%s: %f", Debug.Temp.String[i], Debug.Temp.Val[i].f); + } else if (Debug.Temp.DebugPropertyType[i] == d_int) { + ImGui::Text("%s: %i", Debug.Temp.String[i], Debug.Temp.Val[i].i); + } else if (Debug.Temp.DebugPropertyType[i] == d_uint) { + ImGui::Text("%s: %u", Debug.Temp.String[i], Debug.Temp.Val[i].u); + } + } +#endif + ImGui::End(); +} + +static void +ImGui_Viewport(project_data *File, project_state *State, ui *UI, memory *Memory, ImGuiIO io, GLuint textureID) { bool open = true; ImGui::Begin("Viewport", &open, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); + if (ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows)) + UI->FocusedWindow = focus_viewport; + + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, 0); + + ImVec2 ViewportMin = ImGui::GetCursorScreenPos(); ImVec2 ViewportScale = ImGui::GetContentRegionAvail(); ImVec2 ViewportMax = ImVec2(ViewportMin.x + ViewportScale.x, ViewportMin.y + ViewportScale.y); + if (State->Initializing) { + UI->CompZoom = ImVec2(MainComp->Width, MainComp->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)); + } + + ImVec2 CompPosMin = ImVec2(UI->CompPos.x, UI->CompPos.y); + ImVec2 CompPosMax = ImVec2(UI->CompPos.x + UI->CompZoom.x, UI->CompPos.y + UI->CompZoom.y); + 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)); - - ImVec2 CompZoom(MainComp->Width, MainComp->Height); - ImVec2 CompPos = ViewportMin + (ViewportMax - ViewportMin)*0.5 - CompZoom*0.5; + draw_list->AddRect(CompPosMin, CompPosMax, IM_COL32(255, 255, 255, 55)); // Actual composition texture draw_list->PushClipRect(ViewportMin, ViewportMax, true); - draw_list->AddImage((void *)(intptr_t)textureID, ImVec2(CompPos.x, CompPos.y), - ImVec2(CompPos.x + CompZoom.x, CompPos.y + CompZoom.y)); + draw_list->AddImage((void *)(intptr_t)textureID, CompPosMin, CompPosMax); + draw_list->PopClipRect(); + + // Interactions for dragging and zooming + ImGui::SetCursorScreenPos(ViewportMin); + + ImGui::InvisibleButton("canvas", ViewportScale, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + bool32 IsHovered = ImGui::IsItemHovered(); + bool32 IsActive = ImGui::IsItemActive(); + bool32 IsActivated = ImGui::IsItemActivated(); + bool32 IsDeactivated = ImGui::IsItemDeactivated(); + + if (IsHovered && IsActivated && ImGui::IsMouseDown(ImGuiMouseButton_Left)) + { + // Point to zoom in on if Z is held + UI->TempZoomRatio = ImGui_ScreenPointToCompUV(ViewportMin, UI->CompPos, UI->CompZoom, io.MousePos); + + // Layer selection + /* + if (!ImGui::IsKeyDown(ImGuiKey_Z) || !State->Pen.IsActive) { + for (int i = File.NumberOfLayers - 1; i >= 0; i--) { + project_layer *Layer = File.Layer[i]; + if (!io.KeyShift) DeselectAllLayers(&File, State); + v2 LayerUV = CompUVToLayerUV(Layer, &CompBuffer, UI->TempZoomRatio); + if (TestUV(LayerUV) && !Layer->IsSelected) + { + SelectLayer(Layer, 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)MainComp->Width/MainComp->Height; + UI->CompZoom.y += (Distance); + UI->CompPos.x -= ((Distance)*(real32)MainComp->Width/MainComp->Height)*UI->TempZoomRatio.x; + UI->CompPos.y -= Distance*UI->TempZoomRatio.y; + } + + ImGui::SetCursorScreenPos(ImVec2(ViewportMin.x, ViewportMin.y + ViewportScale.y - ImGui::GetFontSize()*1.5)); + + ImGui::Text("%.1f", 100.0f * (UI->CompZoom.x / MainComp->Width)); + if (State->MsgTime > 0) { + ImGui::SameLine(); + ImGui::SetCursorPosX((ViewportScale.x / 5)*4); + ImGui::Text(State->Msg); + State->MsgTime--; + } + + ImGui::End(); + + + /* + for (int i = 0; i < AmountOf(Layer->Property); i++) { + ImGui::PushID(i); + property_channel *Property = &Layer->Property[i]; + ImGui::DragScalar(Property->Name, ImGuiDataType_Float, &Property->CurrentValue, + Property->ScrubVal, &Property->MinVal, &Property->MaxVal, "%f"); + if (ImGui::IsItemActive()) + State->UpdateFrame = true; + ImGui::PopID(); + } + */ +} + +static void +ImGui_TimelineHorizontalIncrementDraw(ui *UI, ImDrawList *draw_list, ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, block_composition MainComp) +{ + uint32 LineColor = IM_COL32(200, 200, 200, 40); + + real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed.x ; + real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset.x / UI->TimelinePercentZoomed.x; + + Assert(TimelineZoomSize > 0.0f); + + real32 x = 0; + bool32 RightmostEdge = false; + real32 Increment = (real32)1 / MainComp.Frame_Count; + if (UI->TimelinePercentZoomed.x > 0.90) + Increment = (real32)MainComp.FPS / MainComp.Frame_Count; + else if (UI->TimelinePercentZoomed.x > 0.40) + Increment *= 2; + + while (!RightmostEdge) { + ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineAbsolutePos.y); + ImVec2 Max = ImVec2(Min.x + 2, TimelineAbsolutePos.y + TimelineSizeWithBorder.y); + if (Min.x < TimelineAbsolutePos.x + TimelineSizeWithBorder.x) { + draw_list->AddLine(Min, Max, LineColor); + char buf2[6]; + uint32 FrameNumber = (uint32)(x * MainComp.Frame_Count) % MainComp.FPS; + if (FrameNumber != 0) + sprintf(buf2, ":%.2i", FrameNumber); + else + sprintf(buf2, "%.2i:00", (uint32)(x * MainComp.Frame_Count) / MainComp.FPS); + draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); + x += Increment; + if (x > 1.0f) + RightmostEdge = true; + } else { + RightmostEdge = true; + } + } +} + +static void +ImGui_Timeline_DrawLayer(project_data *File, project_state *State, memory *Memory, ui *UI, ImGuiIO io, ImDrawList *draw_list, block_layer *Layer, block_composition *MainComp, uint16 i, + ImVec2 Increment, ImVec2 TimelineAbsolutePos, ImVec2 TimelineMoveSize, ImVec2 TimelineZoomSize, + ImVec2 TimelineSize, ImVec2 TimelineSizeWithBorder, real32 LayerIncrement, + sorted_comp_info *SortedCompArray, sorted_layer *SortedLayerArray) +{ + int32 Frame_Start = Layer->Frame_Start; + int32 Frame_End = Layer->Frame_End; + real32 Vertical_Offset = Layer->Vertical_Offset; + if (Layer->IsSelected) + Layer_Interact_Evaluate(Memory, State, &Frame_Start, &Frame_End, &Vertical_Offset); + ImVec2 Layer_LocalPos = ImVec2(Frame_Start, Vertical_Offset); + ImVec2 Layer_LocalSize = ImVec2(Frame_End - Frame_Start, Layer->Vertical_Height); + + ImVec2 Layer_LocalPos_Ratio = (Layer_LocalPos * Increment); + ImVec2 Layer_LocalSize_Ratio = Layer_LocalSize * Increment; + ImVec2 Layer_ScreenPos_Min = TimelineAbsolutePos + TimelineMoveSize + (Layer_LocalPos_Ratio * TimelineZoomSize); + ImVec2 Layer_ScreenPos_Max = TimelineAbsolutePos + TimelineMoveSize + ((Layer_LocalPos_Ratio + Layer_LocalSize_Ratio) * TimelineZoomSize); + ImVec2 Layer_ScreenSize = Layer_ScreenPos_Max - Layer_ScreenPos_Min; + + if (UI->BoxSelect) { + bool32 Test = 0; + if (io.MouseClickedPos[0].y < io.MousePos.y) + Test = (Layer_ScreenPos_Min.y >= io.MouseClickedPos[0].y && Layer_ScreenPos_Min.y <= io.MousePos.y); + else + Test = (Layer_ScreenPos_Max.y <= io.MouseClickedPos[0].y && Layer_ScreenPos_Max.y >= io.MousePos.y); + + if (Test) { + if (!Layer->IsSelected) { + Layer->IsSelected = true; + } + } else if (!io.KeyShift) { + Layer->IsSelected = false; + } + } + + draw_list->AddRectFilled(Layer_ScreenPos_Min, Layer_ScreenPos_Max, + ImColor(Layer->Col[0], Layer->Col[1], Layer->Col[2], 1.0f)); + draw_list->AddRect(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(1.0f, 1.0f, 1.0f, 0.5f), 2); + + + if (Layer->IsSelected) { + draw_list->AddRectFilled(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(0.25f, 0.25f, 0.25f, 0.5f), 2); + draw_list->AddRect(Layer_ScreenPos_Min, Layer_ScreenPos_Max, ImColor(1.0f, 1.0f, 1.0f, 0.5f), 2); + } + + ImVec2 ResizeSize = ImVec2(Increment.x * 0.45 * TimelineZoomSize.x, Layer_ScreenSize.y); + ImVec2 ResizePos[2] = { Layer_ScreenPos_Min, ImVec2(Layer_ScreenPos_Max.x - ResizeSize.x, Layer_ScreenPos_Min.y) }; + for (int b = 0; b < 2; b++) { + ImGui::PushID(b); + ImGui::SetCursorScreenPos(ResizePos[b]); + ImGui::Button("##layer_resize", ResizeSize); + + if (ImGui::IsItemHovered()) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); + } + if (ImGui::IsItemActivated()) { + if (!Layer->IsSelected) { + State->MostRecentlySelectedLayer = i; + Layer->IsSelected = true; + } + } + if (ImGui::IsItemActive()) { + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) { + State->Interact_Active = interact_type_layer_timeadjust; + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Warp_X, UI->Warp_Y) * TimelineSize); + + State->Interact_Offset[0] = (DragDelta.x / TimelineSizeWithBorder.x * UI->TimelinePercentZoomed.x) * MainComp->Frame_Count; + State->Interact_Offset[1] = b; + DebugWatchVar("Offset1", &State->Interact_Offset[0], d_float); + } + } + if (ImGui::IsItemDeactivated()) { + if (State->Interact_Active == interact_type_layer_timeadjust) { + for (int a = 0; a < File->Layer_Count; a++) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + if (Layer->IsSelected) { + Layer_Interact_Evaluate(Memory, State, &Layer->Frame_Start, &Layer->Frame_End, &Layer->Vertical_Offset); + } + } + State->Interact_Active = interact_type_none; + State->Interact_Offset[0] = 0; + State->Interact_Offset[1] = 0; + } + } + + ImGui::PopID(); + } + + ImGui::SetCursorScreenPos(Layer_ScreenPos_Min); + ImGui::InvisibleButton("##layer_mid", Layer_ScreenSize, ImGuiMouseButton_Left); + + if (ImGui::IsItemActivated()) { + if (!Layer->IsSelected) { + if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count); + State->MostRecentlySelectedLayer = i; + Layer->IsSelected = true; + } + } + + if (ImGui::IsItemActive()) { + if (ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) { + State->Interact_Active = interact_type_layer_move; + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Warp_X, UI->Warp_Y) * TimelineSize); + + State->Interact_Offset[0] = (DragDelta.x / TimelineSizeWithBorder.x * UI->TimelinePercentZoomed.x) * MainComp->Frame_Count; + State->Interact_Offset[1] = (DragDelta.y / TimelineSizeWithBorder.y * UI->TimelinePercentZoomed.y) * LayerIncrement; + + /* + if (UI->DragDelta_Prev.x != 0) { + ImVec2 Offset_Old = (UI->DragDelta_Prev / TimelineSizeWithBorder * UI->TimelinePercentZoomed) * ImVec2(MainComp->Frame_Count, LayerIncrement); + if ((int32)State->Interact_Offset[1] != (int32)Offset_Old.y) + State->UpdateFrame = true; + } + + UI->DragDelta_Prev = DragDelta; + */ + } + } + + if (ImGui::IsItemDeactivated()) { + if (State->Interact_Active == interact_type_layer_move) { + for (int a = 0; a < File->Layer_Count; a++) { + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, a); + if (Layer->IsSelected) { + Layer_Interact_Evaluate(Memory, State, &Layer->Frame_Start, &Layer->Frame_End, &Layer->Vertical_Offset); + } + } + State->Interact_Active = interact_type_none; + State->Interact_Offset[0] = 0; + State->Interact_Offset[1] = 0; + } + } + + if (Layer->IsPrecomp) { + ImVec2 MinClipPos = ImVec2(Layer_ScreenPos_Min.x, Layer_ScreenPos_Max.y); + ImVec2 MaxClipPos = ImVec2(Layer_ScreenPos_Max.x, MinClipPos.y + TimelineZoomSize.y); + draw_list->AddRectFilled(MinClipPos, MaxClipPos, ImColor(0.2f, 0.2f, 0.2f, 1.0f)); + + sorted_comp_info SortedCompInfo = SortedCompArray[Layer->Block_Source_Index]; + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, Layer->Block_Source_Index); + + for (int a = 0; a < SortedCompInfo.LayerCount; a++) + { + ImGui::PushID(a); + sorted_layer SortEntry = SortedLayerInfo[a]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); + + ImVec2 Layer_LocalPos_Screen = Layer_LocalPos_Ratio * TimelineZoomSize; + ImVec2 NestedTimelineAbsolutePos = TimelineAbsolutePos + Layer_LocalPos_Screen + ImVec2(0, Layer_ScreenSize.y); + ImGui_Timeline_DrawLayer(File, State, Memory, UI, io, draw_list, Layer, MainComp, a, + Increment, NestedTimelineAbsolutePos, TimelineMoveSize, TimelineZoomSize, + TimelineSize, TimelineSizeWithBorder, LayerIncrement, + SortedCompArray, SortedLayerArray); + ImGui::PopID(); + } + } +} + +static 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 || WindowSize.y < 50) { + ImGui::PopStyleVar(2); + ImGui::End(); + return; + } + + block_composition *MainComp = (block_composition *)Memory_Block_AddressAtIndex(Memory, F_Precomps, File->PrincipalCompIndex); + + ImVec2 WindowMinAbs = ImGui::GetWindowPos(); + ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize; + + ImVec2 ButtonSize = ImVec2(FontHeight*2, FontHeight*2); + + ImVec2 TimelineBorderPadding = ImVec2(FontHeight, FontHeight); + + ImVec2 TimelineSize = ImVec2(WindowSize.x, WindowSize.y); + ImVec2 TimelineSizeWithBorder = TimelineSize - TimelineBorderPadding*2; + ImVec2 TimelineAbsolutePos = WindowMinAbs + TimelineBorderPadding; + + ImVec2 KeyframeSize = ImVec2(FontHeight, FontHeight); + + int32 FrameCount = MainComp->Frame_Count; + + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRectFilled(WindowMinAbs, WindowMaxAbs, + IM_COL32(255, 255, 255, 50)); + + + ImGui::BeginChild("Timeline", TimelineSize, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, ItemSpacing.y)); + + ImGui::PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); + draw_list->PushClipRect(TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, true); + + real32 LayerIncrement = 40; + + ImVec2 Val_Min(0, 0); + ImVec2 Val_Max(40, LayerIncrement); + + if (UI->TimelinePercentZoomed.x == 0) { + UI->TimelinePercentZoomed = ImVec2(1, 1); + } + + ImGui_TimelineHorizontalIncrementDraw(UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, *MainComp); + + ImVec2 TimelineZoomSize = TimelineSizeWithBorder / UI->TimelinePercentZoomed; + ImVec2 TimelineMoveSize = TimelineSizeWithBorder * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; + + ImVec2 Increment = ImVec2((real32)1 / MainComp->Frame_Count, (real32)1 / LayerIncrement); + + uint64 SortSize = (sizeof(sorted_comp_info) * File->Comp_Count) + (sizeof(sorted_layer) * File->Layer_Count); + void *SortedArray = Memory_PushScratch(Memory, SortSize); + Arbitrary_Zero((uint8 *)SortedArray, SortSize); + sorted_comp_info *SortedCompArray = (sorted_comp_info *)SortedArray; + sorted_layer *SortedLayerArray = (sorted_layer *)((uint8 *)SortedArray + (sizeof(sorted_comp_info) * File->Comp_Count)); + Layer_SortAll(Memory, SortedLayerArray, SortedCompArray, File->Layer_Count, File->Comp_Count); + + sorted_comp_info SortedCompInfo = SortedCompArray[File->PrincipalCompIndex]; + sorted_layer *SortedLayerInfo = Layer_GetSortedArray(SortedLayerArray, SortedCompArray, File->PrincipalCompIndex); + + for (int i = 0; i < SortedCompInfo.LayerCount; i++) + { + ImGui::PushID(i); + sorted_layer SortEntry = SortedLayerInfo[i]; + uint32 Index_Physical = SortEntry.Block_Layer_Index; + block_layer *Layer = (block_layer *)Memory_Block_AddressAtIndex(Memory, F_Layers, Index_Physical); + + ImGui_Timeline_DrawLayer(File, State, Memory, UI, io, draw_list, Layer, MainComp, i, + Increment, TimelineAbsolutePos, TimelineMoveSize, TimelineZoomSize, + TimelineSize, TimelineSizeWithBorder, LayerIncrement, + SortedCompArray, SortedLayerArray); + + ImGui::PopID(); + } + + Memory_PopScratch(Memory, SortSize); + + ImVec2 MouseDelta = io.MouseDelta / TimelineSize; + + real32 BarHandleSize = FontHeight; + real32 BarThickness = 50; + real32 BarMinZoom = 0.01; + + real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset.x; + real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed.x); + + // I use "UI" to denote the size/position after clipping the bar so that it + // doesn't go out of bounds and the handles are always selectable at the edges. + + real32 BarH_Offset = Max(BarH_Pos, 0); + + real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ? + TimelineSizeWithBorder.x - BarH_Pos : + BarH_Size + (BarH_Pos - BarH_Offset); + + if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x) + BarH_SizeUI = TimelineSizeWithBorder.x; + + BarH_SizeUI = BarH_SizeUI - BarHandleSize*2; + + BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4); + + BarH_Offset = Min(BarH_Offset, TimelineSize.x - BarH_SizeUI - BarHandleSize*4); + ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_Offset, TimelineSize.y - BarThickness); + bool32 BarHeld = false; + + ImGui::SetCursorScreenPos(BarH_PosUI); + ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + if ((UI->TimelinePercentZoomed.x - MouseDelta.x) > BarMinZoom) { + UI->TimelinePercentZoomed.x -= MouseDelta.x; + UI->TimelinePercentOffset.x -= MouseDelta.x; + } + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0)); + ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness)); + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelinePercentOffset.x -= MouseDelta.x; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0)); + ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + if ((UI->TimelinePercentZoomed.x + MouseDelta.x) > BarMinZoom) { + UI->TimelinePercentZoomed.x += MouseDelta.x; + } + BarHeld = true; + } + + if (BarHeld) { + ImGui_WarpMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 1); + } + + Assert(UI->TimelinePercentZoomed.x > BarMinZoom); + + real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2; + real32 BarV_Pos = -BarV_MaxSize * UI->TimelinePercentOffset.y; + real32 BarV_Size = BarV_MaxSize / (1 / UI->TimelinePercentZoomed.y); + BarV_Size = Max(BarV_Size, FontHeight*4); + + real32 BarV_Offset = Max(BarV_Pos, 0); + + real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ? + BarV_MaxSize - BarV_Pos : + BarV_Size + (BarV_Pos - BarV_Offset); + + if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize) + BarV_SizeUI = BarV_MaxSize; + + BarV_SizeUI = BarV_SizeUI - BarHandleSize*2; + + BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4); + + BarV_Offset = Min(BarV_Offset, BarV_MaxSize - BarV_SizeUI - BarHandleSize*4); + ImVec2 BarV_PosUI = TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, BarV_Offset); + BarHeld = false; + + ImGui::SetCursorScreenPos(BarV_PosUI); + ImGui::Button("##h-scrollbarleft", ImVec2(BarThickness, BarHandleSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed.y -= MouseDelta.y; + UI->TimelinePercentOffset.y -= MouseDelta.y; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize)); + ImGui::Button("##h-scrollbar", ImVec2(BarThickness, BarV_SizeUI)); + + if (ImGui::IsItemHovered() && io.MouseWheel) + { + UI->TimelinePercentOffset.y -= io.MouseWheel/10; + } + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelinePercentOffset.y -= MouseDelta.y; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize) + ImVec2(0, BarV_SizeUI)); + ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->TimelinePercentZoomed.y += MouseDelta.y; + BarHeld = true; + } + + UI->TimelinePercentZoomed.y = Max(UI->TimelinePercentZoomed.y, 0.01); + + if (BarHeld) { + ImGui_WarpMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 2); + } + + ImGui::SetCursorScreenPos(TimelineAbsolutePos); + ImGui::InvisibleButton("TimelineMoving", TimelineSizeWithBorder, ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight); + // ImGui::Button("TimelineMoving", TimelineSizeWithBorder); + bool32 IsHovered = ImGui::IsItemHovered(); + bool32 IsItemActive = ImGui::IsItemActive(); + bool32 IsItemActivated = ImGui::IsItemActivated(); + bool32 IsItemDeactivated = ImGui::IsItemDeactivated(); + bool32 LeftClick = ImGui::IsMouseDown(ImGuiMouseButton_Left); + bool32 RightClick = ImGui::IsMouseDown(ImGuiMouseButton_Right); + + if (IsHovered && io.MouseWheel) { + real32 Increment = 0.1; + bool32 Direction = (io.MouseWheel > 0) ? 1 : -1; + ImVec2 Offset = (io.MousePos - (TimelineAbsolutePos + TimelineMoveSize)) / TimelineZoomSize; + if (io.KeyShift) { + UI->TimelinePercentOffset.y += Increment*Direction; + } else if (io.KeyCtrl) { + UI->TimelinePercentOffset.x += Increment*Direction*0.3; + } else { + if (Direction == 1) { + UI->TimelinePercentZoomed = UI->TimelinePercentZoomed - (UI->TimelinePercentZoomed * Increment); + UI->TimelinePercentOffset = UI->TimelinePercentOffset - ((UI->TimelinePercentOffset * Increment) + Offset*Increment); + } else { + UI->TimelinePercentOffset = ((UI->TimelinePercentOffset + Offset*Increment) / (1.0f - Increment)); + UI->TimelinePercentZoomed = (UI->TimelinePercentZoomed / (1.0f - Increment)); + } + } + } + + if (IsItemActivated) { + if (!io.KeyShift) Layer_DeselectAll(Memory, File->Layer_Count); + UI->BoxSelect = true; + } + if (IsItemActive) { + Assert(UI->BoxSelect); + draw_list->AddRectFilled(io.MouseClickedPos[0], io.MousePos, + IM_COL32(0, 0, 200, 50)); + } + + if (IsItemDeactivated) { + UI->BoxSelect = false; + } + draw_list->PopClipRect(); + ImGui::PopClipRect(); + + ImGui::PopStyleVar(); + + + ImGui::EndChild(); + + ImGui::PopStyleVar(2); ImGui::End(); } +static void +ImGui_ProcessInputs(project_state *State, ImGuiIO io) +{ + if (io.KeysData[ImGuiKey_Q].Down) { + State->IsRunning = false; + } + if (io.KeysData[ImGuiKey_A].Down) { + State->UpdateFrame = true; + } +} + +#if 0 + real32 MaxVal_Y = -10000; + real32 MinVal_Y = 10000; + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { + keyframe *Keyframe = KeyframeLookup(Property, b); + MaxVal_Y = (Keyframe->Value.f > MaxVal_Y) ? Keyframe->Value.f : MaxVal_Y; + MinVal_Y = (Keyframe->Value.f < MinVal_Y) ? Keyframe->Value.f : MinVal_Y; + } + + keyframe *FirstKeyframe = KeyframeLookup(Property, 0); + keyframe *LastKeyframe = KeyframeLookup(Property, Property->NumberOfTotalKeyframes - 1); + real32 MinVal_X = (Layer->BitmapInfo.FrameOffset + FirstKeyframe->FrameNumber); + real32 MaxVal_X = (Layer->BitmapInfo.FrameOffset + LastKeyframe->FrameNumber); + + UI->Y_MaxVal = MaxVal_Y; + UI->Y_MinVal = MinVal_Y; + + if (!UI->IsDragging) { + UI->Display_Y_MinVal = UI->Y_MinVal; + UI->Display_Y_MaxVal = UI->Y_MaxVal; + } + + real32 Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed; + real32 Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset; + MaxVal_Y = UI->Display_Y_MaxVal; + MinVal_Y = UI->Display_Y_MinVal; + + DebugWatchVar("offset: ", &Y_TimelinePercentOffset, d_float); + DebugWatchVar("zoom: ", &Y_TimelinePercentZoomed, d_float); + + real32 Ratio_Graph_X = (MaxVal_X - MinVal_X) / File->NumberOfFrames; + real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; + real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; + real32 Y_TimelineZoomSize = TimelineSizeWithBorder.y / Y_TimelinePercentZoomed; + real32 Y_TimelineMoveSize = TimelineSizeWithBorder.y * Y_TimelinePercentOffset / Y_TimelinePercentZoomed; + + for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { + ImGui::PushID(b); + + keyframe *Keyframe = KeyframeLookup(Property, b); + // Only used for drawing the bezier. + keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b + 1) : NULL; + + real32 Increment_X = (real32)1 / File->NumberOfFrames; + real32 UI_FrameDistance = Increment_X*TimelineZoomSize; + + int32 Keyframe_X = (Layer->BitmapInfo.FrameOffset + Keyframe->FrameNumber); + real32 Keyframe_Y = Keyframe->Value.f; + + real32 Ratio_X_Mid = (real32)Keyframe_X / File->NumberOfFrames; + real32 Ratio_Y_Mid = (Keyframe_Y - MinVal_Y) / (MaxVal_Y - MinVal_Y); + + ImVec2 KeyframePos_Mid = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_Mid*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_Mid)*Y_TimelineZoomSize); + + ImGui::SetCursorScreenPos(KeyframePos_Mid); + ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); + + if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_R)) { + UI->TempVal = Keyframe->Value.f; + UI->TempVal_X = Keyframe->FrameNumber; + } + + if (ImGui::IsItemActivated()) { + UI->IsDragging = true; + UI->TempVal = Keyframe->Value.f; + UI->TempVal_X = Keyframe->FrameNumber; + } + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); + DebugWatchVar("DragX", &DragDelta.x, d_float); + DebugWatchVar("DragY", &DragDelta.y, d_float); + DebugWatchVar("Wrap_X", &UI->Wrap_X, d_int); + DebugWatchVar("Wrap_Y", &UI->Wrap_Y, d_int); + real32 MouseDeltaRatio = -DragDelta.y / TimelineSizeWithBorder.y * Y_TimelinePercentZoomed; + ImVec2 Increment = ImVec2(DragDelta.x / UI_FrameDistance, ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio)); + + // The plus 0.5 * X_Direction is for making the frame jump happen + // when the cursor is between two frames rather than when passing one. + real32 X_Direction = (Increment.x > 0) ? fabsf(Increment.x) / Increment.x : 0; + + Keyframe->FrameNumber = UI->TempVal_X + (int32)(Increment.x + 0.5*X_Direction); + Keyframe->Value.f = UI->TempVal + Increment.y; + + if (io.KeyShift) { + bool32 RestrainAxis = (fabsf(DragDelta.x) > fabsf(DragDelta.y)); + if (RestrainAxis) { + Keyframe->Value.f = UI->TempVal; + } else { + Keyframe->FrameNumber = UI->TempVal_X; + } + } + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); + } + + // TODO(fox): This is kind of a mess. I built the graph around the + // ratios of the keyframes/timeline to make the bars straightforward, + // meaning the position/offset have to be transformed into a new space + // when a new min/max value is reached. + + if (ImGui::IsItemDeactivated()) { + if ((UI->TempVal >= MaxVal_Y) || Keyframe->Value.f == UI->Y_MaxVal) { + real32 Min = ((Ratio_Y_Mid <= 0.0f) && (UI->TempVal >= MaxVal_Y)) ? MinVal_Y : UI->Y_MinVal; + real32 RealRatio_Y = (UI->Y_MaxVal - UI->Y_MinVal) / (MaxVal_Y - MinVal_Y); + UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / RealRatio_Y; + UI->Y_TimelinePercentOffset = (1.0f/RealRatio_Y + UI->Y_TimelinePercentOffset/RealRatio_Y - 1.0f); + } else if (UI->TempVal <= MinVal_Y || Keyframe->Value.f == UI->Y_MinVal) { + real32 RealRatio_Y = (UI->Y_MinVal - MinVal_Y) / (MaxVal_Y - MinVal_Y); + UI->Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset / (1 - RealRatio_Y); + UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / (1 - RealRatio_Y); + } + UI->IsDragging = false; + UI->Wrap_X = 0; + UI->Wrap_Y = 0; + } + + ImU32 col = ImGui::GetColorU32(ImGuiCol_ScrollbarGrab); + + ImVec2 Handle_Pos[2] = {}; + + if (Keyframe->Type == bezier) { + ImVec2 Handle_Ratio[2] = {}; + + Handle_Ratio[0] = ImVec2((real32)(Keyframe_X + Keyframe->TangentLeft.x) / File->NumberOfFrames, + (Keyframe_Y + Keyframe->TangentLeft.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); + Handle_Ratio[1] = ImVec2((real32)(Keyframe_X + Keyframe->TangentRight.x) / File->NumberOfFrames, + (Keyframe_Y + Keyframe->TangentRight.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); + + Handle_Pos[0] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[0].x*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[0].y)*Y_TimelineZoomSize); + Handle_Pos[1] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[1].x*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[1].y)*Y_TimelineZoomSize); + + draw_list->AddLine(KeyframePos_Mid, Handle_Pos[0], col, 1.0f); + draw_list->AddLine(KeyframePos_Mid, Handle_Pos[1], col, 1.0f); + + for (int i = 0; i < 2; i++) { + ImGui::SetCursorScreenPos(Handle_Pos[i]); + ImGui::Button((i == 0) ? "##keyframe_left" : "##keyframe_right", ImVec2(FontHeight, FontHeight)); + v2 *Tangent = (i == 0) ? &Keyframe->TangentLeft : &Keyframe->TangentRight; + + if (ImGui::IsItemActivated()) { + UI->IsDragging = true; + UI->TempVal_X = Tangent->x; + UI->TempVal = Tangent->y; + } + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + ImVec2 DragDelta = ImGui::GetMouseDragDelta(); + DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); + ImVec2 MouseDeltaRatio = (ImVec2(1, -1) * DragDelta) / TimelineSizeWithBorder * ImVec2(UI->TimelinePercentZoomed / Ratio_Graph_X, Y_TimelinePercentZoomed); + real32 NewPos_X = ((MaxVal_X - MinVal_X) * MouseDeltaRatio.x); + real32 NewPos_Y = (io.KeyShift) ? 0 : ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio.y); + *Tangent = V2(UI->TempVal_X, UI->TempVal) + V2(NewPos_X, NewPos_Y); + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); + } + + if (ImGui::IsItemDeactivated()) { + UI->IsDragging = false; + UI->Wrap_X = 0; + UI->Wrap_Y = 0; + } + } + } + + if (NextKeyframe) { + real32 Ratio_X_2 = (real32)(Layer->BitmapInfo.FrameOffset + NextKeyframe->FrameNumber) / File->NumberOfFrames; + real32 Ratio_Y_2 = (NextKeyframe->Value.f - MinVal_Y) / (MaxVal_Y - MinVal_Y); + + ImVec2 NextKeyframePos = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_2*TimelineZoomSize, + TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_2)*Y_TimelineZoomSize); + draw_list->AddLine(KeyframePos_Mid, NextKeyframePos, col, 1.0f); + } + + ImGui::PopID(); + } + + ImGui_TimelineIncrementDraw(File, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, 0); + ImGui_TimelineIncrementDraw2(File, draw_list, MaxVal_Y, MinVal_Y, Y_TimelinePercentZoomed, Y_TimelinePercentOffset, TimelineSizeWithBorder, TimelineAbsolutePos, 0); + +#if DEBUG + draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.25, TimelineSizeWithBorder.y - 50), + 2, IM_COL32(200, 000, 200, 200), 16, 1); + draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.5, TimelineSizeWithBorder.y - 50), + 2, IM_COL32(200, 000, 200, 200), 16, 1); + draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.75, TimelineSizeWithBorder.y - 50), + 2, IM_COL32(200, 000, 200, 200), 16, 1); +#endif + +#endif + +#if 0 + + ImVec2 MouseDelta = io.MouseDelta / TimelineSize; + + real32 BarHandleSize = FontHeight; + real32 BarThickness = 50; + real32 BarMinZoom = 0.01; + + real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset; + real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed); + + // I use "UI" to denote the size/position after clipping the bar so that it + // doesn't go out of bounds and the handles are always selectable at the edges. + + real32 BarH_Offset = Max(BarH_Pos, 0); + + real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ? + TimelineSizeWithBorder.x - BarH_Pos : + BarH_Size + (BarH_Pos - BarH_Offset); + + if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x) + BarH_SizeUI = TimelineSizeWithBorder.x; + + BarH_SizeUI = BarH_SizeUI - BarHandleSize*2; + + BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4); + + BarH_Offset = Min(BarH_Offset, TimelineSize.x - BarH_SizeUI - BarHandleSize*4); + ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_Offset, TimelineSize.y - BarThickness); + bool32 BarHeld = false; + + ImGui::SetCursorScreenPos(BarH_PosUI); + ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + if ((UI->TimelinePercentZoomed - MouseDelta.x) > BarMinZoom) { + UI->TimelinePercentZoomed -= MouseDelta.x; + UI->TimelinePercentOffset -= MouseDelta.x; + } + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0)); + ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness)); + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->TimelinePercentOffset -= MouseDelta.x; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0)); + ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + if ((UI->TimelinePercentZoomed + MouseDelta.x) > BarMinZoom) { + UI->TimelinePercentZoomed += MouseDelta.x; + } + BarHeld = true; + } + + if (BarHeld) { + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 1); + } + + Assert(UI->TimelinePercentZoomed > BarMinZoom); + + real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2; + real32 BarV_Pos = -BarV_MaxSize * Y_TimelinePercentOffset; + real32 BarV_Size = BarV_MaxSize / (1 / Y_TimelinePercentZoomed); + BarV_Size = Max(BarV_Size, FontHeight*4); + + real32 BarV_Offset = Max(BarV_Pos, 0); + + real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ? + BarV_MaxSize - BarV_Pos : + BarV_Size + (BarV_Pos - BarV_Offset); + + if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize) + BarV_SizeUI = BarV_MaxSize; + + BarV_SizeUI = BarV_SizeUI - BarHandleSize*2; + + BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4); + + BarV_Offset = Min(BarV_Offset, BarV_MaxSize - BarV_SizeUI - BarHandleSize*4); + ImVec2 BarV_PosUI = TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, BarV_Offset); + BarHeld = false; + + ImGui::SetCursorScreenPos(BarV_PosUI); + ImGui::Button("##h-scrollbarleft", ImVec2(BarThickness, BarHandleSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->Y_TimelinePercentZoomed -= MouseDelta.y; + UI->Y_TimelinePercentOffset -= MouseDelta.y; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize)); + ImGui::Button("##h-scrollbar", ImVec2(BarThickness, BarV_SizeUI)); + + if (ImGui::IsItemHovered() && io.MouseWheel) + { + UI->Y_TimelinePercentOffset -= io.MouseWheel/10; + } + + if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) + { + UI->Y_TimelinePercentOffset -= MouseDelta.y; + BarHeld = true; + } + + ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize) + ImVec2(0, BarV_SizeUI)); + ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize)); + + if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) + { + UI->Y_TimelinePercentZoomed += MouseDelta.y; + BarHeld = true; + } + + UI->Y_TimelinePercentZoomed = Max(UI->Y_TimelinePercentZoomed, 0.01); + + if (BarHeld) { + ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 2); + } + + draw_list->PopClipRect(); + ImGui::PopClipRect(); + + ImGui::PopStyleVar(); + + if (io.MouseWheel) { + // NOTE(fox): Change this if any other action is added when hovering over the bar area. + bool32 BarHovering_H = TestRectangle(TimelineAbsolutePos + ImVec2(0, TimelineSize.y - BarThickness), + TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), + io.MousePos); + bool32 BarHovering_V = TestRectangle(TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, 0), + TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), + io.MousePos); + if (BarHovering_H && io.MouseWheel) { + UI->TimelinePercentOffset -= io.MouseWheel/15; + } else if (BarHovering_V && io.MouseWheel) { + UI->Y_TimelinePercentOffset -= io.MouseWheel/15; + } else { + real32 Increment = 0.1; + bool32 Direction = (io.MouseWheel > 0) ? 1 : -1; + real32 Offset = (io.MousePos.y - (TimelineAbsolutePos.y + Y_TimelineMoveSize)) / Y_TimelineZoomSize; + real32 X_Offset = (io.MousePos.x - (TimelineAbsolutePos.x + TimelineMoveSize)) / TimelineZoomSize; + DebugWatchVar("X Offset", &X_Offset, d_float); + if (io.KeyShift) { + UI->Y_TimelinePercentOffset += Increment*Direction; + } else if (io.KeyCtrl) { + UI->TimelinePercentOffset += Increment*Direction*0.3; + } else { + if (Direction == 1) { + UI->Y_TimelinePercentZoomed -= (UI->Y_TimelinePercentZoomed * Increment); + UI->Y_TimelinePercentOffset -= (UI->Y_TimelinePercentOffset * Increment) + Offset*Increment; + UI->TimelinePercentZoomed -= (UI->TimelinePercentZoomed * Increment); + UI->TimelinePercentOffset -= (UI->TimelinePercentOffset * Increment) + X_Offset*Increment; + } else { + UI->Y_TimelinePercentOffset = ((UI->Y_TimelinePercentOffset + Offset*Increment) / (1.0f - Increment)); + UI->Y_TimelinePercentZoomed = (UI->Y_TimelinePercentZoomed / (1.0f - Increment)); + UI->TimelinePercentOffset = ((UI->TimelinePercentOffset + X_Offset*Increment) / (1.0f - Increment)); + UI->TimelinePercentZoomed = (UI->TimelinePercentZoomed / (1.0f - Increment)); + } + } + } + } + + // 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) { + 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) + { + 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; + } + } + } + if (IsItemDeactivated) { + UI->BoxStart = {0, 0}; + UI->BoxEnd = {0, 0}; + UI->BoxSelectActive = false; + if (!io.KeyShift) DeselectAllLayers(File, State); + } + + ImGui::EndChild(); + + ImGui::PopStyleVar(2); + +#endif + + + #if 0 // 0 for timeline keyframe, 1 for graph keyframe, 2 for left graph handle, 3 for right graph handle static void @@ -99,59 +1150,6 @@ ImGui_KeyframeDragging(project_data *File, project_state *State, ui *UI, propert } } -// NOTE(fox): We have to do a bit of hackery here to tell how many times the -// mouse has been wrapped during a drag, since it doesn't seem like we can rely -// on SDL_WarpMouseGlobal to update on the first frame of a WantSetPos request. - -static void -ImGui_WrapMouse(ui *UI, ImVec2 MousePos, ImVec2 Min, ImVec2 Max, int Direction = 3) -{ - if (Direction & 1) { - if (MousePos.x < Min.x) { - UI->WantSetPos = true; - UI->SetPos = ImVec2(Max.x - 5, MousePos.y); - UI->InitPos = MousePos.x; - UI->WrapDirection = 0; - } - if (MousePos.x > Max.x) { - UI->WantSetPos = true; - UI->SetPos = ImVec2(Min.x + 5, MousePos.y); - UI->InitPos = MousePos.x; - UI->WrapDirection = 1; - } - } - if (Direction & 2) { - if (MousePos.y < Min.y) { - UI->WantSetPos = true; - UI->SetPos = ImVec2(MousePos.x, Max.y - 5); - UI->InitPos = MousePos.y; - UI->WrapDirection = 2; - } - if (MousePos.y > Max.y) { - UI->WantSetPos = true; - UI->SetPos = ImVec2(MousePos.x, Min.y + 5); - UI->InitPos = MousePos.y; - UI->WrapDirection = 3; - } - } -} - -static void -ImGui_WrapMouseFinish(ui *UI, ImVec2 MousePos) -{ - if (UI->WrapDirection == 0) { - if (MousePos.x < UI->InitPos) UI->Wrap_X--; - } else if (UI->WrapDirection == 1) { - if (MousePos.x > UI->InitPos) UI->Wrap_X++; - } else if (UI->WrapDirection == 2) { - if (MousePos.y < UI->InitPos) UI->Wrap_Y--; - } else if (UI->WrapDirection == 3) { - if (MousePos.y > UI->InitPos) UI->Wrap_Y++; - } else { - Assert(0); - } -} - static void ImGui_InteractSliderProperty(project_state *State, memory *Memory, property_channel *Property) { @@ -176,17 +1174,6 @@ ImGui_InteractSliderProperty(project_state *State, memory *Memory, property_chan } } -// Returns a normalized UV position of the composition -static v2 -ImGui_ScreenPointToCompUV(ImVec2 ViewportMin, ImVec2 CompPos, ImVec2 CompZoom, ImVec2 MousePos) -{ - ImVec2 LocalMousePos = MousePos - ViewportMin; - ImVec2 LocalCompPos = CompPos - ViewportMin; - ImVec2 MouseScreenUV = LocalMousePos - LocalCompPos; - ImVec2 Result = MouseScreenUV / CompZoom; - return V2(Result); -} - static void ImGui_DebugUndoTree(project_data *File, memory *Memory) { @@ -299,46 +1286,6 @@ ImGui_DebugMemoryViewer(project_data *File, memory *Memory) static bool32 FU; static void -ImGui_TimelineIncrementDraw(project_data *File, ui *UI, ImDrawList *draw_list, - ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, bool32 IsText) -{ - uint32 LineColor = IM_COL32(200, 200, 200, 40); - - real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; - real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; - - Assert(TimelineZoomSize > 0.0f); - - real32 x = 0; - bool32 RightmostEdge = false; - real32 Increment = (real32)1 / File->NumberOfFrames; - if (UI->TimelinePercentZoomed > 0.90) - Increment = (real32)File->FPS / File->NumberOfFrames; - else if (UI->TimelinePercentZoomed > 0.40) - Increment *= 2; - - while (!RightmostEdge) { - ImVec2 Min = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + x*TimelineZoomSize, TimelineAbsolutePos.y); - ImVec2 Max = ImVec2(Min.x + 2, TimelineAbsolutePos.y + TimelineSizeWithBorder.y); - if (Min.x < TimelineAbsolutePos.x + TimelineSizeWithBorder.x) { - draw_list->AddLine(Min, Max, LineColor); - char buf2[6]; - uint32 FrameNumber = (uint32)(x*File->NumberOfFrames) % File->FPS; - if (FrameNumber != 0) - sprintf(buf2, ":%.2i", FrameNumber); - else - sprintf(buf2, "%.2i:00", (uint32)(x*File->NumberOfFrames) / File->FPS); - draw_list->AddText(ImVec2(Min.x, TimelineAbsolutePos.y), IM_COL32(200, 200, 200, 130), buf2); - x += Increment; - if (x > 1.0f) - RightmostEdge = true; - } else { - RightmostEdge = true; - } - } -} - -static void ImGui_TimelineIncrementDraw2(project_data *File, ImDrawList *draw_list, real32 MaxVal_Y, real32 MinVal_Y, real32 Y_TimelinePercentZoomed, real32 Y_TimelinePercentOffset, ImVec2 TimelineSizeWithBorder, ImVec2 TimelineAbsolutePos, bool32 IsText) @@ -1129,612 +2076,6 @@ ImGui_EffectsPanel(project_data *File, project_state *State, memory *Memory, ui } -static 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 || WindowSize.y < 50) { - ImGui::PopStyleVar(2); - ImGui::End(); - return; - } - - ImVec2 WindowMinAbs = ImGui::GetWindowPos(); - ImVec2 WindowMaxAbs = WindowMinAbs + WindowSize; - - ImVec2 ButtonSize = ImVec2(FontHeight*2, FontHeight*2); - - real32 TopbarHeight = FontHeight*2; - 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); - - real32 MaxZoom = TimelineSizeWithBorder.x / (File->NumberOfFrames + 1); - - if (UI->Initializing) { - UI->TimelineZoom = MaxZoom; - } - - 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); - char buf2[6]; - sprintf(buf2, "%.2i:%.2i", File->CurrentFrame / File->FPS, File->CurrentFrame % File->FPS); - ImGui::Text(buf2); - /* - if (UI->IsDragging) { - ImGui::SameLine(); - sprintf(buf2, "X: %.3f, Y: %.3f", - ImGui::Text(buf2); - } - */ - /* - 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 + Increment <= 0) - File->CurrentFrame = 0; - else if (File->CurrentFrame + 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); - - 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::IsItemHovered()) { - ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeEW); - } - 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); - - project_layer *Layer = File->Layer[0]; - property_channel *Property = &Layer->x; - - - real32 MaxVal_Y = -10000; - real32 MinVal_Y = 10000; - for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { - keyframe *Keyframe = KeyframeLookup(Property, b); - MaxVal_Y = (Keyframe->Value.f > MaxVal_Y) ? Keyframe->Value.f : MaxVal_Y; - MinVal_Y = (Keyframe->Value.f < MinVal_Y) ? Keyframe->Value.f : MinVal_Y; - } - - keyframe *FirstKeyframe = KeyframeLookup(Property, 0); - keyframe *LastKeyframe = KeyframeLookup(Property, Property->NumberOfTotalKeyframes - 1); - real32 MinVal_X = (Layer->BitmapInfo.FrameOffset + FirstKeyframe->FrameNumber); - real32 MaxVal_X = (Layer->BitmapInfo.FrameOffset + LastKeyframe->FrameNumber); - - UI->Y_MaxVal = MaxVal_Y; - UI->Y_MinVal = MinVal_Y; - - if (!UI->IsDragging) { - UI->Display_Y_MinVal = UI->Y_MinVal; - UI->Display_Y_MaxVal = UI->Y_MaxVal; - } - - real32 Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed; - real32 Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset; - MaxVal_Y = UI->Display_Y_MaxVal; - MinVal_Y = UI->Display_Y_MinVal; - - DebugWatchVar("offset: ", &Y_TimelinePercentOffset, d_float); - DebugWatchVar("zoom: ", &Y_TimelinePercentZoomed, d_float); - - real32 Ratio_Graph_X = (MaxVal_X - MinVal_X) / File->NumberOfFrames; - real32 TimelineZoomSize = TimelineSizeWithBorder.x / UI->TimelinePercentZoomed; - real32 TimelineMoveSize = TimelineSizeWithBorder.x * UI->TimelinePercentOffset / UI->TimelinePercentZoomed; - real32 Y_TimelineZoomSize = TimelineSizeWithBorder.y / Y_TimelinePercentZoomed; - real32 Y_TimelineMoveSize = TimelineSizeWithBorder.y * Y_TimelinePercentOffset / Y_TimelinePercentZoomed; - - for (int b = 0; b < Property->NumberOfTotalKeyframes; b++) { - ImGui::PushID(b); - - keyframe *Keyframe = KeyframeLookup(Property, b); - // Only used for drawing the bezier. - keyframe *NextKeyframe = (b != Property->NumberOfTotalKeyframes - 1) ? KeyframeLookup(Property, b + 1) : NULL; - - real32 Increment_X = (real32)1 / File->NumberOfFrames; - real32 UI_FrameDistance = Increment_X*TimelineZoomSize; - - int32 Keyframe_X = (Layer->BitmapInfo.FrameOffset + Keyframe->FrameNumber); - real32 Keyframe_Y = Keyframe->Value.f; - - real32 Ratio_X_Mid = (real32)Keyframe_X / File->NumberOfFrames; - real32 Ratio_Y_Mid = (Keyframe_Y - MinVal_Y) / (MaxVal_Y - MinVal_Y); - - ImVec2 KeyframePos_Mid = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_Mid*TimelineZoomSize, - TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_Mid)*Y_TimelineZoomSize); - - ImGui::SetCursorScreenPos(KeyframePos_Mid); - ImGui::Button("##keyframe", ImVec2(FontHeight, FontHeight)); - - if (ImGui::IsItemHovered() && ImGui::IsKeyPressed(ImGuiKey_R)) { - UI->TempVal = Keyframe->Value.f; - UI->TempVal_X = Keyframe->FrameNumber; - } - - if (ImGui::IsItemActivated()) { - UI->IsDragging = true; - UI->TempVal = Keyframe->Value.f; - UI->TempVal_X = Keyframe->FrameNumber; - } - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - ImVec2 DragDelta = ImGui::GetMouseDragDelta(); - DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); - DebugWatchVar("DragX", &DragDelta.x, d_float); - DebugWatchVar("DragY", &DragDelta.y, d_float); - DebugWatchVar("Wrap_X", &UI->Wrap_X, d_int); - DebugWatchVar("Wrap_Y", &UI->Wrap_Y, d_int); - real32 MouseDeltaRatio = -DragDelta.y / TimelineSizeWithBorder.y * Y_TimelinePercentZoomed; - ImVec2 Increment = ImVec2(DragDelta.x / UI_FrameDistance, ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio)); - - // The plus 0.5 * X_Direction is for making the frame jump happen - // when the cursor is between two frames rather than when passing one. - real32 X_Direction = (Increment.x > 0) ? fabsf(Increment.x) / Increment.x : 0; - - Keyframe->FrameNumber = UI->TempVal_X + (int32)(Increment.x + 0.5*X_Direction); - Keyframe->Value.f = UI->TempVal + Increment.y; - - if (io.KeyShift) { - bool32 RestrainAxis = (fabsf(DragDelta.x) > fabsf(DragDelta.y)); - if (RestrainAxis) { - Keyframe->Value.f = UI->TempVal; - } else { - Keyframe->FrameNumber = UI->TempVal_X; - } - } - ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); - } - - // TODO(fox): This is kind of a mess. I built the graph around the - // ratios of the keyframes/timeline to make the bars straightforward, - // meaning the position/offset have to be transformed into a new space - // when a new min/max value is reached. - - if (ImGui::IsItemDeactivated()) { - if ((UI->TempVal >= MaxVal_Y) || Keyframe->Value.f == UI->Y_MaxVal) { - real32 Min = ((Ratio_Y_Mid <= 0.0f) && (UI->TempVal >= MaxVal_Y)) ? MinVal_Y : UI->Y_MinVal; - real32 RealRatio_Y = (UI->Y_MaxVal - UI->Y_MinVal) / (MaxVal_Y - MinVal_Y); - UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / RealRatio_Y; - UI->Y_TimelinePercentOffset = (1.0f/RealRatio_Y + UI->Y_TimelinePercentOffset/RealRatio_Y - 1.0f); - } else if (UI->TempVal <= MinVal_Y || Keyframe->Value.f == UI->Y_MinVal) { - real32 RealRatio_Y = (UI->Y_MinVal - MinVal_Y) / (MaxVal_Y - MinVal_Y); - UI->Y_TimelinePercentOffset = UI->Y_TimelinePercentOffset / (1 - RealRatio_Y); - UI->Y_TimelinePercentZoomed = UI->Y_TimelinePercentZoomed / (1 - RealRatio_Y); - } - UI->IsDragging = false; - UI->Wrap_X = 0; - UI->Wrap_Y = 0; - } - - ImU32 col = ImGui::GetColorU32(ImGuiCol_ScrollbarGrab); - - ImVec2 Handle_Pos[2] = {}; - - if (Keyframe->Type == bezier) { - ImVec2 Handle_Ratio[2] = {}; - - Handle_Ratio[0] = ImVec2((real32)(Keyframe_X + Keyframe->TangentLeft.x) / File->NumberOfFrames, - (Keyframe_Y + Keyframe->TangentLeft.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); - Handle_Ratio[1] = ImVec2((real32)(Keyframe_X + Keyframe->TangentRight.x) / File->NumberOfFrames, - (Keyframe_Y + Keyframe->TangentRight.y - MinVal_Y) / (MaxVal_Y - MinVal_Y)); - - Handle_Pos[0] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[0].x*TimelineZoomSize, - TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[0].y)*Y_TimelineZoomSize); - Handle_Pos[1] = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Handle_Ratio[1].x*TimelineZoomSize, - TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Handle_Ratio[1].y)*Y_TimelineZoomSize); - - draw_list->AddLine(KeyframePos_Mid, Handle_Pos[0], col, 1.0f); - draw_list->AddLine(KeyframePos_Mid, Handle_Pos[1], col, 1.0f); - - for (int i = 0; i < 2; i++) { - ImGui::SetCursorScreenPos(Handle_Pos[i]); - ImGui::Button((i == 0) ? "##keyframe_left" : "##keyframe_right", ImVec2(FontHeight, FontHeight)); - v2 *Tangent = (i == 0) ? &Keyframe->TangentLeft : &Keyframe->TangentRight; - - if (ImGui::IsItemActivated()) { - UI->IsDragging = true; - UI->TempVal_X = Tangent->x; - UI->TempVal = Tangent->y; - } - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - ImVec2 DragDelta = ImGui::GetMouseDragDelta(); - DragDelta = DragDelta + (ImVec2(UI->Wrap_X, UI->Wrap_Y) * TimelineSize); - ImVec2 MouseDeltaRatio = (ImVec2(1, -1) * DragDelta) / TimelineSizeWithBorder * ImVec2(UI->TimelinePercentZoomed / Ratio_Graph_X, Y_TimelinePercentZoomed); - real32 NewPos_X = ((MaxVal_X - MinVal_X) * MouseDeltaRatio.x); - real32 NewPos_Y = (io.KeyShift) ? 0 : ((MaxVal_Y - MinVal_Y) * MouseDeltaRatio.y); - *Tangent = V2(UI->TempVal_X, UI->TempVal) + V2(NewPos_X, NewPos_Y); - ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder); - } - - if (ImGui::IsItemDeactivated()) { - UI->IsDragging = false; - UI->Wrap_X = 0; - UI->Wrap_Y = 0; - } - } - } - - if (NextKeyframe) { - real32 Ratio_X_2 = (real32)(Layer->BitmapInfo.FrameOffset + NextKeyframe->FrameNumber) / File->NumberOfFrames; - real32 Ratio_Y_2 = (NextKeyframe->Value.f - MinVal_Y) / (MaxVal_Y - MinVal_Y); - - ImVec2 NextKeyframePos = ImVec2(TimelineAbsolutePos.x + TimelineMoveSize + Ratio_X_2*TimelineZoomSize, - TimelineAbsolutePos.y + Y_TimelineMoveSize + (1.0f - Ratio_Y_2)*Y_TimelineZoomSize); - draw_list->AddLine(KeyframePos_Mid, NextKeyframePos, col, 1.0f); - } - - ImGui::PopID(); - } - - ImGui_TimelineIncrementDraw(File, UI, draw_list, TimelineSizeWithBorder, TimelineAbsolutePos, 0); - ImGui_TimelineIncrementDraw2(File, draw_list, MaxVal_Y, MinVal_Y, Y_TimelinePercentZoomed, Y_TimelinePercentOffset, TimelineSizeWithBorder, TimelineAbsolutePos, 0); - -#if DEBUG - draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.25, TimelineSizeWithBorder.y - 50), - 2, IM_COL32(200, 000, 200, 200), 16, 1); - draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.5, TimelineSizeWithBorder.y - 50), - 2, IM_COL32(200, 000, 200, 200), 16, 1); - draw_list->AddCircle(TimelineAbsolutePos + ImVec2(TimelineSizeWithBorder.x * 0.75, TimelineSizeWithBorder.y - 50), - 2, IM_COL32(200, 000, 200, 200), 16, 1); -#endif - - ImVec2 MouseDelta = io.MouseDelta / TimelineSize; - - real32 BarHandleSize = FontHeight; - real32 BarThickness = 50; - real32 BarMinZoom = 0.01; - - real32 BarH_Pos = -TimelineSizeWithBorder.x * UI->TimelinePercentOffset; - real32 BarH_Size = TimelineSizeWithBorder.x / (1 / UI->TimelinePercentZoomed); - - // I use "UI" to denote the size/position after clipping the bar so that it - // doesn't go out of bounds and the handles are always selectable at the edges. - - real32 BarH_Offset = Max(BarH_Pos, 0); - - real32 BarH_SizeUI = (BarH_Size + BarH_Pos > TimelineSizeWithBorder.x) ? - TimelineSizeWithBorder.x - BarH_Pos : - BarH_Size + (BarH_Pos - BarH_Offset); - - if (BarH_Offset == 0 && BarH_SizeUI > TimelineSizeWithBorder.x) - BarH_SizeUI = TimelineSizeWithBorder.x; - - BarH_SizeUI = BarH_SizeUI - BarHandleSize*2; - - BarH_SizeUI = Max(BarH_SizeUI, FontHeight*4); - - BarH_Offset = Min(BarH_Offset, TimelineSize.x - BarH_SizeUI - BarHandleSize*4); - ImVec2 BarH_PosUI = TimelineAbsolutePos + ImVec2(BarH_Offset, TimelineSize.y - BarThickness); - bool32 BarHeld = false; - - ImGui::SetCursorScreenPos(BarH_PosUI); - ImGui::Button("##scrollbarleft", ImVec2(BarHandleSize, BarThickness)); - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - if ((UI->TimelinePercentZoomed - MouseDelta.x) > BarMinZoom) { - UI->TimelinePercentZoomed -= MouseDelta.x; - UI->TimelinePercentOffset -= MouseDelta.x; - } - BarHeld = true; - } - - ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0)); - ImGui::Button("##scrollbarhori", ImVec2(BarH_SizeUI, BarThickness)); - - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) - { - UI->TimelinePercentOffset -= MouseDelta.x; - BarHeld = true; - } - - ImGui::SetCursorScreenPos(BarH_PosUI + ImVec2(BarHandleSize, 0) + ImVec2(BarH_SizeUI, 0)); - ImGui::Button("##scrollbarright", ImVec2(BarHandleSize, BarThickness)); - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - if ((UI->TimelinePercentZoomed + MouseDelta.x) > BarMinZoom) { - UI->TimelinePercentZoomed += MouseDelta.x; - } - BarHeld = true; - } - - if (BarHeld) { - ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 1); - } - - Assert(UI->TimelinePercentZoomed > BarMinZoom); - - real32 BarV_MaxSize = TimelineSizeWithBorder.y - BarThickness/2; - real32 BarV_Pos = -BarV_MaxSize * Y_TimelinePercentOffset; - real32 BarV_Size = BarV_MaxSize / (1 / Y_TimelinePercentZoomed); - BarV_Size = Max(BarV_Size, FontHeight*4); - - real32 BarV_Offset = Max(BarV_Pos, 0); - - real32 BarV_SizeUI = (BarV_Size + BarV_Pos > BarV_MaxSize) ? - BarV_MaxSize - BarV_Pos : - BarV_Size + (BarV_Pos - BarV_Offset); - - if (BarV_Offset == 0 && BarV_SizeUI > BarV_MaxSize) - BarV_SizeUI = BarV_MaxSize; - - BarV_SizeUI = BarV_SizeUI - BarHandleSize*2; - - BarV_SizeUI = Max(BarV_SizeUI, FontHeight*4); - - BarV_Offset = Min(BarV_Offset, BarV_MaxSize - BarV_SizeUI - BarHandleSize*4); - ImVec2 BarV_PosUI = TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, BarV_Offset); - BarHeld = false; - - ImGui::SetCursorScreenPos(BarV_PosUI); - ImGui::Button("##h-scrollbarleft", ImVec2(BarThickness, BarHandleSize)); - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - UI->Y_TimelinePercentZoomed -= MouseDelta.y; - UI->Y_TimelinePercentOffset -= MouseDelta.y; - BarHeld = true; - } - - ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize)); - ImGui::Button("##h-scrollbar", ImVec2(BarThickness, BarV_SizeUI)); - - if (ImGui::IsItemHovered() && io.MouseWheel) - { - UI->Y_TimelinePercentOffset -= io.MouseWheel/10; - } - - if (ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1)) - { - UI->Y_TimelinePercentOffset -= MouseDelta.y; - BarHeld = true; - } - - ImGui::SetCursorScreenPos(BarV_PosUI + ImVec2(0, BarHandleSize) + ImVec2(0, BarV_SizeUI)); - ImGui::Button("##h-scrollbarright", ImVec2(BarThickness, BarHandleSize)); - - if ((ImGui::IsItemActive() && ImGui::IsMouseDragging(ImGuiMouseButton_Left, -1))) - { - UI->Y_TimelinePercentZoomed += MouseDelta.y; - BarHeld = true; - } - - UI->Y_TimelinePercentZoomed = Max(UI->Y_TimelinePercentZoomed, 0.01); - - if (BarHeld) { - ImGui_WrapMouse(UI, io.MousePos, TimelineAbsolutePos, TimelineAbsolutePos + TimelineSizeWithBorder, 2); - } - - draw_list->PopClipRect(); - ImGui::PopClipRect(); - - ImGui::PopStyleVar(); - - if (io.MouseWheel) { - // NOTE(fox): Change this if any other action is added when hovering over the bar area. - bool32 BarHovering_H = TestRectangle(TimelineAbsolutePos + ImVec2(0, TimelineSize.y - BarThickness), - TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), - io.MousePos); - bool32 BarHovering_V = TestRectangle(TimelineAbsolutePos + ImVec2(TimelineSize.x - BarThickness, 0), - TimelineAbsolutePos + ImVec2(TimelineSize.x, TimelineSize.y), - io.MousePos); - if (BarHovering_H && io.MouseWheel) { - UI->TimelinePercentOffset -= io.MouseWheel/15; - } else if (BarHovering_V && io.MouseWheel) { - UI->Y_TimelinePercentOffset -= io.MouseWheel/15; - } else { - real32 Increment = 0.1; - bool32 Direction = (io.MouseWheel > 0) ? 1 : -1; - real32 Offset = (io.MousePos.y - (TimelineAbsolutePos.y + Y_TimelineMoveSize)) / Y_TimelineZoomSize; - real32 X_Offset = (io.MousePos.x - (TimelineAbsolutePos.x + TimelineMoveSize)) / TimelineZoomSize; - DebugWatchVar("X Offset", &X_Offset, d_float); - if (io.KeyShift) { - UI->Y_TimelinePercentOffset += Increment*Direction; - } else if (io.KeyCtrl) { - UI->TimelinePercentOffset += Increment*Direction*0.3; - } else { - if (Direction == 1) { - UI->Y_TimelinePercentZoomed -= (UI->Y_TimelinePercentZoomed * Increment); - UI->Y_TimelinePercentOffset -= (UI->Y_TimelinePercentOffset * Increment) + Offset*Increment; - UI->TimelinePercentZoomed -= (UI->TimelinePercentZoomed * Increment); - UI->TimelinePercentOffset -= (UI->TimelinePercentOffset * Increment) + X_Offset*Increment; - } else { - UI->Y_TimelinePercentOffset = ((UI->Y_TimelinePercentOffset + Offset*Increment) / (1.0f - Increment)); - UI->Y_TimelinePercentZoomed = (UI->Y_TimelinePercentZoomed / (1.0f - Increment)); - UI->TimelinePercentOffset = ((UI->TimelinePercentOffset + X_Offset*Increment) / (1.0f - Increment)); - UI->TimelinePercentZoomed = (UI->TimelinePercentZoomed / (1.0f - Increment)); - } - } - } - } - - // 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) { - 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) - { - 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; - } - } - } - if (IsItemDeactivated) { - UI->BoxStart = {0, 0}; - UI->BoxEnd = {0, 0}; - UI->BoxSelectActive = false; - if (!io.KeyShift) DeselectAllLayers(File, State); - } - - ImGui::EndChild(); - - ImGui::PopStyleVar(2); - - if (IsRectTouching(WindowMinAbs, WindowMaxAbs, io.MousePos, io.MousePos + 1)) { - real32 Multiplier = 16; - if (io.KeyCtrl && io.MouseWheel) { - real32 ZoomAmount = io.MouseWheel*Multiplier; - real32 LocalMousePos = ImGui::GetMousePos().x - TimelineStartingPos.x; - real32 ZoomRatio = LocalMousePos / UI->TimelineZoom; - if (UI->TimelineZoom + ZoomAmount > 0) { - UI->TimelineZoom += ZoomAmount; - UI->ScrollXOffset -= ZoomAmount*ZoomRatio; - } - } else if (io.KeyShift && io.MouseWheel) { - UI->ScrollXOffset += io.MouseWheel*Multiplier; - } else { - UI->ScrollXOffset += io.MouseWheelH*Multiplier; - UI->ScrollYOffset += io.MouseWheel*Multiplier; - } - } - - - - ImGui::End(); -} - static void ImGui_ProcessInputs(project_data *File, project_state *State, comp_buffer *CompBuffer, memory *Memory, ui *UI, ImGuiIO io) |